【问题标题】:Aggregate CSV file with python使用 python 聚合 CSV 文件
【发布时间】:2014-07-01 20:45:22
【问题描述】:

我的交叉表 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 个唯一年龄分组和 z 个列变量的文件。最终文件将包含一个标题行,其中包含 y*z+1 列,并且低于此 x 行数。

希望这是有道理的,任何帮助/指针将不胜感激。

【问题讨论】:

  • 这是怎么转置的?它只是一个 groupby 聚合...

标签: python csv python-3.x


【解决方案1】:

我将提出一个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")

【讨论】:

    【解决方案2】:

    解决这个问题的最明显方法是分两个阶段:

    1. 从输入的 CSV 文件中将数据收集到一个方便的数据结构中。
    2. 使用收集的数据写入输出 CSV 文件。

    在我看来,最方便使用的数据结构是dict 对象的defaultdict,使用主键(在本例中为“Country”)作为顶级键,并将组合键(在本例中为“Age”)和附加键作为二级字典的键:

    {
        '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
    

    【讨论】:

      猜你喜欢
      • 2014-02-07
      • 2021-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-05
      • 2020-06-08
      • 2013-04-22
      相关资源
      最近更新 更多