如何将单个CSV文件切割成按字段分组的几个较小的文件?

时间:2012-03-06 18:12:06

标签: python csv

我将世界银行千年发展目标中的大量数据设为CSV。数据显示如下:

Country Code   Country Name   Indicator
ABW            Aruba          % Forest coverage
ADO            Andorra        % Forest coverage
AFG            Afghanistan    % Forest coverage
...
ABW            Aruba          % Literacy rate
ADO            Andorra        % Literacy rate
AFG            Afghanistan    % Literacy rate
...
ABW            Aruba          % Another indicator
ADO            Andorra        % Another indicator
AFG            Afghanistan    % Another indicator

该文件目前为8.2MB。我打算为这些数据编写一个Web界面,我想按国家/地区分割数据,这样我就可以发出ajax请求,这样我就可以为每个国家加载一个单独的CSV。

我迷失在如何以编程方式或使用任何工具执行此操作。我不一定需要Python,但这是我最了解的。我不一定需要一个完整的脚本,一般指针如何解决这个问题。

我正在使用的原始数据源位于:

http://duopixel.com/stack/data.csv

4 个答案:

答案 0 :(得分:4)

您可以使用Python csv moduleitertools.groupby 以下示例在Python 2.7.1上进行了测试 编辑:更新了对问题添加的新信息的帐户答案。

import csv, itertools as it, operator as op

csv_contents = []
with open('yourfile.csv', 'rb') as fin:
  dict_reader = csv.DictReader(fin)   # default delimiter is comma
  fieldnames = dict_reader.fieldnames # save for writing
  for line in dict_reader:            # read in all of your data
    csv_contents.append(line)         # gather data into a list (of dicts)

# input to itertools.groupby must be sorted by the grouping value 
sorted_csv_contents = sorted(csv_contents, key=op.itemgetter('Country Name'))

for groupkey, groupdata in it.groupby(sorted_csv_contents, 
                                      key=op.itemgetter('Country Name')):
  with open('slice_{:s}.csv'.format(groupkey), 'wb') as fou:
    dict_writer = csv.DictWriter(fou, fieldnames=fieldnames)
    dict_writer.writeheader()         # new method in 2.7; use writerow() in 2.6-
    dict_writer.writerows(groupdata)

其他说明:

  • 您可以使用常规的csv阅读器和编写器,但DictReader和DictWriter很好,因为您可以按名称引用列。
  • 始终使用' b'在读取或写入.csv文件时标记,因为在Windows上对行结尾的处理方式有所不同。
  • 如果有什么不清楚请告诉我,我会进一步解释!

答案 1 :(得分:4)

One-liner:

awk -F "," 'NF>1 && NR>1 {print $0 >> ("data_" $1 ".csv"); close("data_" $1 ".csv")}' data.csv

这将创建名为data_ABW等的新文件,其中包含适当的信息。 NR>1部分跳过标题行。然后,对于每一行,将整行($0)附加到名为Data_$1的文件,其中$1替换为第一列中的文本那条线。最后,close语句确保没有太多打开的文件。如果你没有这么多国家,你可以摆脱这个并显着提高命令的速度。

在回答下面@ Lenwood的评论时,要在每个输出文件中包含标题,您可以这样做:

awk -F "," 'NR==1 {header=$0}; NF>1 && NR>1 {if(! files[$1]) {print header >> ("data_" $1 ".csv"); files[$1]=1}; print $0 >> ("data_" $1 ".csv"); close("data_" $1 ".csv")}' data.csv

(您可能必须逃避感叹号......)第一个新部件NR==1 {header=$0};只将输入文件的第一行存储为变量header。然后,另一个新的部分if(! files[$1]) ... files[$1]=1};使用关联数组files来跟踪它是否已将标头放入给定文件中,如果没有,则将其放在那里。

请注意,这会附加文件,因此如果这些文件已经存在,则只会添加它们。因此,如果在主文件中获得新数据,则可能需要在再次运行此命令之前删除其他文件。

(如果不明显,如果您希望将文件命名为data_Aruba,则可以将$1更改为$2。)

答案 2 :(得分:2)

使用pandas Python data analysis library非常简单:

from pandas.io.parsers import read_csv

df = read_csv(input_file, header=1, sep='\t', index_col=[0,1,2])
for (country_code, country_name), group in df.groupby(level=[0,1]):
    group.to_csv(country_code+'.csv')

结果

$ for f in *.csv ; do echo $f; cat $f; echo; done

ABW.csv
Country Code,Country Name,Indicator
ABW,Aruba,% Forest coverage
ABW,Aruba,% Literacy rate
ABW,Aruba,% Another indicator

ADO.csv
Country Code,Country Name,Indicator
ADO,Andorra,% Forest coverage
ADO,Andorra,% Literacy rate
ADO,Andorra,% Another indicator

AFG.csv
Country Code,Country Name,Indicator
AFG,Afghanistan,% Forest coverage
AFG,Afghanistan,% Literacy rate
AFG,Afghanistan,% Another indicator

答案 3 :(得分:1)

在shell脚本中。

首先,awk '{print $1}' | sort | uniq > code.lst会为您提供国家/地区代码列表。然后,您可以遍历国家/地区代码并使用grep选择与代码匹配的所有youfilename.csv行。

for c in `ls code.lst` do
   grep $c youfilename.csv > youfilename_$c.csv
done