我有一个pandas数据框架,其中一列文本字符串包含逗号分隔的值。我想拆分每个CSV字段,并为每个条目创建一个新行(假设CSV是干净的,只需要在','上拆分)。例如,a应该变成b:

In [7]: a
Out[7]: 
    var1  var2
0  a,b,c     1
1  d,e,f     2

In [8]: b
Out[8]: 
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

到目前为止,我已经尝试了各种简单的函数,但是.apply方法在轴上使用时似乎只接受一行作为返回值,而且我不能让.transform工作。任何建议都将不胜感激!

示例数据:

from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
               {'var1': 'b', 'var2': 1},
               {'var1': 'c', 'var2': 1},
               {'var1': 'd', 'var2': 2},
               {'var1': 'e', 'var2': 2},
               {'var1': 'f', 'var2': 2}])

我知道这不会起作用,因为我们通过numpy丢失了DataFrame元数据,但它应该给你一个我试图做的感觉:

def fun(row):
    letters = row['var1']
    letters = letters.split(',')
    out = np.array([row] * len(letters))
    out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)

当前回答

类似的问题:pandas:如何将一列中的文本拆分为多行?

你可以这样做:

>> a=pd.DataFrame({"var1":"a,b,c d,e,f".split(),"var2":[1,2]})
>> s = a.var1.str.split(",").apply(pd.Series, 1).stack()
>> s.index = s.index.droplevel(-1)
>> del a['var1']
>> a.join(s)
   var2 var1
0     1    a
0     1    b
0     1    c
1     2    d
1     2    e
1     2    f

其他回答

这里有很多答案,但我很惊讶没有人提到内置的熊猫爆炸功能。看看下面的链接: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html#pandas.DataFrame.explode

由于某种原因,我无法访问该函数,所以我使用下面的代码:

import pandas_explode
pandas_explode.patch()
df_zlp_people_cnt3 = df_zlp_people_cnt2.explode('people')

以上是我的数据样本。如你所见,人物栏有一系列人物,我试图把它炸开。我给出的代码适用于列表类型数据。因此,请尝试将逗号分隔的文本数据转换为列表格式。此外,由于我的代码使用内置函数,它比自定义/应用函数快得多。

注意:你可能需要用pip安装pandas_explosion。

我提出了一个具有任意列数的数据框架的解决方案(同时一次仍然只分离一列的条目)。

def splitDataFrameList(df,target_column,separator):
    ''' df = dataframe to split,
    target_column = the column containing the values to split
    separator = the symbol used to perform the split

    returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
    The values in the other columns are duplicated across the newly divided rows.
    '''
    def splitListToRows(row,row_accumulator,target_column,separator):
        split_row = row[target_column].split(separator)
        for s in split_row:
            new_row = row.to_dict()
            new_row[target_column] = s
            row_accumulator.append(new_row)
    new_rows = []
    df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
    new_df = pandas.DataFrame(new_rows)
    return new_df

对于这个问题,我提出了以下解决方案:

def iter_var1(d):
    for _, row in d.iterrows():
        for v in row["var1"].split(","):
            yield (v, row["var2"])

new_a = DataFrame.from_records([i for i in iter_var1(a)],
        columns=["var1", "var2"])

我一直在与内存不足的经验作斗争,使用各种方法来爆炸我的列表,所以我准备了一些基准来帮助我决定哪些答案应该点赞。我测试了列表长度与列表数量的不同比例的五种场景。分享以下结果:

时间:(越少越好,点击查看大版)

内存使用峰值:(越少越好)

结论:

@MaxU的回答(更新2),code dename concatenate在几乎所有情况下都提供了最好的速度,同时保持peek内存使用低, 如果你需要用相对较小的列表处理大量的行,并且可以负担得起增加的峰值内存,请参阅@DMulligan的答案(代码堆栈), 接受的@Chang的答案适用于有几行但非常大的列表的数据帧。

所有细节(函数和基准测试代码)都在GitHub要点中。请注意,基准测试问题被简化了,不包括将字符串拆分到列表中——大多数解决方案都以类似的方式执行。

经过痛苦的实验,我找到了比公认的答案更快的方法,我让这个方法起作用了。它在我试用的数据集上运行速度快了大约100倍。

如果有人知道如何使其更优雅,请务必修改我的代码。我找不到一种方法,不设置其他你想保留的列作为下标,然后重设下标,重命名列,但我想还有其他方法可以。

b = DataFrame(a.var1.str.split(',').tolist(), index=a.var2).stack()
b = b.reset_index()[[0, 'var2']] # var1 variable is currently labeled 0
b.columns = ['var1', 'var2'] # renaming var1