参考资料:
《Python数据分析基础》,作者[美]Clinton W. Brownley,译者陈光欣,中国工信出版集团,人民邮电出版社
在一个大文件集合中查找一组项目
当我们有大量历史数据的时候,要找到真正需要的数据是非常困难的。我们可以打开每个文件,找出需要的记录,并将其复制粘贴到一个新文件中。但这个过程既浪费时间,又容易出错。使用Python可以自动化地完成整个过程,既节省时间,又不会出错。
首先,我们在合适的位置创建一个文件夹,命名为file_archive。然后,我们打开Excel,输入下图所示的数据,并将其命名为supplies_2012.csv保存在file_archive文件夹中。
我们再打开Excel,如下图所示创建一个Excel工作簿,命名为supplies.xls,并保存在file_archive文件夹中。这个Excel工作簿中包含两个工作表:supplies_2013和supplies_2014。
然后,我们将这个Excel工作簿另存为supplies.xlsx,同样保存在file_archive文件夹中。
现在,在file_archive文件夹中应该有3个文件。
我们还需要建立一个item_numbers_to_find.csv文件。我们要搜索的5个数值项目就是下表所示的5个数值。
我们的搜索任务是,搜索file_archive文件夹,找出包含我们所需的数值项目的文件,当找到一个数值项目时,需要把包含这个项目的整行数据写入输出文件。
Python脚本的代码如下:
#!/usr/bin/env python3
import csv
import glob
import os
import sys
from datetime import date
from xlrd import open_workbook, xldate_as_tuple
item_numbers_file = sys.argv[1]
path_to_folder = sys.argv[2]
output_file = sys.argv[3]
item_numbers_to_find = []
with open(item_numbers_file, 'r', newline='') as item_numbers_csv_file:
filereader = csv.reader(item_numbers_csv_file)
for row in filereader:
item_numbers_to_find.append(row[0])
print(item_numbers_to_find)
filewriter = csv.writer(open(output_file, 'a', newline=''))
file_counter = 0
line_counter = 0
count_of_item_numbers = 0
for input_file in glob.glob(os.path.join(path_to_folder, '*.*')):
file_counter += 1
if input_file.split('.')[1] == 'csv':
with open(input_file, 'r', newline='') as csv_in_file:
filereader = csv.reader(csv_in_file)
header = next(filereader)
for row in filereader:
row_of_output = []
for column in range(len(header)):
if column == 3:
cell_value = str(row[column]).lstrip('$').replace(',', '').strip()
row_of_output.append(cell_value)
else:
cell_value = str(row[column]).strip()
row_of_output.append(cell_value)
row_of_output.append(os.path.basename(input_file))
if row[0] in item_numbers_to_find:
filewriter.writerow(row_of_output)
count_of_item_numbers += 1
line_counter += 1
elif input_file.split('.')[1] == 'xls' or input_file.split('.')[1] == 'xlsx':
workbook = open_workbook(input_file)
for worksheet in workbook.sheets():
try:
header = worksheet.row_values(0)
except IndexError:
pass
for row in range(1, worksheet.nrows):
row_of_output = []
for column in range(len(header)):
if worksheet.cell_type(row, column) == 3:
cell_value = xldate_as_tuple(worksheet.cell(row, column).value, workbook.datemode)
cell_value = str(date(*cell_value[0:3])).strip()
row_of_output.append(cell_value)
else:
cell_value = str(worksheet.cell_value(row, column)).strip()
row_of_output.append(cell_value)
row_of_output.append(os.path.basename(input_file))
row_of_output.append(worksheet.name)
if str(worksheet.cell(row, 0).value).split('.')[0].strip() in item_numbers_to_find:
filewriter.writerow(row_of_output)
count_of_item_numbers += 1
line_counter += 1
print('Number of files: ', file_counter)
print('Number of lines: ', line_counter)
print('Number of item numbers: ', count_of_item_numbers)
这个脚本看似很长,但其实都是前面学过的内容,它只是将前面三章的内容综合了一下。我们在命令行窗口中运行这个脚本。
为CSV文件中数据的任意数目分类计算统计量
我们创建一个CSV数据文件customer_category_history.csv,其中包含一个客户购买服务包的数据集。
我们需要做的,是在这个数据集上执行计算,得到客户在他们购买的每个服务包类别上花费的总时间。代码如下:
#!/usr/bin/env python3
import csv
import sys
from datetime import date, datetime
def date_diff(date1, date2):
try:
diff = str(datetime.strptime(date1, '%m/%d/%Y') - datetime.strptime(date2, '%m/%d/%Y')).split()[0]
except:
diff = 0
if diff == '0:00:00':
diff = 0
return diff
input_file = sys.argv[1]
output_file = sys.argv[2]
packages = {}
previous_name = 'N/A'
previous_package = 'N/A'
previous_package_date = 'N/A'
first_row = True
today = date.today().strftime('%m/%d/%Y')
with open(input_file, 'r', newline='') as input_csv_file:
filereader = csv.reader(input_csv_file)
header = next(filereader)
for row in filereader:
current_name = row[0]
current_package = row[1]
current_package_date = row[3]
if current_name not in packages:
packages[current_name] = {}
if current_package not in packages[current_name]:
packages[current_name][current_package] = 0
if current_name != previous_name:
if first_row:
first_row = False
else:
diff = date_diff(today, previous_package_date)
if previous_package not in packages[previous_name]:
packages[previous_name][previous_package] = int(diff)
else:
packages[previous_name][previous_package] += int(diff)
else:
diff = date_diff(current_package_date, previous_package_date)
packages[previous_name][previous_package] += int(diff)
previous_name = current_name
previous_package = current_package
previous_package_date = current_package_date
header = ['Customer Name', 'Category', 'Total Time (in Days)']
with open(output_file, 'w', newline='') as output_csv_file:
filewriter = csv.writer(output_csv_file)
filewriter.writerow(header)
for customer_name, customer_name_value in packages.items():
for package_category, package_category_value in packages[customer_name].items():
row_of_output = []
print(customer_name, package_category, package_category_value)
row_of_output.append(customer_name)
row_of_output.append(package_category)
row_of_output.append(package_category_value)
filewriter.writerow(row_of_output)
这个脚本使用了Python字典数据结构来组织和保存计算结果。实际上,这个脚本中使用的字典是嵌套的字典。外部字典的名称为packages
,键为客户名称,与其对应的值是另一个字典,其中的键为服务包类别的名称,值为客户拥有这个服务包的天数。
我们在命令行窗口中运行这个脚本。
为文本文件中数据的任意数目分类计算统计量
文本文件(也称平面文件)也是商业中常用的文件类型。CSV文件实际上就是以逗号分隔的文本文件形式保存的。活动日志、错误日志和交易记录是商业数据保存在文本文件中的几个更常见的例子。
接下来我们试着访问MySQL错误日志。
作者注:MySQL错误日志文件的路径为:C:\ProgramData\MySQL\MySQL Server 8.0\Data\LAPTOP-LNF22KFU.err
我们选择以记事本方式打开该文件,可以看到错误日志中的信息。
每个人的MySQL错误日志文件中的数据是不一样的。参考书上为我们提供了一个独立的、有代表性的MySQL错误日志文件,我们把它下载下来。
接下来,我们需要做的就是通过Python脚本来进行错误消息的分析和计算。代码如下:
#!/usr/bin/env python3
import sys
input_file = sys.argv[1]
output_file = sys.argv[2]
messages = {}
notes = []
with open(input_file, 'r', newline='') as text_file:
for row in text_file:
if '[Note]' in row:
row_list = row.split(' ', 4)
day = row_list[0].strip()
note = row_list[4].strip('\n').strip()
if note not in notes:
notes.append(note)
if day not in messages:
messages[day] = {}
if note not in messages[day]:
messages[day][note] = 1
else:
messages[day][note] += 1
filewriter = open(output_file, 'w', newline='')
header = ['Date']
header.extend(notes)
header = ','.join(map(str, header)) + '\n'
print(header)
filewriter.write(header)
for day, day_value in messages.items():
row_of_output = []
row_of_output.append(day)
for index in range(len(notes)):
if notes[index] in day_value.keys():
row_of_output.append(day_value[notes[index]])
else:
row_of_output.append(0)
output = ','.join(map(str, row_of_output)) + '\n'
print(output)
filewriter.write(output)
filewriter.close()
我们在命令行窗口中运行这个脚本。