Python(Pandas?):具有可变列宽的CSV文件中的聚合数据

时间:2017-12-19 04:14:17

标签: python pandas csv dictionary aggregate

我有一个可变列宽的csv文件,其中每一行都是日期,区域和在该日期/区域观察到的一组ID。

数据如下所示:

12/01/2017,Region1,BMW_123,TESLA_332,TESLA_2002
11/07/2017,Region2,TESLA_332,BMW_123,TESLA_2002,TESLA_99812
11/19/2017,Region2,BMW_123,TESLA_31
10/23/2017,Region1,BMW_4,TESLA_3,TESLA_90
11/02/2017,Region2,TESLA_28,BMW_56,TESLA_22,TESLA_821
10/14/2017,Region2,BMW_1,BMW_8,BMW_2,TESLA_3,TESLA_4,TESLA_99,TESLA_81,TESLA_56

我想:

(1)拆分所有ID并保留品牌,因此“TESLA_12345”将成为“TESLA”。这部分没问题。

(2)按月份区域汇总,以便我们知道每个区域元组的每个品牌(例如特斯拉或宝马)的失败次数。

输出应该看起来像这样(格式不是非常重要 - 它必须清晰易读):

month region BMW TESLA
10 Region1   1     2
12 Region1   1     2
10 Region2   3     5
11 Region2   3     7

这样做的一种自然方式似乎是逐行读取CSV并更新嵌套字典中每个品牌的计数(即,有一个区域字典包含2个区域,每个区域包含保留的月份序列像{宝马:2,TESLA:1}那样的计数。然而,我正在努力更新嵌套的dicts,我想知道是否有一个更简单的解决方案(或者如果Pandas可以毫不费力地做到这一点等)。

(注意:显然,月份可以从日期中提取:

datetime.strptime(mydate, "%m/%d/%Y").strftime("%m")

1 个答案:

答案 0 :(得分:4)

这是一种方法。我不会称之为漂亮,但它完成了工作。

  1. 第一个问题是每行中的字段数不同。您可以一次在文件中读取一行,并将其存储在列表data中。您还可以在执行此操作时修剪汽车ID:

    import pandas as pd
    
    # assuming CSV is named test.csv
    f = open("test.csv", "r")
    
    data = []
    for i, line in enumerate(f.readlines()):
        splitted = line.split(",")
        just_brand = [x.split("_")[0] for x in splitted]
        data.append(just_brand)
    
  2. 现在我们已将文件读入Python数据结构,我们可以重新排序文件行,使得具有最多字段数的条目位于顶部。这对Pandas来说很有用,因为它可以比额外的列更好地处理丢失的列。如果我们从最大数量的列开始,将更好地处理更短的后续行。

    df = pd.DataFrame(sorted(data, key=lambda row: len(row), reverse=True))
    
    df
                0        1      2      3      4      5      6      7      8      9
    0  10/14/2017  Region2    BMW    BMW    BMW  TESLA  TESLA  TESLA  TESLA  TESLA
    1  11/07/2017  Region2  TESLA    BMW  TESLA  TESLA   None   None   None   None
    2  11/02/2017  Region2  TESLA    BMW  TESLA  TESLA   None   None   None   None
    3  12/01/2017  Region1    BMW  TESLA  TESLA   None   None   None   None   None
    4  10/23/2017  Region1    BMW  TESLA  TESLA   None   None   None   None   None
    5  11/19/2017  Region2    BMW  TESLA   None   None   None   None   None   None
    
  3. 从这里开始,只需要对事物进行命名,组织和格式化。

    df = (df.set_index([0,1])
            .stack()
            .reset_index(level=1)
            .rename(columns={1:"region",0:"make"})
            .reset_index(level=1, drop=True))
    
    df = (df.groupby([pd.to_datetime(df.index).month,"region","make"])
            .make.count()
            .unstack()
            .reset_index()
            .rename(columns={0:"month"}))
    df.columns.name = ""
    
    df
        region  BMW  TESLA  month
    0  Region1    1      2     10
    1  Region2    3      5     10
    2  Region2    3      7     11
    3  Region1    1      2     12
    
  4. (替代,不那么体操)

    # get TESLA, BMW counts for each row
    cts = df.iloc[:,2:].apply(lambda x: x.value_counts(), axis=1)
    # merge with date, region
    df2 = pd.concat([df.iloc[:, :2], cts], axis=1)
    # groupby and sum
    (df2.groupby([pd.to_datetime(df[0]).dt.month,1])
        .sum()
        .reset_index()
        .rename(columns={0:"month",1:"region"}))