拆分(爆炸)熊猫数据帧字符串条目以分离行

我有一个pandas dataframe,其中一列文本字符串包含逗号分隔的值。我想拆分每个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)
351460 次浏览

这样怎么样:

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

然后你只需要重命名列

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

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

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"])

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

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

类似的问题: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

只是从上面使用了jiln的优秀答案,但需要展开以拆分多个列。我想分享一下。

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_columns, separator):
split_rows = []
for target_column in target_columns:
split_rows.append(row[target_column].split(separator))
# Seperate for multiple columns
for i in range(len(split_rows[0])):
new_row = row.to_dict()
for j in range(len(split_rows)):
new_row[target_columns[j]] = split_rows[j][i]
row_accumulator.append(new_row)
new_rows = []
df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
new_df = pd.DataFrame(new_rows)
return new_df

下面是这个常见任务的我写的函数。它比Series/stack方法更有效。列的顺序和名称被保留。

def tidy_split(df, column, sep='|', keep=False):
"""
Split the values of a column and expand so the new DataFrame has one split
value per row. Filters rows where the column is missing.


Params
------
df : pandas.DataFrame
dataframe with the column to split and expand
column : str
the column to split and expand
sep : str
the string used to split the column's values
keep : bool
whether to retain the presplit value as it's own row


Returns
-------
pandas.DataFrame
Returns a dataframe with the same columns as `df`.
"""
indexes = list()
new_values = list()
df = df.dropna(subset=[column])
for i, presplit in enumerate(df[column].astype(str)):
values = presplit.split(sep)
if keep and len(values) > 1:
indexes.append(i)
new_values.append(presplit)
for value in values:
indexes.append(i)
new_values.append(value)
new_df = df.iloc[indexes, :].copy()
new_df[column] = new_values
return new_df

使用这个函数,最初的问题就像下面这样简单:

tidy_split(a, 'var1', sep=',')

更新3:使用__ABC0 / DataFrame.explode()方法(在Pandas 0.25.0中实现,并在Pandas 1.3.0中扩展以支持多列爆炸)更有意义,如使用示例所示:

对于单个列:

In [1]: df = pd.DataFrame({'A': [[0, 1, 2], 'foo', [], [3, 4]],
...:                    'B': 1,
...:                    'C': [['a', 'b', 'c'], np.nan, [], ['d', 'e']]})


In [2]: df
Out[2]:
A  B          C
0  [0, 1, 2]  1  [a, b, c]
1        foo  1        NaN
2         []  1         []
3     [3, 4]  1     [d, e]


In [3]: df.explode('A')
Out[3]:
A  B          C
0    0  1  [a, b, c]
0    1  1  [a, b, c]
0    2  1  [a, b, c]
1  foo  1        NaN
2  NaN  1         []
3    3  1     [d, e]
3    4  1     [d, e]

对于多列(熊猫1.3.0+):

In [4]: df.explode(['A', 'C'])
Out[4]:
A  B    C
0    0  1    a
0    1  1    b
0    2  1    c
1  foo  1  NaN
2  NaN  1  NaN
3    3  1    d
3    4  1    e

更新2:更通用的向量化函数,它将适用于多个normal和多个list

def explode(df, lst_cols, fill_value='', preserve_index=False):
# make sure `lst_cols` is list-alike
if (lst_cols is not None
and len(lst_cols) > 0
and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
lst_cols = [lst_cols]
# all columns except `lst_cols`
idx_cols = df.columns.difference(lst_cols)
# calculate lengths of lists
lens = df[lst_cols[0]].str.len()
# preserve original index values
idx = np.repeat(df.index.values, lens)
# create "exploded" DF
res = (pd.DataFrame({
col:np.repeat(df[col].values, lens)
for col in idx_cols},
index=idx)
.assign(**{col:np.concatenate(df.loc[lens>0, col].values)
for col in lst_cols}))
# append those rows that have empty lists
if (lens == 0).any():
# at least one list in cells is empty
res = (res.append(df.loc[lens==0, idx_cols], sort=False)
.fillna(fill_value))
# revert the original index order
res = res.sort_index()
# reset index if requested
if not preserve_index:
res = res.reset_index(drop=True)
return res

演示:

多个list列——所有list列在每行中必须有相同的元素#:

In [134]: df
Out[134]:
aaa  myid        num          text
0   10     1  [1, 2, 3]  [aa, bb, cc]
1   11     2         []            []
2   12     3     [1, 2]      [cc, dd]
3   13     4         []            []


In [135]: explode(df, ['num','text'], fill_value='')
Out[135]:
aaa  myid num text
0   10     1   1   aa
1   10     1   2   bb
2   10     1   3   cc
3   11     2
4   12     3   1   cc
5   12     3   2   dd
6   13     4

保留原始索引值:

In [136]: explode(df, ['num','text'], fill_value='', preserve_index=True)
Out[136]:
aaa  myid num text
0   10     1   1   aa
0   10     1   2   bb
0   10     1   3   cc
1   11     2
2   12     3   1   cc
2   12     3   2   dd
3   13     4

设置:

df = pd.DataFrame({
'aaa': {0: 10, 1: 11, 2: 12, 3: 13},
'myid': {0: 1, 1: 2, 2: 3, 3: 4},
'num': {0: [1, 2, 3], 1: [], 2: [1, 2], 3: []},
'text': {0: ['aa', 'bb', 'cc'], 1: [], 2: ['cc', 'dd'], 3: []}
})

CSV专栏:

In [46]: df
Out[46]:
var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ


In [47]: explode(df.assign(var1=df.var1.str.split(',')), 'var1')
Out[47]:
var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

使用这个小技巧,我们可以将类csv列转换为list列:

In [48]: df.assign(var1=df.var1.str.split(','))
Out[48]:
var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

更新: 通用向量化方法(也适用于多列):

原始DF:

In [177]: df
Out[177]:
var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

解决方案:

首先让我们把CSV字符串转换成列表:

In [178]: lst_col = 'var1'


In [179]: x = df.assign(**{lst_col:df[lst_col].str.split(',')})


In [180]: x
Out[180]:
var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

现在我们可以这样做:

In [181]: pd.DataFrame({
...:     col:np.repeat(x[col].values, x[lst_col].str.len())
...:     for col in x.columns.difference([lst_col])
...: }).assign(**{lst_col:np.concatenate(x[lst_col].values)})[x.columns.tolist()]
...:
Out[181]:
var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

旧的回答:

@AFinkelstein解决方案的启发,我想让它更一般化一点,它可以应用到有两列以上的DF,并且和AFinkelstein的解决方案一样快):

In [2]: df = pd.DataFrame(
...:    [{'var1': 'a,b,c', 'var2': 1, 'var3': 'XX'},
...:     {'var1': 'd,e,f,x,y', 'var2': 2, 'var3': 'ZZ'}]
...: )


In [3]: df
Out[3]:
var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ


In [4]: (df.set_index(df.columns.drop('var1',1).tolist())
...:    .var1.str.split(',', expand=True)
...:    .stack()
...:    .reset_index()
...:    .rename(columns={0:'var1'})
...:    .loc[:, df.columns]
...: )
Out[4]:
var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

另一种解决方案是使用python复制包

import copy
new_observations = list()
def pandas_explode(df, column_to_explode):
new_observations = list()
for row in df.to_dict(orient='records'):
explode_values = row[column_to_explode]
del row[column_to_explode]
if type(explode_values) is list or type(explode_values) is tuple:
for explode_value in explode_values:
new_observation = copy.deepcopy(row)
new_observation[column_to_explode] = explode_value
new_observations.append(new_observation)
else:
new_observation = copy.deepcopy(row)
new_observation[column_to_explode] = explode_values
new_observations.append(new_observation)
return_df = pd.DataFrame(new_observations)
return return_df


df = pandas_explode(df, column_name)

下面是一个相当直接的消息,它使用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

基于优秀的@DMulligan的解决方案,这里有一个通用的向量化(无循环)函数,它将数据帧的一列分割成多行,并将其合并回原始数据帧。它还使用了来自回答的一个很棒的泛型change_column_order函数。

def change_column_order(df, col_name, index):
cols = df.columns.tolist()
cols.remove(col_name)
cols.insert(index, col_name)
return df[cols]


def split_df(dataframe, col_name, sep):
orig_col_index = dataframe.columns.tolist().index(col_name)
orig_index_name = dataframe.index.name
orig_columns = dataframe.columns
dataframe = dataframe.reset_index()  # we need a natural 0-based index for proper merge
index_col_name = (set(dataframe.columns) - set(orig_columns)).pop()
df_split = pd.DataFrame(
pd.DataFrame(dataframe[col_name].str.split(sep).tolist())
.stack().reset_index(level=1, drop=1), columns=[col_name])
df = dataframe.drop(col_name, axis=1)
df = pd.merge(df, df_split, left_index=True, right_index=True, how='inner')
df = df.set_index(index_col_name)
df.index.name = orig_index_name
# merge adds the column to the last place, so we need to move it back
return change_column_order(df, col_name, orig_col_index)

例子:

df = pd.DataFrame([['a:b', 1, 4], ['c:d', 2, 5], ['e:f:g:h', 3, 6]],
columns=['Name', 'A', 'B'], index=[10, 12, 13])
df
Name    A   B
10   a:b     1   4
12   c:d     2   5
13   e:f:g:h 3   6


split_df(df, 'Name', ':')
Name    A   B
10   a       1   4
10   b       1   4
12   c       2   5
12   d       2   5
13   e       3   6
13   f       3   6
13   g       3   6
13   h       3   6

注意,它保留了列的原始索引和顺序。它也适用于具有非连续索引的数据框架。

字符串函数split可以接受一个选项布尔参数“expand”。

下面是使用这个论点的解决方案:

(a.var1
.str.split(",",expand=True)
.set_index(a.var2)
.stack()
.reset_index(level=1, drop=True)
.reset_index()
.rename(columns={0:"var1"}))

博士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.arangerepeat来生成可以与iloc一起使用的数据帧索引位置。

常见问题解答

为什么不使用loc?

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

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

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

为什么使用assign?

当我使用assign使用与我正在展开的相同的列名时,我将覆盖现有的列并保持其在数据框架中的位置。

为什么索引值重复?

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

来重置

为字符串

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

然后,我使用sepjoin字符串,然后split

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)})

有可能在不改变数据框架结构的情况下拆分和爆炸数据框架

拆分和展开特定列的数据

输入:

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






#Get the indexes which are repetative with the split
df['var1'] = df['var1'].str.split(',')
df = df.explode('var1')

:

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

Edit-1

对多列的行进行拆分和展开

Filename    RGB                                             RGB_type
0   A   [[0, 1650, 6, 39], [0, 1691, 1, 59], [50, 1402...   [r, g, b]
1   B   [[0, 1423, 16, 38], [0, 1445, 16, 46], [0, 141...   [r, g, b]

基于参考列重新索引,并将列值信息与堆栈对齐

df = df.reindex(df.index.repeat(df['RGB_type'].apply(len)))
df = df.groupby('Filename').apply(lambda x:x.apply(lambda y: pd.Series(y.iloc[0])))
df.reset_index(drop=True).ffill()

:

                Filename    RGB_type    Top 1 colour    Top 1 frequency Top 2 colour    Top 2 frequency
Filename
A  0       A   r   0   1650    6   39
1       A   g   0   1691    1   59
2       A   b   50  1402    49  187
B  0       B   r   0   1423    16  38
1       B   g   0   1445    16  46
2       B   b   0   1419    16  39

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

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

Speed

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

Peak memory usage

结论:

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

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

升级了MaxU的答案,支持MultiIndex

def explode(df, lst_cols, fill_value='', preserve_index=False):
"""
usage:
In [134]: df
Out[134]:
aaa  myid        num          text
0   10     1  [1, 2, 3]  [aa, bb, cc]
1   11     2         []            []
2   12     3     [1, 2]      [cc, dd]
3   13     4         []            []


In [135]: explode(df, ['num','text'], fill_value='')
Out[135]:
aaa  myid num text
0   10     1   1   aa
1   10     1   2   bb
2   10     1   3   cc
3   11     2
4   12     3   1   cc
5   12     3   2   dd
6   13     4
"""
# make sure `lst_cols` is list-alike
if (lst_cols is not None
and len(lst_cols) > 0
and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
lst_cols = [lst_cols]
# all columns except `lst_cols`
idx_cols = df.columns.difference(lst_cols)
# calculate lengths of lists
lens = df[lst_cols[0]].str.len()
# preserve original index values
idx = np.repeat(df.index.values, lens)
res = (pd.DataFrame({
col:np.repeat(df[col].values, lens)
for col in idx_cols},
index=idx)
.assign(**{col:np.concatenate(df.loc[lens>0, col].values)
for col in lst_cols}))
# append those rows that have empty lists
if (lens == 0).any():
# at least one list in cells is empty
res = (res.append(df.loc[lens==0, idx_cols], sort=False)
.fillna(fill_value))
# revert the original index order
res = res.sort_index()
# reset index if requested
if not preserve_index:
res = res.reset_index(drop=True)


# if original index is MultiIndex build the dataframe from the multiindex
# create "exploded" DF
if isinstance(df.index, pd.MultiIndex):
res = res.reindex(
index=pd.MultiIndex.from_tuples(
res.index,
names=['number', 'color']
)
)
return res

熊猫>= 0.25

Series和DataFrame方法定义了一个< >强.explode() < / >强方法,该方法将列表分解为单独的行。请参阅爆炸一个类似列表的列的文档部分。

因为你有一个由逗号分隔的字符串列表,用逗号分隔字符串以得到元素列表,然后在该列上调用explode

df = pd.DataFrame({'var1': ['a,b,c', 'd,e,f'], 'var2': [1, 2]})
df
var1  var2
0  a,b,c     1
1  d,e,f     2


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


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

注意,explode只适用于单个列(目前)。要同时爆炸多个列,请参见下面。

nan和空列表得到了他们应得的待遇,而不需要你跳圈来得到正确的。

df = pd.DataFrame({'var1': ['d,e,f', '', np.nan], 'var2': [1, 2, 3]})
df
var1  var2
0  d,e,f     1
1            2
2    NaN     3


df['var1'].str.split(',')


0    [d, e, f]
1           []
2          NaN


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


var1  var2
0    d     1
0    e     1
0    f     1
1          2  # empty list entry becomes empty string after exploding
2  NaN     3  # NaN left un-touched

与基于__ABC0/repeat的解决方案相比,这是一个很大的优势(完全忽略空列表,并阻塞nan)。


多列爆炸

熊猫1.3更新

df.explode工作在pandas 1.3开始的多个列上:

df = pd.DataFrame({'var1': ['a,b,c', 'd,e,f'],
'var2': ['i,j,k', 'l,m,n'],
'var3': [1, 2]})
df
var1   var2  var3
0  a,b,c  i,j,k     1
1  d,e,f  l,m,n     2


(df.set_index(['var3'])
.apply(lambda col: col.str.split(','))
.explode(['var1', 'var2'])
.reset_index()
.reindex(df.columns, axis=1))


var1 var2  var3
0    a    i     1
1    b    j     1
2    c    k     1
3    d    l     2
4    e    m     2
5    f    n     2

在旧版本中,你会将explode列移动到应用程序内部,这性能要差得多:

(df.set_index(['var3'])
.apply(lambda col: col.str.split(',').explode())
.reset_index()
.reindex(df.columns, axis=1))

其思想是将所有应该的列设置为索引,然后通过apply对其余列进行爆炸。当列表大小相等时,这种方法效果很好。

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

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

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

enter image description here

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

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

使用split(___, expand=True)levelname参数到reset_index()的一行代码:

>>> b = a.var1.str.split(',', expand=True).set_index(a.var2).stack().reset_index(level=0, name='var1')
>>> b
var2 var1
0     1    a
1     1    b
2     1    c
0     2    d
1     2    e
2     2    f

如果你需要b看起来和问题中的完全一样,你可以另外做:

>>> b = b.reset_index(drop=True)[['var1', 'var2']]
>>> b
var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

我有一个类似的问题,我的解决方案是将数据帧转换为字典列表,然后进行转换。函数如下:

import re
import pandas as pd


def separate_row(df, column_name):
ls = []
for row_dict in df.to_dict('records'):
for word in re.split(',', row_dict[column_name]):
row = row_dict.copy()
row[column_name]=word
ls.append(row)
return pd.DataFrame(ls)

例子:

>>> from pandas import DataFrame
>>> import numpy as np
>>> a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
{'var1': 'd,e,f', 'var2': 2}])
>>> a
var1  var2
0  a,b,c     1
1  d,e,f     2
>>> separate_row(a, "var1")
var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

您还可以稍微更改该函数以支持分离列表类型行。

我的版本的解决方案添加到这个集合!:-)

# Original problem
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}])
### My solution
import pandas as pd
import functools
def expand_on_cols(df, fuse_cols, delim=","):
def expand_on_col(df, fuse_col):
col_order = df.columns
df_expanded = pd.DataFrame(
df.set_index([x for x in df.columns if x != fuse_col])[fuse_col]
.apply(lambda x: x.split(delim))
.explode()
).reset_index()
return df_expanded[col_order]
all_expanded = functools.reduce(expand_on_col, fuse_cols, df)
return all_expanded


assert(b.equals(expand_on_cols(a, ["var1"], delim=",")))
在从本页上的所有解决方案中添加一些零碎的内容后,我能够得到这样的东西(对于需要立即使用它的人)。 函数的参数是df(输入数据帧)和key(用分隔符分隔字符串的列)。如果分隔符与分号";"不同,只需替换为分隔符。

def split_df_rows_for_semicolon_separated_key(key, df):
df=df.set_index(df.columns.drop(key,1).tolist())[key].str.split(';', expand=True).stack().reset_index().rename(columns={0:key}).loc[:, df.columns]
df=df[df[key] != '']
return df

我很欣赏“Chang She"”的回答,真的,但是iterrows()函数在大型数据集上需要很长时间。我面对了这个问题,然后我走到了这一步。

# First, reset_index to make the index a column
a = a.reset_index().rename(columns={'index':'duplicated_idx'})


# Get a longer series with exploded cells to rows
series = pd.DataFrame(a['var1'].str.split('/')
.tolist(), index=a.duplicated_idx).stack()


# New df from series and merge with the old one
b = series.reset_index([0, 'duplicated_idx'])
b = b.rename(columns={0:'var1'})


# Optional & Advanced: In case, there are other columns apart from var1 & var2
b.merge(
a[a.columns.difference(['var1'])],
on='duplicated_idx')


# Optional: Delete the "duplicated_index"'s column, and reorder columns
b = b[a.columns.difference(['duplicated_idx'])]

使用assignexplode的一行程序:

    col1  col2
0  a,b,c     1
1  d,e,f     2


df.assign(col1 = df.col1.str.split(',')).explode('col1', ignore_index=True)

输出:

  col1  col2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

试一试:

vals = np.array(a.var1.str.split(",").values.tolist())
var = np.repeat(a.var2, vals.shape[1])


out = pd.DataFrame(np.column_stack((var, vals.ravel())), columns=a.columns)
display(out)


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


在pandas的最新版本中,你可以使用split后跟explode

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

一个

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