【问题标题】:convert arbitrarily-many columns into key-value pairs using python/pandas使用 python/pandas 将任意多列转换为键值对
【发布时间】:2020-08-07 09:46:10
【问题描述】:

我正在尝试将具有 r 行和 c 列的非常宽的 csv 文件转换为具有 r*c 行和三列形式 row_id、col_name、col_value 的 dict 或数据帧。由于列数非常大(超过 10,000 列),因此无法手动完成。

例如,我从 pandas 数据框开始:

import pandas as pd

df = pd.DataFrame({'id': {0: '1',  1: '2',  2: '3'},
 'c1': {0: 'S', 1: 'S', 2: 'D'},
 'c2': {0: 'XX',  1: 'WX',  2: 'WX'},
 'c3': {0: '32',  1: '63',  2: '32'}})

df = df.set_index('id')

看起来像这样:

    id  c1  c2  c3
0   1   S   XX  32
1   2   S   WX  63
2   3   D   WX  32

请记住,此示例数据框只有三列,但解决方案需要处理大量列。

目标是将其转换为如下所示的字典或数据框:

    id  key     value
0   1   c1  S
1   1   c2  XX
2   1   c3  32
3   2   c1  S
4   2   c2  WX
5   2   c3  63
6   3   c1  D
7   3   c2  WX
8   3   c3  32

我已经编写了一些实现所需输出的东西,方法是按列和行从数据帧迭代到一个新的数据帧:

data = []

for i, row in df.iterrows():
    for j, column in row.iteritems():
        a_dictionary = i, j, column
        data.append(a_dictionary)

df_out = pd.DataFrame(data)
df_out.columns = ['id', 'key', 'value']

但我读过一篇文章,应该避免在 pandas 和 python 中使用 for 循环。那么合适的解决方案应该是什么样的呢?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    您是否考虑过使用pd.melt

    import pandas as pd
    df = pd.DataFrame({'id': {0: '1',  1: '2',  2: '3'},
     'c1': {0: 'S', 1: 'S', 2: 'D'},
     'c2': {0: 'XX',  1: 'WX',  2: 'WX'},
     'c3': {0: '32',  1: '63',  2: '32'}})
    
    out = pd.melt(df,
                  id_vars=['id'],
                  value_vars=df.columns[1:])
    
      id variable value
    0  1       c1     S
    1  2       c1     S
    2  3       c1     D
    3  1       c2    XX
    4  2       c2    WX
    5  3       c2    WX
    6  1       c3    32
    7  2       c3    63
    8  3       c3    32
    

    【讨论】:

    • 这很好用而且速度很快。但是,我在结果中看到的行数比我预期的要少——实际上是 448,与源 df 中的行数相同。我想知道为什么....
    • 只是为了了解您是否仅获得 5,589,248-448 行?您是否可以复制一个小示例,以便我检查一下?或者,如果另一个答案给您正确的答案,您可以尝试检查 2 个输出之间的差异。
    • 我还没有发现差异,但我认为这是我的用户错误。如果我发现它最终看起来很有用,我会在这里更新。再次感谢!
    • 您介意为pd.meltpd.stack 两种方法添加时间。只是出于好奇。
    • 当然!我对pd.iterrows()pd.iteritems() 的笨拙方式:43 秒; pd.stack():4 秒; pd.melt():0.9 秒
    【解决方案2】:

    你可以这样做:

    In [212]: df.stack(dropna=False)\
                .reset_index(name='Value')\
                .rename(columns={'level_1': 'key'})                                                                                                                            
    Out[212]: 
      id key Value
    0  1  c1     S
    1  1  c2    XX
    2  1  c3    32
    3  2  c1     S
    4  2  c2    WX
    5  2  c3    63
    6  3  c1     D
    7  3  c2    WX
    8  3  c3    32
    

    【讨论】:

    • 感谢 Mayank!但我有一个担忧:在我的实际 df 中,我有 448 行和 12,476 列,所以我应该在输出中看到 5,589,248 行。但是使用你的方法我只看到 1,225,347。我在输入 df 中有 NaN 值。 stack() 是否删除了具有 NaN 值的记录?
    • 是的,确实如此。让我更新我的答案以避免这种情况。
    • 感谢 Mayank。添加 dropna=False 不会保留缺少值的结果行,但在这种情况下这种行为很好,因为每条记录只有一个“值”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-22
    • 1970-01-01
    • 2021-10-02
    • 2019-06-05
    • 1970-01-01
    • 2017-10-25
    相关资源
    最近更新 更多