【问题标题】:Python (Pandas?): Aggregate Data in CSV File with Variable Column WidthsPython(Pandas?):在具有可变列宽的 CSV 文件中聚合数据
【发布时间】:2018-06-01 11:21:32
【问题描述】:

我有一个列宽可变的 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 个区域的区域字典,每个区域都包含保持{BMW:2, TESLA: 1} 这样的计数。但是,我正在努力更新嵌套的字典,我想知道是否有更简单的解决方案(或者 Pandas 是否可以毫不费力地做到这一点,等等)

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

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

)

【问题讨论】:

标签: python pandas csv dictionary aggregate


【解决方案1】:

这是一种方法。我不会说它漂亮,但它可以完成工作。

  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"}))
    

【讨论】:

  • 如果你创建两个 int 列,“TESLA”和“BMW”,并存储每个列在每一行中出现的次数不是更好吗?这使得进一步处理变得更加容易。
  • Qusai Alothman,我根据您的建议添加了一个解决方案。谢谢!
猜你喜欢
  • 2014-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
  • 2021-05-27
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多