【问题标题】:Pandas - read CSV into dataframe, where a column has a varying number of subcolumnsPandas - 将 CSV 读入数据框,其中一列具有不同数量的子列
【发布时间】:2017-07-31 21:29:45
【问题描述】:

在 Pandas 中,是否可以有一个包含不同数量子列的列的数据框?

例如,假设我有这个 CSV 文件:

transactionId, userName, date, itemList, totalCost

其中itemList 包含可变数量的itemId;itemPrice 对,这些对由管道分隔(|)。列表中itemId;itemPrice 对的数量没有上限。

itemId ; itemPrice | itemId ; itemPrice

以下是一些行示例:

transactionId, userName, date,       itemList,              totalCost
123,           Bob  ,    7/29/2017,  ABC;10|XYZ;20,         30
234,           Alice,    7/31/2017,  CDE;20|QRS;15|KLM;10,  45

第一行有两对itemId;itemPrice,而第二行有三对。

如何创建一个数据框来包含这些信息?我需要在数据框内添加一个数据框吗?

还有其他关于可变列数的 Stackoverflow 帖子,但 they assume a maximum number of columns

【问题讨论】:

  • 您可以考虑将它们存储为字典吗?
  • 如果您希望每个单元格有一个单独的值(而不是字典),您应该决定是使表格“宽”(每个额外的值多出一列)还是“高”(多出一行)每个额外的价值)。答案会因您的选择而异。
  • @DYZ:我想我需要它们。例如,我的目标之一是审核itemPrice 值的总和是否等于整行的totalCost

标签: python pandas dataframe


【解决方案1】:

我会尝试标准化您的数据as proposed by @DYZ in comments

In [145]: df = df.join(df.pop('itemList')
     ...:                .str.extractall(r'(?P<item>\w+);(?P<price>\d+)')
     ...:                .reset_index(level=1, drop=True))
     ...:

In [146]: df
Out[146]:
   transactionId userName       date  totalCost item price
0            123      Bob  7/29/2017         30  ABC    10
0            123      Bob  7/29/2017         30  XYZ    20
1            234    Alice  7/31/2017         45  CDE    20
1            234    Alice  7/31/2017         45  QRS    15
1            234    Alice  7/31/2017         45  KLM    10

标准化数据允许我们应用 Pandas/Numpy/SciPy/等。 ufunctions 直接作用于包含标量值的列。

演示:检查totalCost

df.price = pd.to_numeric(df.price, errors='coerce')

In [151]: df.assign(tot2=df.groupby(level=0).price.transform('sum'))
Out[151]:
   transactionId userName       date  totalCost item  price  tot2
0            123      Bob  7/29/2017         30  ABC     10    30
0            123      Bob  7/29/2017         30  XYZ     20    30
1            234    Alice  7/31/2017         45  CDE     20    45
1            234    Alice  7/31/2017         45  QRS     15    45
1            234    Alice  7/31/2017         45  KLM     10    45

In [152]: df.assign(tot2=df.groupby(level=0).price.transform('sum')).query("totalCost != tot2")
Out[152]:
Empty DataFrame
Columns: [transactionId, userName, date, totalCost, item, price, tot2]
Index: []

PS last empty DF 表明我们没有任何条目包含totalCost != sum(price)

【讨论】:

    【解决方案2】:

    你用列表理解来解析它们

    d1 = df.assign(
        itemList=[[x.split(';') for x in y.split('|')] for y in df.itemList.tolist()]
    )
    
    d1
    
       transactionId userName       date                           itemList  totalCost
    0            123      Bob  7/29/2017             [[ABC, 10], [XYZ, 20]]         30
    1            234    Alice  7/31/2017  [[CDE, 20], [QRS, 15], [KLM, 10]]         45
    

    对评论的回应

    f = lambda x: np.array(x)[:, 1].astype(int).sum()
    
    d1.assign(sumPrice=d1.itemList.apply(f))
    
       transactionId userName       date                           itemList  totalCost  sumPrice
    0            123      Bob  7/29/2017             [[ABC, 10], [XYZ, 20]]         30        30
    1            234    Alice  7/31/2017  [[CDE, 20], [QRS, 15], [KLM, 10]]         45        45
    

    【讨论】:

    • 这是一个有趣的表示。我创建了这个数据框,dataframe.info()itemList 列的类型是Object。我将如何遍历 itemList 中的项目以总结 itemPrice 值?
    • 我可以做到,但我们已经在没有手电筒的情况下走上了一条黑暗的道路……你确定要继续吗?我会坚持@MaxU 的回答中的高数据框。
    • 如果可以的话,很多读者都会喜欢它,包括我自己。我试图了解所涉及的权衡,例如空间与编码复杂度。
    • @stackoverflowuser2010 添加了示例。
    • 谢谢。非常感谢。
    猜你喜欢
    • 2021-01-22
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 2019-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多