2.7 从多个文件中连接数据
对于包含相似数据的多个文件,你经常希望将其中的数据连接起来,以使所有数据都在一个文件中。以前你完成这种工作的方式可能是:打开每个文件,将每个工作表中的数据复制粘贴到一个单独的工作表中。这种手动处理方式不但浪费时间,还容易出错。而且,在有些情况下,因为需要合并的文件数量和文件大小的原因,手动处理根本不可能完成。
知道了手动连接数据的局限性之后,接下来看一下如何通过 Python 完成这个任务。这里将会使用本节开头创建的 3 个 CSV 文件来演示如何从多个文件中连接数据。
01. 基础Python
要使用基础 Python 将多个输入文件中的数据垂直连接成一个输出文件,在文本编辑器中输入下列代码,然后将文件保存为 9csv_reader_concat_rows_from_multiple_files.py:
1 #!/usr/bin/env python3 2 import csv 3 import glob 4 import os 5 import sys 6 input_path = sys.argv[1] 7 output_file = sys.argv[2] 8 9 first_file = True 10 for input_file in glob.glob(os.path.join(input_path,'sales_*')): 11 print(os.path.basename(input_file)) 12 with open(input_file, 'r', newline='') as csv_in_file: 13 with open(output_file, 'a', newline='') as csv_out_file: 14 filereader = csv.reader(csv_in_file) 15 filewriter = csv.writer(csv_out_file) 16 if first_file: 17 for row in filereader: 18 filewriter.writerow(row) 19 first_file = False 20 else: 21 header = next(filereader, None) 22 for row in filereader: 23 filewriter.writerow(row)
第 13 行代码是一个 with 语句,用来打开输出文件。在前面介绍写入输出文件的示例中,open 函数中的字符串是 'w',表示以可写的方式打开输出文件。
在这个示例中,使用 'a' 代替 'w' 以追加的方式打开输出文件,以使每个输入文件中的数据可以追加(也就是添加)到输出文件中。如果使用可写方式,从一个输入文件中输出的数据会覆盖掉前一个输入文件中的数据,最后的输出文件会只包含最后处理的那个输入文件中的数据。
从第 16 行代码开始的 if-else 语句根据第 9 行代码中创建的 first_file 变量来区分当前文件是第一个输入文件,还是其后的输入文件。在输入文件中做这个区分的目的是将标题行仅写入输出文件一次。if 代码块处理第一个输入文件,将包括标题行的所有行写入输出文件。else 代码块处理所有余下的输入文件,使用 next 方法将每个文件中的标题行赋给一个变量(这样就可以在后面的处理过程中跳过标题行),然后将其余数据行写入输出文件。
要运行这个脚本,在命令行中输入以下命令,然后按回车键:
python 9csv_reader_concat_rows_from_multiple_files.py "C:\Users\Clinton\Desktop"\ output_files\9output.csv
你可以看到输入文件的文件名被打印到屏幕上,如图 2-16 所示。
图 2-16:Python 脚本输出:连接成输出文件的文件名
屏幕上的输出展示了处理过的文件的文件名。此外,脚本还将 3 个输入文件中的数据连接成了一个单独的输出文件 9output.csv,位于桌面上的 output_files 文件夹中。图 2-17 显示了文件中的内容。
图 2-17:输出 CSV 文件,连接了来自于多个输入文件的行
图中显示,脚本成功地连接了来自于 3 个输入文件的数据。输出文件中包含一个标题行和 3 个输入文件中所有的数据行。
在解释代码时,前面提到了在第 13 行代码中为什么要使用 'a'(追加模式),而不是 'w'(可写模式),还提到了为什么要区分第一个输入文件和其后的输入文件。要实际验证一下的话,你可以将 'a' 改成 'w',然后保存脚本,对 3 个输入文件重新执行一下,看看输出文件有什么变化。同样,你也可以删除 if-else 语句,将所有输入文件中的所有行都打印出来,看看输出会如何改变。
还有一点需要注意,这个示例中的模式 'sales_*' 相对来说是比较特殊的,也就是说在你的桌面上除了 3 个输入文件之外,不太可能还有文件名以 sales_ 开头的文件。其他情况下,你更可能使用一个不那么特殊的模式,比如 '*.csv',来搜索所有 CSV 文件。在这种情况下,你不应该在包含所有输入文件的文件夹内创建输出文件。不应该这样做的原因就是,你在打开输出文件的同时还在处理输入文件。这样,如果你的模式是 '*.csv',输出文件也是个 CSV 文件,那么脚本就会像处理输入文件一样试图处理输出文件,这就会导致问题和错误。这种可能性就是最好在另一个文件夹中处理输出文件的原因,就像在这个示例中所做的一样。
02. pandas
pandas 可以直接从多个文件中连接数据。基本过程就是将每个输入文件读取到 pandas 数据框中,将所有数据框追加到一个数据框列表,然后使用 concat 函数将所有数据框连接成一个数据框。concat 函数可以使用 axis 参数来设置连接数据框的方式,axis=0 表示从头到尾垂直堆叠,axis=1 表示并排地平行堆叠。
要使用 pandas 将多个输入文件中的数据垂直连接成一个输出文件,在文本编辑器中输入下列代码,然后将文件保存为 pandas_concat_rows_from_multiple_files.py:
#!/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)
这段代码垂直堆叠数据框。如果你需要平行连接数据,那么就在 concat 函数中设置 axis=1。除了数据框,pandas 中还有一个数据容器,称为序列。你可以使用同样的语法去连接序列,只是要将连接的对象由数据框改为序列。
有时候,除了简单地垂直或平行连接数据,你还需要基于数据集中的关键字列的值来连接数据集。pandas 提供了类似 SQL join 操作的 merge 函数。如果你很熟悉 SQL join,那么就非常容易理解 merge 函数的语法:pd.merge(DataFrame1, DataFrame2, on='key', how='inner')。
Python 的另一个内置模块 NumPy 也提供了若干函数来垂直或平行连接数据。通常是将 NumPy 导入为 np。然后,要垂直连接数据,你可以使用 np.concatenate([array1, array2], axis=0)、np.vstack((array1, array2)) 或 np.r_[array1, array2]。同样,要平行连接数据,你可以使用 np.concatenate([array1, array2], axis=1)、np.hstack((array1, array2)) 或 np.c_[array1, array2]。
要运行这个脚本,在命令行中输入以下命令,然后按回车键:
python pandas_concat_rows_from_multiple_files.py "C:\Users\Clinton\Desktop"\ output_files\pandas_output.csv
你可以打开输出文件 pandas_output.csv 查看一下结果。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论