Python数据分析基础之Excel文件(5)

  上一篇博客主要讲了如何在一个工作簿的所有工作表中筛选特定的行和列。但是,有些情况下,我们只需要处理工作簿中其中几个工作表。在这种情况下,我们可以使用sheet_by_index()sheet_by_name()函数来处理这些待处理的工作表。

在一组工作表中筛选特定行

1.基础Python

  我们想要筛选出sales_2013.xlsx这个工作簿的第一个和第二个工作表中销售额大于$1900.00的那些行。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('set_of_worksheets')
my_sheets = [0, 1]
threshold = 1900.0
sales_column_index = 3
first_worksheet = True
with open_workbook(input_file) as workbook:
data = []
for sheet_index in range(workbook.nsheets):
if sheet_index in my_sheets:
worksheet = workbook.sheet_by_index(sheet_index)
if first_worksheet:
header_row = worksheet.row_values(0)
data.append(header_row)
first_worksheet = False
for row_index in range(1, worksheet.nrows):
row_list = []
sale_amount = worksheet.cell_value(row_index, sales_column_index)
sale_amount = sale_amount.replace(r'$', '')
sale_amount = sale_amount.replace(r',', '')
sale_amount = float(sale_amount)
if sale_amount > threshold:
for column_index in range(worksheet.ncols):
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
if row_list:
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  第13行创建的列表变量my_sheets包含了两个整数,分别对应要处理的工作表(即第一个和第二个工作表)的索引值。第19行代码创建了工作簿中所有工作表的索引值,第20行代码在for循环中应用if判断语句检验要处理的工作表索引值是否在列表my_sheets中,这样可以确保代码只处理了我们想处理的工作表。

作者注:代码的第29-31行是我自己加上的,参考书(《Python数据分析基础》,作者Clinton W. Brownley,译者陈光欣,人民邮电出版社)上的代码并没有这三行,运行的时候会报错:
Traceback (most recent call last):
File “E:\python_pycharm\Python数据分析基础\第3章 Excel文件\11excel_value_meets_condition_set_of_worksheets.py”, line 29, in
if sale_amount > threshold:
TypeError: ‘>’ not supported between instances of ‘str’ and ‘float’
改正后可以正常运行。这应该是书本的错误。

  在命令行窗口中运行这个脚本,得到输出文件。

在这里插入图片描述

2.pandas

  使用pandas模块在工作簿中选择几个工作表非常容易,我们只需要在read_excel()函数中将工作表的索引值或名称设置成一个列表就可以了。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

my_sheets = [0, 1]
threshold = 1900.0
data_frame = pd.read_excel(input_file, sheet_name=my_sheets, index_col=None)
row_list = []
for worksheet_name, data in data_frame.items():
data['Sale Amount'] = data['Sale Amount'].str.replace(r'\$', '')
data['Sale Amount'] = data['Sale Amount'].str.replace(',', '')
row_list.append(data[data['Sale Amount'].astype(float) > threshold])
filtered_rows = pd.concat(row_list, axis=0, ignore_index=True)
writer = pd.ExcelWriter(output_file)
filtered_rows.to_excel(writer, sheet_name='set_of_worksheets', index=False)
writer.save()

  在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

Python数据分析基础之Excel文件(4)

  之前的3篇博客主要讲了如何处理单个工作表。但是,很多情况下我们需要处理多个工作表,如果手工处理的话,效率会非常低,甚至根本不可行。在这种情况下,Python可以让我们自动化和规模化地进行数据处理,远远超过手工处理能够达到的限度。

在所有工作表中筛选特定行

1.基础Python

  对于《Python数据分析基础之Excel文件(1)》中建立的sales_2013.xlsx这个Excel工作簿,我们想在所有工作表中筛选出销售额大于$2,000.00的所有行。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('filtered_rows_all_worksheets')
sales_column_index = 3
threshold = 2000.0
first_worksheet = True
with open_workbook(input_file) as workbook:
data = []
for worksheet in workbook.sheets():
if first_worksheet:
header_row = worksheet.row_values(0)
data.append(header_row)
first_worksheet = False
for row_index in range(1, worksheet.nrows):
row_list = []
sale_amount = worksheet.cell_value(row_index, sales_column_index)
sale_amount = sale_amount.replace(r'$', '')
sale_amount = sale_amount.replace(r',', '')
sale_amount = float(sale_amount)
if sale_amount > threshold:
for column_index in range(worksheet.ncols):
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
if row_list:
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  在上面的代码中,变量sales_column_index保存Sale Amount列的索引值,变量threshold保存我们关心的销售额。我们要做的是将Sale Amount列中的每个值与这个阈值进行比较,以此来确定哪一行要被写入输出文件中。变量first_worksheet则是一个旗帜,其作用是用来判断当前工作表是否为第一个工作表。初始化旗帜时,我们将其设为True
  第19行代码通过旗帜来判断当前工作表是否为第一个工作表,如果是第一个工作表,我们就将标题行提取出来,追加到列表data中,然后令first_worksheet = False,继续处理余下的行。
  在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  在pandas模块中,通过在read_excel()函数中设置sheet_name=None,可以一次性读取工作簿中的所有工作表。pandas将这些工作表读入一个数据框字典,字典中的键就是工作表的名称,值就是包含工作表中数据的数据框。通过在字典的键和值之间迭代,就可以使用工作簿中所有的数据。
  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, sheet_name=None, index_col=None)
row_output = []
for worksheet_name, data in data_frame.items():
data['Sale Amount'] = data['Sale Amount'].str.replace(r'\$', '')
data['Sale Amount'] = data['Sale Amount'].str.replace(r',', '')
data['Sale Amount'] = data['Sale Amount'].astype(float)
row_output.append(data[data['Sale Amount'].astype(float) > 2000.0])
filtered_rows = pd.concat(row_output, axis=0, ignore_index=True)
writer = pd.ExcelWriter(output_file)
filtered_rows.to_excel(writer, sheet_name='sale_amount_gt2000', index=False)
writer.save()

  在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

在所有工作表中选取特定列

1.基础Python

  对于《Python数据分析基础之Excel文件(1)》中建立的sales_2013.xlsx这个Excel工作簿,我们想在所有工作表中选取Customer Name和Sale Amount列。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('selected_columns_all_worksheets')
my_columns = ['Customer Name', 'Sale Amount']
first_worksheet = True
with open_workbook(input_file) as workbook:
data = [my_columns]
index_of_cols_to_keep = []
for worksheet in workbook.sheets():
if first_worksheet:
header = worksheet.row_values(0)
for column_index in range(len(header)):
if header[column_index] in my_columns:
index_of_cols_to_keep.append(column_index)
first_worksheet = False
for row_index in range(1, worksheet.nrows):
row_list = []
for column_index in index_of_cols_to_keep:
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  列表变量my_columns包含了我们要保留的两列的名称。将my_columns放入data,作为data中的第一个列表,因为它是要写入输出文件的列的标题。列表变量index_of_cols_to_keep用来保存Customer Name和Sale Amount列的索引值。
  在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  使用pandas模块的思路和上面筛选特定列的思路类似,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, sheet_name=None, index_col=None)
column_output = []
for worksheet_name, data in data_frame.items():
column_output.append(data.loc[:, ['Customer Name', 'Sale Amount']])
selected_columns = pd.concat(column_output, axis=0, ignore_index=True)
writer = pd.ExcelWriter(output_file)
selected_columns.to_excel(writer, sheet_name='selected_columns_all_worksheets', index=False)
writer.save()

  在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

Python数据分析基础之Excel文件(3)

选取特定列

  有些时候,我们并不需要工作表中所有的列。我们可以用Python选取出需要保留的列。
  有两种方法可以在Excel文件中选取特定的列:
  1.使用列索引值;
  2.使用列标题。

1.1 使用列索引值(基础Python)

  这里我们以保留之前的january_2013工作表中Customer Name和Purchase Date这两列为例。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')
my_columns = [1, 4]
with open_workbook(input_file) as workbook:
worksheet = workbook.sheet_by_name('january_2013')
data = []
for row_index in range(worksheet.nrows):
row_list = []
for column_index in my_columns:
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  上面的代码中,列表变量my_columns包含整数1和4,分别代表了Customer Name和Purchase Date这两列的索引值。其他代码与之前练习过的代码类似,在这里不再赘述。
  我们在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

1.2 使用列索引值(pandas)

  使用pandas模块根据列索引值选取特定列,我们需要设置数据框以及用到iloc函数。这里需要提到一点,iloc函数可以使我们同时选择特定的行和特定的列,所以如果我们想使用它选取特定的列,那么就需要在列索引值前面加上一个冒号和一个逗号,表示为这些特定的列保留所有的行,否则函数就会使用这些索引值去筛选行。
  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, 'january_2013', index_col=None)
data_frame_column_by_index = data_frame.iloc[:, [1, 4]]
writer = pd.ExcelWriter(output_file)
data_frame_column_by_index.to_excel(writer, sheet_name='jan_13_output', index=False)
writer.save()

  在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

2.1 使用列标题(基础Python)

  这种方法适用于想保留的列的标题非常容易识别,或者在处理多个输入文件的过程中,各个输入文件中列的位置会发生改变但标题不变的情况。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')
my_columns = ['Customer ID', 'Purchase Date']
with open_workbook(input_file) as workbook:
worksheet = workbook.sheet_by_name('january_2013')
data = [my_columns]
header_list = worksheet.row_values(0)
header_index_list = []
for header_index in range(len(header_list)):
if header_list[header_index] in my_columns:
header_index_list.append(header_index)
for row_index in range(1, worksheet.nrows):
row_list = []
for column_index in header_index_list:
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  上面的代码中,列表变量my_columns包含了要保留的两列的名称。因为这是要写入文件的列表题,所以直接将其加入到输出列表data中。其他代码与之前练习过的代码类似,在这里不再赘述。
  我们在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

2.2 使用列标题(pandas)

  使用pandas模块根据列标题选取特定列,我们需要使用loc函数。和前面提到的iloc函数类似,如果我们想使用它选取特定的列,那么就需要在列索引值前面加上一个冒号和一个逗号,表示为这些特定的列保留所有的行,否则函数就会使用这些索引值去筛选行。
  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, 'january_2013', index_col=None)
data_frame_column_by_name = data_frame.loc[:, ['Customer ID', 'Purchase Date']]
writer = pd.ExcelWriter(output_file)
data_frame_column_by_name.to_excel(writer, sheet_name='jan_13_output', index=False)
writer.save()

  在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

Python数据分析基础之Excel文件(2)

处理单个工作表——读写Excel文件

1.基础Python

  要使用基础Python读写Excel文件,我们需要导入xlrd和xlwt模块。
  对于上一节创建的Excel工作簿,我们试着来读取其中的january_2013工作表中的数据。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3

import sys
from xlrd import open_workbook
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')
with open_workbook(input_file) as workbook:
worksheet = workbook.sheet_by_name('january_2013')
for row_index in range(worksheet.nrows):
for column_index in range(worksheet.ncols):
output_worksheet.write(row_index, column_index, worksheet.cell_value(row_index, column_index))
output_workbook.save(output_file)

  我们来解释一下上面的代码。

1
2
from xlrd import open_workbook
from xlwt import Workbook

  这两行代码导入了xlrd模块的open_workbook()函数以及xlwt模块的Workbook对象。

1
2
output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')

  这里实例化Workbook对象,以使我们可以将结果写入用于输出的Excel文件。接下来一行,add_sheet()函数为输出工作簿添加了一个名为jan_2013_output的工作表。

1
worksheet = workbook.sheet_by_name('january_2013')

  这行代码使用workbook对象的sheet_by_name()函数引用名称为january_2013的工作表。
  我们在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, sheet_name='january_2013')
writer = pd.ExcelWriter(output_file)
data_frame.to_excel(writer, sheet_name='jan_13_output', index=False)
writer.save()

  在命令行窗口中运行脚本后得到的输出文件如下:
在这里插入图片描述

处理单个工作表——筛选特定行

  和之前学习CSV文件一样,有的时候我们不需要Excel文件中的所有行,在这种情况下,我们可以使用Python来对工作表中的行进行筛选。

1.1 行中的值满足某个条件(基础Python)

  比如说,我们想对之前的工作表中的行进行筛选,保留Sale Amount大于$1,400.00的行。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')
sale_amount_column_index = 3
with open_workbook(input_file) as workbook:
worksheet = workbook.sheet_by_name('january_2013')
data = []
header = worksheet.row_values(0)
data.append(header)
for row_index in range(1, worksheet.nrows):
row_list = []
sale_amount = worksheet.cell_value(row_index, sale_amount_column_index)
sale = worksheet.cell_value(row_index, sale_amount_column_index)
sale_amount = float(str(sale).strip('$').replace(',', ''))
if sale_amount > 1400.0:
for column_index in range(worksheet.ncols):
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
if row_list:
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  我们来解释一下上面的代码。

1
2
from datetime import date
from xlrd import open_workbook, xldate_as_tuple

  这里我们导入了datetime模块的date()函数,以使我们可以将数值转换成日期并对日期进行格式化。xlrd模块中的xldate_as_tuple()函数可以将Excel中代表日期、时间或日期时间的数值转换为元组。将数值转换成元组可以提取出具体时间元素,并将时间元素格式化成不同的时间格式。

1
data = []

  这行代码创建了一个空列表data,我们将用要写入输出文件的行来填充它。

1
sale_amount = worksheet.cell_value(row_index, sale_amount_column_index)

  变量sale_amount用来保存行中的销售额,作为检验销售额是否大于$1,400.00的条件。cell_value()函数使用sale_amount_column_index的值来定位Sale Amount列。

1
2
3
4
5
6
7
8
9
for column_index in range(worksheet.ncols):
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)

  这里创建了一个for循环,来处理Sale Amount大于$1,400.00的那些行。我们先用cell_value()函数来提取出每个单元格的值,赋给变量cell_value,再用cell_type()函数提取出每个单元格的类型,赋给变量cell_type。然后,检验行中的每个值是否为日期类型(即检验cell_type是否等于3),如果是,就将这个值格式化成日期数据,追加进前面创建的空列表row_list中。如果cell_type不是3,则说明其为非日期类型数据,直接追加进列表row_list即可。

xlrd模块的说明文档(https://secure.simplistix.co.uk/svn/xlrd/trunk/xlrd/doc/xlrd.html?p=4966#sheet.Cell-class)中写到,单元格类型为3表示这个单元格中包含日期数据。

1
2
if row_list:
data.append(row_list)

  我们为输入文件的每一行都创建空列表row_list,但是只使用值填充某些空列表。对于输入文件的每一行,这里的if语句判断row_list是否为空,如果非空,则将其添加到data中。

1
2
3
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)

  我们在data中的各个列表之间和列表中的各个值之间进行迭代,将这些值写入输出文件。
  将要保留的行追加到data中的原因是,这样可以得到新的连续的行索引值。于是,当我们将这些行写入输出文件时,它们看上去就像是一个连续的整体,行与行之间不会出现缺口。否则,如果在主体for循环中处理各行的时候就将它们写入输出文件的话,那么xlwt的write()函数就会使用输入文件中原来的行索引值将行写入输出文件,造成行与行之间的缺口。
  我们在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

1.2 行中的值满足某个条件(pandas)

  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, 'january_2013', index_col=None)
data_frame_value_meets_condition = data_frame['Sale Amount'] = data_frame['Sale Amount'].str.replace(r'\$', '')
data_frame['Sale Amount'] = data_frame['Sale Amount'].str.replace(',', '')
data_frame['Sale Amount'] = data_frame['Sale Amount'].astype(float)
data_frame_value_meets_condition = data_frame[data_frame['Sale Amount'].astype(float) > 1400.0]
writer = pd.ExcelWriter(output_file)
data_frame_value_meets_condition.to_excel(writer, sheet_name='jan_13_output', index=False)
writer.save()

  在命令行窗口中运行脚本,得到的输出文件如下:
在这里插入图片描述

2.1 行中的值属于某个集合(基础Python)

  接下来,我们来筛选出january_2013工作表中购买日期属于1/24/20131/31/2013的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3

import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')
important_dates = ['01/24/2013', '01/31/2013']
purchase_date_column_index = 4
with open_workbook(input_file) as workbook:
worksheet = workbook.sheet_by_name('january_2013')
data = []
header = worksheet.row_values(0)
data.append(header)
for row_index in range(1, worksheet.nrows):
purchase_datetime = xldate_as_tuple(worksheet.cell_value(row_index, purchase_date_column_index),
workbook.datemode)
purchase_date = date(*purchase_datetime[0:3]).strftime('%m/%d/%Y')
row_list = []
if purchase_date in important_dates:
for column_index in range(worksheet.ncols):
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
if row_list:
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

在跟着书上的代码学习时,书上的代码有一些印刷错误,这让我踩了不少的坑。上面的代码是在PyCharm 2019.3.2(Professional Edition) + Anaconda环境下成功运行的。感谢实验室的学长的指导。

  这个脚本与基于条件筛选行的脚本非常相似,这里只对有差别的部分做一下简单的解释。

1
important_dates = ['01/24/2013', '01/31/2013']

  这行代码创建了列表important_dates,里面包含了要使用的日期。这里需要注意的一点是,日期不能写成类似于’1/24/2013’的格式,否则会出问题!
  在命令行窗口中运行这个脚本,得到输出文件如下:
在这里插入图片描述

2.2 行中的值属于某个集合(pandas)

  如果使用pandas模块的话,我们想筛选出特定的日期,需要用到函数isin()。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, 'january_2013', index_col=None)
important_dates = ['01/24/2013', '01/31/2013']
data_frame_value_in_set = data_frame[data_frame['Purchase Date'].isin(important_dates)]
writer = pd.ExcelWriter(output_file)
data_frame_value_in_set.to_excel(writer, sheet_name='jan_13_output', index=False)
writer.save()

  在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

3.1 行中的值匹配于特定模式(基础Python)

  要想使行中的值匹配于特定模式,我们需要引入正则表达式。下面的代码将会筛选出表格中客户姓名以大写字母’J’开头的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python3

import re
import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
from xlwt import Workbook

input_file = sys.argv[1]
output_file = sys.argv[2]

output_workbook = Workbook()
output_worksheet = output_workbook.add_sheet('jan_2013_output')
pattern = re.compile(r'(?P<my_pattern>^J.*)')
customer_name_column_index = 1
with open_workbook(input_file) as workbook:
worksheet = workbook.sheet_by_name('january_2013')
data = []
header = worksheet.row_values(0)
data.append(header)
for row_index in range(1, worksheet.nrows):
row_list = []
if pattern.search(worksheet.cell_value(row_index, customer_name_column_index)):
for column_index in range(worksheet.ncols):
cell_value = worksheet.cell_value(row_index, column_index)
cell_type = worksheet.cell_type(row_index, column_index)
if cell_type == 3:
date_cell = xldate_as_tuple(cell_value, workbook.datemode)
date_cell = date(*date_cell[0:3]).strftime('%m/%d/%Y')
row_list.append(date_cell)
else:
row_list.append(cell_value)
if row_list:
data.append(row_list)
for list_index, output_list in enumerate(data):
for element_index, element in enumerate(output_list):
output_worksheet.write(list_index, element_index, element)
output_workbook.save(output_file)

  这里我们只对正则表达式的部分做一个说明。

1
pattern = re.compile(r'(?P<my_pattern>^J.*)')

  这行代码使用re模块的compile()函数创建了一个正则表达式pattern。其中,r表示单引号之间的模式是一个原始字符串。元字符?P<my_pattern>捕获了名为<my_pattern>的组中匹配了的子字符串。我们要搜索的实际模式是^J.*。插入符号^表示在字符串开头搜索模式,句点.可以匹配任何除了换行符\n的字符,星号*则表示重复前面的字符0次或更多次。.*组合在一起使用表示除换行符\n之外的任意字符可以在J后面出现任意次。
  有关正则表达式的内容,可以参考我之前写的博客:Python与正则表达式
  我们在命令行窗口中运行这个脚本,得到输出文件。
在这里插入图片描述

3.2 行中的值匹配于特定模式(pandas)

  pandas模块提供了若干字符串和正则表达式函数,如startswith(), endswith(), match()search()等。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_excel(input_file, 'january_2013', index_col=None)
data_frame_value_matcher_pattern = data_frame[data_frame['Customer Name'].str.startswith("J")]
writer = pd.ExcelWriter(output_file)
data_frame_value_matcher_pattern.to_excel(writer, sheet_name='jan_13_output', index=False)
writer.save()

  在命令行窗口中运行脚本,得到输出文件如下:
在这里插入图片描述

Python数据分析基础之Excel文件(1)

参考资料:
《Python数据分析基础》,作者[美]Clinton W. Brownley,译者陈光欣,中国工信出版集团,人民邮电出版社

Excel文件简述

  Microsoft Excel是Microsoft为使用Windows和Apple Macintosh操作系统的电脑编写的一款电子表格软件,它几乎无处不在,是商业活动中不可或缺的工具。使用Python可以处理Excel文件中的数据。
  与Python的csv模块不同,Python中没有处理Excel文件的标准模块。我们需要安装xlrd和xlwt两个模块。

内省Excel工作簿

  我们创建一个Excel工作簿,并在其中添加3个独立的工作表,如下图所示。
在这里插入图片描述)在这里插入图片描述)在这里插入图片描述  Excel文件与CSV文件至少在两个重要方面有所不同。首先,CSV文件是纯文本文件,而Excel文件不是纯文本文件,我们不能在文本编辑器中打开它并查看数据。其次,与CSV文件不同,一个Excel工作簿被设计成包含多个工作表。
  通过内省一个工作簿,我们可以在实际开始处理工作簿中的数据之前,检查工作表的数目和每个工作表中的数据类型和数据量。
  下面我们使用Python分析上面的工作簿,来确定工作簿中工作表的数量、名称和每个工作表中行列的数量。

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3

import sys
from xlrd import open_workbook

input_file = sys.argv[1]

workbook = open_workbook(input_file)
print('Number of worksheets: ', workbook.nsheets)
for worksheet in workbook.sheets():
print("Worksheet name: ", worksheet.name, "\tRows: ", worksheet.nrows, "\tColumns: ", worksheet.ncols)

  我们来解释一下上面的代码。

1
from xlrd import open_workbook

  这行代码导入xlrd模块的open_workbook()函数来读取和分析Excel文件。

1
workbook = open_workbook(input_file)

  这行代码使用open_workbook()函数打开一个Excel输入文件,并赋给对象workbook。可以使用这个对象从工作簿中得到单独的工作表。

1
2
for worksheet in workbook.sheets():
print("Worksheet name: ", worksheet.name, "\tRows: ", worksheet.nrows, "\tColumns: ", worksheet.ncols)

  for循环语句在工作簿中的所有工作表之间迭代。workbook对象的sheets()方法可以识别出工作簿中所有的工作表。print语句使用workbook对象的name, nrows, ncols属性来确定每个工作表的名称以及行与列的数量。
  我们在命令行窗口中运行这个脚本,得到下面的输出结果。
在这里插入图片描述

Python数据分析基础之CSV文件(6)

  之前的5篇文章全部讲的是处理单个CSV文件。但是,在大多数情况下,我们需要处理很多文件,而手工处理效率低,或者文件多到手工处理根本行不通。在这种情况下,使用Python可以规模化地处理文件,减少了人为工作量的同时,也有效地减少了人为犯错的概率。
  为了规模化地处理CSV文件,我们需要使用Python内置的glob模块。我们使用下面的语句来导入该模块:

1
import glob

读取多个CSV文件

  在本例中,我们需要先新建3个CSV文件,如下图所示。
在这里插入图片描述)在这里插入图片描述)在这里插入图片描述
  我们先从最简单的行列计数开始。尽管有些时候我们知道要处理的文件中的内容,但在多数情况下,文件是别人发送给我们的,我们不会立即知道文件中的内容。因此,行列计数是最简单的,也是最重要的。
  我们编写下列代码,来读取上面创建的3个CSV文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3

import csv
import glob
import os
import sys

input_path = sys.argv[1]

file_counter = 0
for input_file in glob.glob(os.path.join(input_path, 'sales_*')):
row_counter = 1
with open(input_file, 'r', newline='') as csv_in_file:
filereader = csv.reader(csv_in_file)
header = next(filereader, None)
for row in filereader:
row_counter += 1
print('{0!s}: \t{1:d} rows \t{2:d} columns'.format(os.path.basename(input_file), row_counter, len(header)))
file_counter += 1
print('Number of files: {0:d}'.format(file_counter))

  我们来解释一下上面的代码。

1
import os

  这行代码导入了Python内置的os模块,它提供的函数可以列出和解析我们要处理的文件路径名。

1
2
for input_file in glob.glob(os.path.join(input_path, 'sales_*')):
...

  这里创建了一个for循环,它将数据处理扩展到多个文件中。os模块中的os.path.join()函数将圆括号中的两部分连在一起,其中input_path是包含输入文件的文件夹的路径,sales_*表示任何以模式sales_开头的文件名。glob模块中的glob.glob()函数将sales_*中的*转换为实际的文件名。也就是说,在这个例子中,glob.glob()os.path.join()两个函数创建了一个包含3个输入文件的列表。然后,for循环语句对于列表中每个输入文件执行下面缩进的各行代码。
  我们在命令行窗口中运行这个脚本。
在这里插入图片描述
  可以看到,输出结果显示脚本处理了3个文件,每个文件都有7行和5列。

从多个文件中连接数据

  上面讲了如何读取多个CSV文件,接下来讨论如何把多个CSV文件中的数据连接起来。

1.基础Python

  代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python3

import csv
import glob
import os
import sys

input_path = sys.argv[1]
output_file = sys.argv[2]

first_file = True
for input_file in glob.glob(os.path.join(input_path, 'sales_*')):
print(os.path.basename(input_file))
with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'a', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
if first_file:
for row in filereader:
filewriter.writerow(row)
first_file = False
else:
header = next(filereader, None)
for row in filereader:
filewriter.writerow(row)

  我们来解释一下上面的代码。

1
with open(output_file, 'a', newline='') as csv_out_file:

  和之前不同,这里open()函数中的参数不是'w'(可写)而是'a'(追加)。这里为什么不用可写模式呢?我们先来讨论一下可写模式和追加模式的区别。
模式|描述
——|——
w|打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a|打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
  使用追加模式,每个输入文件中的数据可以追加(也就是添加)到输出文件中。而如果使用可写模式,从一个输入文件中输出的数据会覆盖掉前一个输入文件中的数据,最后的输出文件会只包含最后处理的那个输入文件中的数据。

1
2
3
4
5
6
7
8
9
10
11
first_file = True
for input_file in glob.glob(os.path.join(input_path, 'sales_*')):
...
if first_file:
for row in filereader:
filewriter.writerow(row)
first_file = False
else:
header = next(filereader, None)
for row in filereader:
filewriter.writerow(row)

  这里的if-else语句根据前面创建的first_file变量来区分当前文件是否为第一个输入文件。做这个区分的目的是将标题行仅写入输出文件一次,避免重复输入。if代码块处理第一个输入文件,将所有行写入输出文件。else代码块处理余下的输入文件,使用next()方法将每个输入文件中的标题行赋给变量header,这样就可以在后面的处理过程中跳过标题行,然后将数据行写入输出文件。
  我们在命令行窗口运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  pandas模块可以直接从多个文件中连接数据。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3

import pandas as pd
import glob
import os
import sys

input_path = sys.argv[1]
output_file = sys.argv[2]

all_files = glob.glob(os.path.join(input_path, 'sales_*'))
all_data_frames = []
for file in all_files:
data_frame = pd.read_csv(file, index_col=None)
all_data_frames.append(data_frame)
data_frame_concat = pd.concat(all_data_frames, axis=0, ignore_index=True)
data_frame_concat.to_csv(output_file, index=False)

  使用pandas模块连接多个文件中的数据,基本过程就是将每个输入文件读取到pandas数据框中,将所有数据框追加到一个数据框列表,然后使用concat()函数将所有数据框连接成一个数据框。concat()函数可以使用axis参数来设置连接数据框的方式,axis=0表示从头到尾垂直堆叠,axis=1表示并排地平行堆叠。
  此处省略输出结果。

计算每个文件中值的总和与均值

  有些时候,当有多个输入文件时,需要对每个输入文件计算一些统计量,如总和、均值等。

1.基础Python

  代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python3

import csv
import glob
import os
import sys

input_path = sys.argv[1]
output_file = sys.argv[2]

output_header_list = ['file_name', 'total_sales', 'average_sales']
csv_out_file = open(output_file, 'a', newline='')
filewriter = csv.writer(csv_out_file)
filewriter.writerow(output_header_list)
for input_file in glob.glob(os.path.join(input_path, 'sales_*')):
with open(input_file, 'r', newline='') as csv_in_file:
filereader = csv.reader(csv_in_file)
output_list = []
output_list.append(os.path.basename(input_file))
header = next(filereader)
total_sales = 0.0
number_of_sales = 0.0
for row in filereader:
sale_amount = row[3]
total_sales += float(str(sale_amount).strip('$').replace(',', ''))
number_of_sales += 1
average_sales = '{0:.2f}'.format(total_sales / number_of_sales)
output_list.append(total_sales)
output_list.append(average_sales)
filewriter.writerow(output_list)
csv_out_file.close()

  我们来解释一下上面的代码。

1
2
3
4
output_header_list = ['file_name', 'total_sales', 'average_sales']
csv_out_file = open(output_file, 'a', newline='')
filewriter = csv.writer(csv_out_file)
filewriter.writerow(output_header_list)

  这里创建了一个输出文件的列标题列表output_header_list,并创建filewriter对象,使用writerow()将标题行写入输出文件。

1
2
output_list = []
output_list.append(os.path.basename(input_file))

  这里创建了一个空列表output_list,保存要写入输出文件中的每行输出。下面一行代码将输入文件的文件名追加到output_list中。

1
header = next(filereader)

  这行代码使用next()函数除去每个输入文件的标题行。
  之后的代码内容和之前学习过的内容类似,代码本身也比较简单易懂,故在此不再赘述。
  我们在命令行窗口运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  pandas模块中提供了一些摘要统计函数,比如sum()mean()。使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python3

import pandas as pd
import glob
import os
import sys

input_path = sys.argv[1]
output_file = sys.argv[2]

all_files = glob.glob(os.path.join(input_path, 'sales_*'))
all_data_frames = []
for input_file in all_files:
data_frame = pd.read_csv(input_file, index_col=None)
total_sales = pd.DataFrame([float(str(value).strip('$').replace(',', ''))
for value in data_frame.loc[:, 'Sale Amount']]).sum()
average_sales = pd.DataFrame([float(str(value).strip('$').replace(',', ''))
for value in data_frame.loc[:, 'Sale Amount']]).mean()
data = {'file_name': os.path.basename(input_file),
'total_sales': total_sales,
'average_sales': average_sales}
all_data_frames.append(pd.DataFrame(data, columns=['file_name', 'total_sales', 'average_sales']))
data_frames_concat = pd.concat(all_data_frames, axis=0, ignore_index=True)
data_frames_concat.to_csv(output_file, index=False)

  使用列表生成式将销售额这一列中带$的字符转换为浮点数,然后使用数据框函数将这个对象转换为一个DataFrame,以便可以使用sum()mean()这两个函数计算列的总计和均值。因为输出文件中的每行应该包含输入文件名,以及文件中销售额的总计和均值,所以可以将这3种数据组合成一个文本框,使用concat()函数将这些数据框连接成为一个数据框,并将这个数据框写入文件。
  此处省略输出结果。

写在最后

  以上就是Python数据分析中有关CSV文件的全部内容。由于本人能力有限,学习时间也比较短,难免出现纰漏,如果发现还请指正。
  我的邮箱:1398635912@qq.com

Python数据分析基础之CSV文件(5)

选取连续的行

  有时,工作表的头部和尾部是我们不想处理的。在很多情况下,工作表头部是标题、作者信息等,尾部是来源、假设、附加说明、注意事项等,我们并不需要处理这些内容。在这时,我们可以用Python来选取CSV文件中连续的行。
  我们把之前的supplier_data.csv文件打开,在工作表头部和尾部分别加入一些不需要处理的内容,如下图所示。
在这里插入图片描述

1.基础Python

  要使用基础Python选取特定的行,我们需要创建一个变量row_counter来跟踪行索引。在上面的示例中,我们知道应该保留行索引大于或等于3并且小于或等于15的行。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

row_counter = 0
with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
for row in filereader:
if 3 <= row_counter <= 15:
filewriter.writerow([value.strip() for value in row])
row_counter += 1

  如果我们对比一下之前的读写CSV文件的代码,不难发现上面的这个代码只是在原来的基础上稍微做了一下改动,引入变量row_counter,并且在遍历输入文件中所有的行时使用if语句跳过不需要的头部和尾部内容,只保留需要的行。
  对于输入文件的前3行,行索引值为0,1,2,此时if的判断结果为False,所以不执行if代码块,并将row_counter的值加1。对于输入文件的最后3行,行索引值为16,17,18,if的判断结果也是False,也不执行if代码块,并将row_counter的值加1。
  而对于想要保留的行,它们的行索引值为3~15,即row_counter在3和15之间。此时if判断结果为True,执行if代码块,处理这些行并将其写入输出文件。在列表生成式中,使用strip()函数除去每行两端的空格、制表符和换行符。
  我们在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  在pandas模块中,drop()函数可以根据行索引或列标题来丢弃行或列。也就是说,我们只需要使用drop函数丢弃掉前3行和后3行(即行索引为0,1,2,16,17,18的行)即可达到目的。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_csv(input_file, header=None)
data_frame = data_frame.drop([0, 1, 2, 16, 17, 18])
data_frame.columns = data_frame.iloc[0]
data_frame = data_frame.reindex(data_frame.index.drop(3))
data_frame.to_csv(output_file, index=False)

  pandas模块中的iloc函数功能强大,它可以根据行索引选取一个单独行作为列索引。reindex()函数可以为数据框重新生成索引。
  此处省略输出结果。

添加标题行

  有些时候,电子表格没有标题行,而我们却希望所有列都有列标题。使用Python脚本可以添加列标题。
  我们打开supplier_data.csv文件。删除掉标题行,并将其保存为supplier_data_no_header_row.csv。
在这里插入图片描述

1.基础Python

  代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
header_list = ['Supplier Name', 'Invoice Number', 'Part Number', 'Cost', 'Purchase Date']
filewriter.writerow(header_list)
for row in filereader:
filewriter.writerow(row)

  在上面的代码中,创建了一个列表变量header_list,其中包含了作为列标题的5个字符串。writerow()函数将这些列表值写入输出文件的第一行。
  我们在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  pandas模块中的read_csv()函数可以直接指定输入文件不包含标题行,并可以提供一个列标题列表。代码如下:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

header_list = ['Supplier Name', 'Invoice Number', 'Part Number', 'Cost', 'Purchase Date']
data_frame = pd.read_csv(input_file, header=None, names=header_list)
data_frame.to_csv(output_file, index=False)

  此处省略输出结果。

Python数据分析基础之CSV文件(4)

  有些时候,我们也并不需要文件中所有的列。有两种方法可以在CSV文件中选取特定的列:
  1.使用列索引值;
  2.使用列标题。

使用列索引值

1.基础Python

  举个例子,在之前的CSV文件中,我们只想保留供应商姓名和成本这两列,使用Python编辑代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

my_columns = [0, 3]
with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
for row_list in filereader:
row_list_output = []
for index_value in my_columns:
row_list_output.append(row_list[index_value])
filewriter.writerow(row_list_output)

  我们来解释一下上面的代码。

1
my_columns = [0, 3]

  这行代码创建了一个列表变量my_columns,其中包含了想要保留的两列的索引值,分别对应供应商姓名和成本两列。

1
2
3
4
5
for row_list in filereader:
row_list_output = []
for index_value in my_columns:
row_list_output.append(row_list[index_value])
filewriter.writerow(row_list_output)

  在这个for循环中,首先创建了一个空列表变量row_list_output,保存每行中想要保留的值。在内层的for循环语句中,在my_columns中的各个索引值之间进行迭代,通过append()函数使用每行中my_columns索引位置的值为row_list_output填充元素。这几行代码生成了一个列表,其中包含了每行中要写入输出文件的值。filewriterwriterow()方法需要一个字符串序列或数值序列,而列表row_list_out正是一个字符串序列。
  第一次循环时,index_value等于0,append()函数将row[0]加入row_list_output。此后,代码回到for index_value ...行,此时index_value等于3,append()函数将row[3]加入row_list_output。这时,内层for循环结束,代码前进到最后一行,writerow()row_list_output中的列表值写入输出文件。然后,代码回到外层for循环,开始处理输入文件中的下一行。
  我们在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_csv(input_file)
data_frame_column_by_index = data_frame.iloc[:, [0, 3]]
data_frame_column_by_index.to_csv(output_file, index=False)

  在上述代码中,iloc函数根据索引位置选取列。
  此处省略输出结果。

使用列标题

  当想保留的列的标题非常容易识别,或者在处理多个输入文件时,各个输入文件中列的位置会发生改变,但标题不变的时候,使用列标题来选取特定的列的方法非常有效。

1.基础Python

  举个例子,在前面的CSV文件中,我们只想保留发票号码和购买日期两列,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

my_columns = ['Invoice Number', 'Purchase Date']
my_columns_index = []
with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
header = next(filereader, None)
for index_value in range(len(header)):
if header[index_value] in my_columns:
my_columns_index.append(index_value)
filewriter.writerow(my_columns)
for row_list in filereader:
row_list_output = []
for index_value in my_columns_index:
row_list_output.append(row_list[index_value])
filewriter.writerow(row_list_output)

  我们来解释一下上面的代码。

1
2
my_columns = ['Invoice Number', 'Purchase Date']
my_columns_index = []

  这里创建了一个列表变量my_columns,其中包含的两个字符串即要保留的两列的名字。下面创建的空列表变量my_columns_index要使用两个保留列的索引值来填充。

1
header = next(filereader, None)

  这行代码使用next()函数从输入文件中读取第一行,并保存在列表变量header中。

1
2
3
4
5
6
7
8
9
for index_value in range(len(header)):
if header[index_value] in my_columns:
my_columns_index.append(index_value)
filewriter.writerow(my_columns)
for row_list in filereader:
row_list_output = []
for index_value in my_columns_index:
row_list_output.append(row_list[index_value])
filewriter.writerow(row_list_output)

  这几行代码与上一种方法的代码思路类似,在此不再赘述。
  在命令行窗口中运行这个脚本,并打开输出文件查看结果。
在这里插入图片描述

2.pandas

  使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_csv(input_file)
data_frame_column_by_name = data_frame.loc[:, ['Invoice Number', 'Purchase Date']]
data_frame_column_by_name.to_csv(output_file, index=False)

  在上面的代码中,使用loc函数来选取列。
  此处省略输出结果。

Python数据分析基础之CSV文件(3)

  这一节主要讲一下在读写CSV文件时筛选特定的行。
  有些时候,我们并不需要文件中所有的数据。例如,我们可能只需要一个包含特定词或数字的行的子集,或者是与某个具体日期关联的行的子集。在这些情况下,我们可以用Python筛选出特定的行来使用。
  下面主要来讲在输入文件中筛选出特定行的3种方法:
  1.行中的值满足某个条件;
  2.行中的值属于某个集合;
  3.行中的值匹配于某个模式(正则表达式)。
  其实,这三种筛选方法的代码在结构上是一致的。通用结构如下:

1
2
3
4
5
for row in filereader:
***if value in row meets some business rule or set of rules:***
do something
else:
do something else

  下面我们来详细讨论一下上述3种方法。

行中的值满足某个条件

1.基础Python

  在之前的样例中,如果我们只想保留供应商名字为Supplier Z或成本大于$600.00的行,并将结果写入输出文件。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
header = next(filereader)
filewriter.writerow(header)
for row_list in filereader:
supplier = str(row_list[0]).strip()
cost = str(row_list[3]).strip('$').replace(',', '')
if supplier == 'Supplier Z' or float(cost) > 600.0:
filewriter.writerow(row_list)

  我们来解释一下上面的代码。

1
2
header = next(filereader)
filewriter.writerow(header)

  这两行代码使用csv模块的next()函数读出输入文件的第一行,赋给列表变量header,并且使用writerow()函数将标题行写入输出文件。

1
supplier = str(row_list[0]).strip()

  这行代码取出每行数据中的供应商名字,赋给变量supplier。这行代码使用列表索引取出每行数据的第一个值row[0],使用str()函数将其转换为一个字符串,再使用strip()函数删除字符串两端的空格、制表符和换行符。最后,将处理好的字符串赋给变量supplier

1
cost = str(row_list[3]).strip('$').replace(',', '')

  这行代码取出每行数据中的成本,赋给变量cost。这行代码使用列表索引取出每行数据的第四个值row[3],使用str()函数将其转换为一个字符串,再使用strip('$')函数从字符串中删除$符号,再使用replace()函数从字符串中删除逗号。最后,将处理好的字符串赋给变量cost

1
2
if supplier == 'Supplier Z' or float(cost) > 600.0:
filewriter.writerow(row_list)

  这两行代码通过创建if语句来检验每行中的这两个值是否满足条件,若条件满足,则使用filewriterwriterow()函数将其写入输出文件。
  我们在命令行窗口运行这个脚本。在窗口中没有任何输出,我们可以打开输出文件来查看结果。
在这里插入图片描述

2.pandas

  pandas模块提供了一个loc函数,它可以同时选择特定的行与列。使用pandas模块的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_csv(input_file)
data_frame['Cost'] = data_frame['Cost'].str.strip('$').astype(float)
data_frame_value_meets_condition = data_frame.loc[(data_frame['Supplier Name'].str.contains('Z'))
| (data_frame['Cost'] > 600.0), :]
data_frame_value_meets_condition.to_csv(output_file, index=False)

  我们在命令行窗口运行这个脚本,同样地,在屏幕上我们看不到任何输出,我们可以打开输出文件查看结果。
  此处省略输出结果。

行中的值属于某个集合

  有些时候,当行中的值属于某个集合时,才需要保留这些行。例如,我们希望在数据集中保留那些供应商名字属于集合{Supplier X, Supplier Y}的行,或者希望保留所有购买日期属于集合{'1/20/2014', '1/30/2014'}的行。在这种情况下,我们可以检验行中的值是否属于某个集合,筛选出具有属于该集合的行。

1.基础Python

  代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

important_dates = ['1/20/2014', '1/30/2014']
with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
header = next(filereader)
filewriter.writerow(header)
for row_list in filereader:
a_date = row_list[4]
if a_date in important_dates:
filewriter.writerow(row_list)

  我们来讨论一下上述代码。

1
important_dates = ['1/20/2014', '1/30/2014']

  这行代码创建了一个名为important_dates的列表变量,其中包含了两个特定日期。这个列表变量就是我们的集合。

1
a_date = row_list[4]

  这行代码取出每一行的购买日期,并赋给变量a_date

1
2
if a_date in important_dates:
filewriter.writerow(row_list)

  这行代码创建了一个if语句来检验变量a_date中的购买日期是否属于集合important_date,如果变量值在集合中,下一行代码就将这一行写入输出文件。
  我们在命令行窗口中运行这个脚本,并打开输出文件来查看结果。
在这里插入图片描述

2.pandas

  使用pandas模块的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_csv(input_file)
important_dates = ['1/20/2014', '1/30/2014']
data_frame_value_in_set = data_frame.loc[data_frame['Purchase Date'].isin(important_dates), :]
data_frame_value_in_set.to_csv(output_file, index=False)

  这里的输出结果和上面基础Python方法的输出结果一致,故省略。

行中的值匹配于某个模式(正则表达式)

  有些时候,当行中的值匹配或包含一个特定模式(正则表达式)时,才需要保留这些行。例如,我们希望在数据集中保留所有发票开始于“001-”的行,或者希望保留所有供应商名字中包含“Y”的行。在这种情况下,我们可以检验行中的值是否匹配或包含某种模式,然后筛选出匹配或包含该模式的行。

1.基础Python

  代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3

import csv
import re
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

pattern = re.compile(r'(?P<my_pattern_group>^001-.*)', re.I)
with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file)
filewriter = csv.writer(csv_out_file)
header = next(filereader)
filewriter.writerow(header)
for row_list in filereader:
invoice_number = row_list[1]
if pattern.search(invoice_number):
filewriter.writerow(row_list)

  我们来讨论一下上述代码。

1
import re

  这一行代码导入了re模块,也就是正则表达式模块。

1
pattern = re.compile(r'(?P<my_pattern_group>^001-.*)', re.I)

  这行代码使用re模块的compile()函数创建变量pattern。其中r表示将单引号之间的模式当作原始字符串来处理。元字符?P<my_pattern_group>捕获了名为<my_pattern_group>的组中匹配了的字符串,以便在需要的时候将它们打印到屏幕或写入文件。这里要搜索的实际模式是^001-.*。插入符号^表示只在字符串开头搜索模式,*表示重复前面的字符0次或更多次,.*组合在一起使用表示除换行符\n之外的任意字符可以在“001-”后面出现任意次。最后,参数re.I告诉了正则表达式进行大小写敏感的匹配。
  有关正则表达式的内容,可以参考我之前写的博客:Python与正则表达式

1
2
if pattern.search(invoice_number):
filewriter.writerow(row_list)

  这里使用re模块的search()函数在invoice_number的值中寻找模式,如果模式出现在变量值中,就将这行写入输出文件。
  我们在命令行窗口运行这个脚本,得到输出文件如下图所示。
在这里插入图片描述

2.pandas

  使用pandas模块的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3

import pandas as pd
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

data_frame = pd.read_csv(input_file)
data_frame_value_matches_pattern = data_frame.loc[data_frame['Invoice Number'].str.startswith("001-"), :]
data_frame_value_matches_pattern.to_csv(output_file, index=False)

  上述代码中,startswith()函数用来搜索数据,不用再使用正则表达式。
  这里的输出结果和上面基础Python方法的输出结果一致,故省略。

Python数据分析基础之CSV文件(2)

基本字符串分析的失败

  在上一节我们讲了使用Python对CSV文件进行读写。事实上,当列中包含额外的逗号时,CSV分析会失败。
  我们打开supplier_data.csv,将Cost列中的最后两个成本数量分别改为$6,015.00和$1,006,015.00,如下图所示。
在这里插入图片描述  之前的脚本是按照行中的逗号分析每行数据的,这会让脚本错误地拆分最后两行的数据,因为数据中有逗号。
  有很多方法可以改进这个脚本中的代码,使它可以正确地处理包含逗号的数值。我们可以使用正则表达式,但为了不使脚本复杂化,我们可以使用Python内置的csv模块。这个模块可以方便灵活地处理复杂的CSV文件。

修改后的脚本读写CSV文件

  使用Python内置的csv模块处理CSV文件的一个优点是,这个模块就是被设计用于正确处理数据值中的嵌入逗号和其他复杂模式的,它可以识别出这些模式并正确地处理数据。
  修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3

import csv
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

with open(input_file, 'r', newline='') as csv_in_file:
with open(output_file, 'w', newline='') as csv_out_file:
filereader = csv.reader(csv_in_file, delimiter=',')
filewriter = csv.writer(csv_out_file, delimiter=',')
for row_list in filereader:
print(row_list)
filewriter.writerow(row_list)

  我们来解释一下上面的代码。

1
import csv

  这行代码导入csv模块,以便可以使用其中的函数来分析输入文件,写入输入文件。

1
2
filereader = csv.reader(csv_in_file, delimiter=',')
filewriter = csv.writer(csv_out_file, delimiter=',')

  这两行代码使用csv模块中的reader()函数和writer()函数分别创建了一个文件读取对象filereader和文件写入对象filewriter。这两个函数中的第二个参数delimiter=','是默认分隔符,如果输入文件和输出文件都是用逗号分隔的,就不需要指定这个参数。这里指定分隔符函数,是为了防备待处理的输入文件或要写入的输出文件具有不同的分隔符,例如分号;或制表符\t

1
filewriter.writerow(row_list)

  这行代码使用filewriter对象的writerow()函数来将每行中的列表值写入输出文件。
  我们在命令行窗口运行这个脚本,如下图所示。
在这里插入图片描述
在这里插入图片描述