在pandas数据框中将多个列拆分为行

11

我有一个pandas数据框,如下所示:

ticker    account      value         date
aa       assets       100,200       20121231, 20131231
bb       liabilities  50, 150       20141231, 20131231

我想将df['value']df['date']分开,使得数据框如下:

ticker    account      value         date
aa       assets       100           20121231
aa       assets       200           20131231 
bb       liabilities  50            20141231
bb       liabilities  150           20131231

非常感激任何帮助。


重复问题:https://dev59.com/tFcO5IYBdhLWcg3waw6o - Pygirl
这个回答解决了你的问题吗?在pandas DataFrame中高效地展开(拆分)多个列表列的方法 - Pygirl
5个回答

16

你可以首先通过split列,stack创建Series并通过strip去除空格:

s1 = df.value.str.split(',', expand=True).stack().str.strip().reset_index(level=1, drop=True)
s2 = df.date.str.split(',', expand=True).stack().str.strip().reset_index(level=1, drop=True)

然后将两个 Series 进行concat,并添加至 df1

df1 = pd.concat([s1,s2], axis=1, keys=['value','date'])

删除旧的列 valuedate 并使用 join

print (df.drop(['value','date'], axis=1).join(df1).reset_index(drop=True))
  ticker      account value      date
0     aa       assets   100  20121231
1     aa       assets   200  20131231
2     bb  liabilities    50  20141231
3     bb  liabilities   150  20131231

感谢jezrael和piRSquared的回答!jezrael,你的方法很有效。 - tan

9

我注意到这个问题很多。也就是说,如何将具有列表的列拆分为多行?我看到它被称为爆炸。以下是一些链接:

所以我写了一个能够实现它的函数。
def explode(df, columns):
    idx = np.repeat(df.index, df[columns[0]].str.len())
    a = df.T.reindex_axis(columns).values
    concat = np.concatenate([np.concatenate(a[i]) for i in range(a.shape[0])])
    p = pd.DataFrame(concat.reshape(a.shape[0], -1).T, idx, columns)
    return pd.concat([df.drop(columns, axis=1), p], axis=1).reset_index(drop=True)

但在使用之前,我们需要在一列中使用列表(或可迭代对象)。

设置

df = pd.DataFrame([['aa', 'assets',      '100,200', '20121231,20131231'],
                   ['bb', 'liabilities', '50,50',   '20141231,20131231']],
                  columns=['ticker', 'account', 'value', 'date'])

df

enter image description here

分割 valuedate 列:

df.value = df.value.str.split(',')
df.date = df.date.str.split(',')

df

enter image description here

现在我们可以分别或同时展开任一列。

解决方案

explode(df, ['value','date'])

enter image description here


计时

我从@jezrael的计时中删除了strip,因为我无法有效地将其添加到我的计时中。对于这个问题来说,这是必要的一步,因为OP在逗号后字符串中有空格。我旨在提供一种通用的方式来展开一个已经包含可迭代对象的列,我认为我已经做到了。

代码

def get_df(n=1):
    return pd.DataFrame([['aa', 'assets',      '100,200,200', '20121231,20131231,20131231'],
                         ['bb', 'liabilities', '50,50',   '20141231,20131231']] * n,
                        columns=['ticker', 'account', 'value', 'date'])

小的2行示例

enter image description here

中等规模200行示例

enter image description here

大型 2,000,000 行样本

enter image description here


我非常好奇时间问题 ;) iterritems 操作速度较慢,但另一方面有很多像 stackconcatjoin 这样的操作,所以可能是可以相提并论的。 - jezrael
我看到解决方案中的差异 - 我使用“strip”。你能把它添加到你的解决方案中,然后再次尝试定时吗?我认为你忘记了。 - jezrael
@jezrael 更新了。请注意我刚才下面写的 ###计时 - piRSquared

2

Pandas >= 0.25

df.value = df.value.str.split(',')
df.date = df.date.str.split(',')
df = df.explode('value').explode("date").reset_index(drop=True)

df:

    ticker  account      value  date
0   aa      assets       100    20121231
1   aa      assets       100    20131231
2   aa      assets       200    20121231
3   aa      assets       200    20131231
4   bb      liabilities  50     20141231
5   bb      liabilities  50     20131231
6   bb      liabilities  50     20141231
7   bb      liabilities  50     20131231

2

我根据之前的答案编写了 explode 函数。它可能对想要快速获取并使用该功能的任何人有用。

def explode(df, cols, split_on=','):
    """
    Explode dataframe on the given column, split on given delimeter
    """
    cols_sep = list(set(df.columns) - set(cols))
    df_cols = df[cols_sep]
    explode_len = df[cols[0]].str.split(split_on).map(len)
    repeat_list = []
    for r, e in zip(df_cols.as_matrix(), explode_len):
        repeat_list.extend([list(r)]*e)
    df_repeat = pd.DataFrame(repeat_list, columns=cols_sep)
    df_explode = pd.concat([df[col].str.split(split_on, expand=True).stack().str.strip().reset_index(drop=True)
                            for col in cols], axis=1)
    df_explode.columns = cols
    return pd.concat((df_repeat, df_explode), axis=1)

例如,@piRSquared 给出了以下示例:

df = pd.DataFrame([['aa', 'assets', '100,200', '20121231,20131231'],
                   ['bb', 'liabilities', '50,50', '20141231,20131231']],
                  columns=['ticker', 'account', 'value', 'date'])
explode(df, ['value', 'date'])

输出

+-----------+------+-----+--------+
|    account|ticker|value|    date|
+-----------+------+-----+--------+
|     assets|    aa|  100|20121231|
|     assets|    aa|  200|20131231|
|liabilities|    bb|   50|20141231|
|liabilities|    bb|   50|20131231|
+-----------+------+-----+--------+

0
因为我太新了,不允许我写评论,所以我写了一个“答案”。
@titipata你的答案非常好,但在我看来,你的代码中有一个小的“错误”,我自己找不到。
我使用这个问题的示例,并只更改了值。
df = pd.DataFrame([['title1', 'publisher1', '1.1,1.2', '1'],
               ['title2', 'publisher2', '2', '2.1,2.2']],
              columns=['titel', 'publisher', 'print', 'electronic'])

explode(df, ['print', 'electronic'])

    publisher   titel   print   electronic
0   publisher1  title1  1.1     1
1   publisher1  title1  1.2     2.1
2   publisher2  title2  2       2.2

正如您所看到的,在“电子”列中,应该在第1行中显示值“1”,而不是“2.1”。

因此,整个数据集都会发生变化。我希望有人能帮助我找到解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接