我有一个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)

当前回答

这里有很多答案,但我很惊讶没有人提到内置的熊猫爆炸功能。看看下面的链接: 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。

其他回答

这是一个相当直接的消息,它使用pandas str访问器的split方法,然后使用NumPy将每一行平铺成一个数组。

通过使用np.repeat以正确的次数重复未分割的列来检索相应的值。

var1 = df.var1.str.split(',', expand=True).values.ravel()
var2 = np.repeat(df.var2.values, len(var1) / len(df))

pd.DataFrame({'var1': var1,
              'var2': var2})

  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

这里有很多答案,但我很惊讶没有人提到内置的熊猫爆炸功能。看看下面的链接: 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。

在最近的熊猫版本中,你可以使用分裂和爆炸

a.assign(var1=a['var1'].str.split(',')).explode('var1')

a

   var1 var2
0   a   1
0   b   1
0   c   1
1   d   2
1   e   2
1   f   2

这样怎么样:

In [55]: pd.concat([Series(row['var2'], row['var1'].split(','))              
                    for _, row in a.iterrows()]).reset_index()
Out[55]: 
  index  0
0     a  1
1     b  1
2     c  1
3     d  2
4     e  2
5     f  2

然后你只需要重命名列

博士TL;

import pandas as pd
import numpy as np

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

示范

explode_str(a, 'var1', ',')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

让我们创建一个包含列表的新数据框架d

d = a.assign(var1=lambda d: d.var1.str.split(','))

explode_list(d, 'var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

一般的评论

我用np。使用repeat来生成我可以用iloc使用的数据框架索引位置。

FAQ

为什么不用loc?

因为索引可能不是唯一的,使用loc将返回与查询索引匹配的每一行。

为什么不用values属性切片呢?

当调用值时,如果整个数据框架在一个内聚的“块”中,Pandas将返回该“块”数组的视图。否则熊猫将不得不拼凑一个新的阵列。补码时,该数组必须是统一的dtype。这通常意味着返回一个dtype为object的数组。通过使用iloc而不是对values属性进行切片,我减轻了不得不处理这个问题的负担。

为什么使用赋值?

当我使用与爆炸相同的列名进行赋值时,我将覆盖现有的列并保持其在数据框架中的位置。

为什么索引值重复?

通过在重复位置上使用iloc,得到的索引显示相同的重复模式。对列表或字符串中的每个元素重复一次。 这可以用reset_index(drop=True)重置


为字符串

我不想过早地把弦分开。因此,我计算sep参数的出现次数,假设我要分割,结果列表的长度将比分隔符的数量多1。

然后我使用sep来连接字符串,然后分裂。

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

为列表

与字符串类似,只是我不需要计算sep的出现次数,因为它已经分裂了。

我使用Numpy的concatenate将列表阻塞在一起。

import pandas as pd
import numpy as np

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})