我的交叉列表CSV文件如下所示:
Country,Age,All,M,F
UK,Under65,30987,15000,15987
UK,65andOver,12345,6345,6000
Germany,Under65,32646,15642,17004
Germany,65andOver,14747,7192,7555
France,Under65,31587,16286,15301
France,65andOver,13741,6187,7554
我想修改它,看起来像这样:
Country,Under65_All,Under65_M,Under65_F,65andOver_All,65andOver_M,65andOver_F
UK,30987,15000,15987,12345,6345,6000
Germany,32646,15642,17004,14747,7192,7555
France,31587,16286,15301,13741,6187,7554
每个国家/地区现在都位于一行,并且已扩展了列数(没有交叉表)。
我试图在Python 3中执行此操作.Excel VBA已经用完,因为我使用了一些较大的CSV文件来达到行限制。
我想我想要做的就是"聚合"另外一个"小组"步。我已经阅读了CSV文件并计算了可能有用的各种值:唯一国家/地区的数量(3),唯一年龄组的数量(2),名称和最终输出文件所需的列数(7)。
我希望使代码尽可能灵活,以便它可以读取包含x个唯一国家/地区的y文件和y个唯一年龄组和z个列变量的文件。并且最终文件将包含一个标题行,其中y * z + 1列且低于此x行数。
希望这是有道理的,任何帮助/指针都会受到赞赏。
答案 0 :(得分:4)
我打算提出一个pandas
解决方案,因为否则你正在重新发明轮子,但是没有办法解决它需要一些习惯的事实。好处是,一旦你选择了这样的操作变得相对简单。
import pandas as pd
df = pd.read_csv("c.dat")
df = pd.melt(df, id_vars=["Country", "Age"], var_name="Other")
df["Column"] = df.pop("Age") + "_" + df.pop("Other")
df = df.pivot(index="Country", columns="Column")
df.columns = df.columns.droplevel(0)
df.to_csv("out.csv")
产生
>>> !cat out.csv
Country,65andOver_All,65andOver_F,65andOver_M,Under65_All,Under65_F,Under65_M
France,13741,7554,6187,31587,15301,16286
Germany,14747,7555,7192,32646,17004,15642
UK,12345,6000,6345,30987,15987,15000
(如果我们真的想要,我们可以对列进行排序。)
在这里复制整个教程是没有意义的 - 尽管你可以阅读重塑教程here - 但我至少可以概述一下这是如何工作的。
一步一步。首先,我们将csv文件读入DataFrame
(有点像excel表):
>>> df = pd.read_csv("c.dat")
>>> df
Country Age All M F
0 UK Under65 30987 15000 15987
1 UK 65andOver 12345 6345 6000
2 Germany Under65 32646 15642 17004
3 Germany 65andOver 14747 7192 7555
4 France Under65 31587 16286 15301
5 France 65andOver 13741 6187 7554
您可以按行,列等访问框架。出于您的目的,我们可以融合(取消)此数据:
>>> df = pd.melt(df, id_vars=["Country", "Age"], var_name="Other")
>>> df
Country Age Other value
0 UK Under65 All 30987
1 UK 65andOver All 12345
2 Germany Under65 All 32646
3 Germany 65andOver All 14747
4 France Under65 All 31587
5 France 65andOver All 13741
6 UK Under65 M 15000
7 UK 65andOver M 6345
8 Germany Under65 M 15642
9 Germany 65andOver M 7192
10 France Under65 M 16286
11 France 65andOver M 6187
12 UK Under65 F 15987
13 UK 65andOver F 6000
14 Germany Under65 F 17004
15 Germany 65andOver F 7555
16 France Under65 F 15301
17 France 65andOver F 7554
现在我们有了我们想要的行标签(国家/地区)和有关其他列的信息,无论它们是什么,以及值。你想要“年龄”和“其他”中的任何东西组合在一起,所以:
>>> df["Column"] = df.pop("Age") + "_" + df.pop("Other")
>>> df
Country value Column
0 UK 30987 Under65_All
1 UK 12345 65andOver_All
2 Germany 32646 Under65_All
3 Germany 14747 65andOver_All
4 France 31587 Under65_All
5 France 13741 65andOver_All
6 UK 15000 Under65_M
7 UK 6345 65andOver_M
8 Germany 15642 Under65_M
9 Germany 7192 65andOver_M
10 France 16286 Under65_M
11 France 6187 65andOver_M
12 UK 15987 Under65_F
13 UK 6000 65andOver_F
14 Germany 17004 Under65_F
15 Germany 7555 65andOver_F
16 France 15301 Under65_F
17 France 7554 65andOver_F
现在所有的辛勤工作都已完成。我们只需致电pivot
即可将其转为:
>>> df = df.pivot(index="Country", columns="Column")
>>> df
value \
Column 65andOver_All 65andOver_F 65andOver_M Under65_All Under65_F
Country
France 13741 7554 6187 31587 15301
Germany 14747 7555 7192 32646 17004
UK 12345 6000 6345 30987 15987
Column Under65_M
Country
France 16286
Germany 15642
UK 15000
(在屏幕上看起来更好。)它给了我们额外的“价值”水平,你不想要,所以让我们放弃:
>>> df.columns = df.columns.droplevel(0)
>>> df
Column 65andOver_All 65andOver_F 65andOver_M Under65_All Under65_F \
Country
France 13741 7554 6187 31587 15301
Germany 14747 7555 7192 32646 17004
UK 12345 6000 6345 30987 15987
Column Under65_M
Country
France 16286
Germany 15642
UK 15000
然后我们把它写到csv:
>>> df.to_csv("out.csv")
答案 1 :(得分:3)
最明显的解决方法分为两个阶段:
在我看来,最方便的数据结构是defaultdict
个dict
个对象,使用主键(在本例中为“Country”)作为顶级键,和组合键的连接(在本例中为“Age”)和附加键作为二级dicts的键:
{
'France': {
'65andOver_All': '13741',
'65andOver_F': '7554',
'65andOver_M': '6187',
'Under65_All': '31587',
'Under65_F': '15301',
'Under65_M': '16286'
},
'Germany': {
'65andOver_All': '14747',
# ...
},
# ...
}
还需要跟踪使用的标头 - set
可能是最佳选择。
使用这些数据结构,代码看起来像这样:
from collections import defaultdict
from csv import DictReader, DictWriter
def aggregate(infile, outfile, p_key, c_key):
"""Group 'infile' on 'p_key', combining additional keys with 'c_key'."""
data = defaultdict(dict)
headers = set()
with open(infile) as f:
for row in DictReader(f):
p_value = row.pop(p_key)
c_value = row.pop(c_key)
for key, value in row.items():
header = "_".join([c_value, key])
headers.add(header)
data[p_value][header] = value
field_names = [p_key] + sorted(headers)
with open(outfile, "w") as f:
writer = DictWriter(f, field_names)
writer.writeheader()
for p_value, row in data.items():
row[p_key] = p_value
writer.writerow(row)
使用示例:
>>> aggregate("in.csv", "out.csv", "Country", "Age")
产生的 out.csv 文件:
Country,65andOver_All,65andOver_F,65andOver_M,Under65_All,Under65_F,Under65_M
France,13741,7554,6187,31587,15301,16286
UK,12345,6000,6345,30987,15987,15000
Germany,14747,7555,7192,32646,17004,15642