Pandas将列中可用的列表数据展开为行

63

我在 Pandas 中有一个类似于这样的数据框:

 column1      column2
 [a,b,c]        1
 [d,e,f]        2
 [g,h,i]        3

期望输出:

column1      column2
  a              1
  b              1
  c              1
  d              2
  e              2
  f              2
  g              3
  h              3
  i              3

如何处理这些数据?


2
print(type(df.ix[0, 'column1'])) 是什么? - jezrael
打印(type(df.ix[0, 'column1'])):---是列表 - Sanjay Yadav
3个回答

75

DataFrame.explode

pandas >= 0.25.0 版本以来,我们可以使用 explode 方法实现此功能,该方法将列表中的每个元素扩展为一行,并重复其余列:

df.explode('column1').reset_index(drop=True)

输出


  column1  column2
0       a        1
1       b        1
2       c        1
3       d        2
4       e        2
5       f        2
6       g        3
7       h        3
8       i        3

pandas >= 1.1.0 版本开始,我们有了 ignore_index 参数,因此不必再与 reset_index 命令连锁使用:

df.explode('column1', ignore_index=True)

输出

  column1  column2
0       a        1
1       b        1
2       c        1
3       d        2
4       e        2
5       f        2
6       g        3
7       h        3
8       i        3

如果你使用的是 pandas < 0.25.0 版本,我已经制作了一个补丁以使其运行。请前往以下链接查看: https://gist.github.com/BurakaKrishna/538cdad998247b95f9b2898015360a8e - Shiva Rama Krishna
我看到你的代码中使用了很多for循环,我不建议人们采用这种方法。在此处有更好的向量化替代方案,适用于pandas < 0.25.0版本。@ShivaRamaKrishna - Erfan
有没有一种不使用列表作为索引的好方法?例如,假设我有两个数据框,一个带有秒精度的时间戳,另一个只有分钟精度。我想通过将所有值重复60次来扩展具有分钟精度的那个,以便我可以合并它们。我猜我可以在每个数据框中创建一个长度为60的列表作为新索引,并使用explode方法来完成此操作,但想知道是否有更多pandas的方法来完成这个任务。 - topher217
1
这看起来像是从分钟到秒的重采样问题,而不是 @topher217 的 explode 问题。 - Erfan
1
@Erfan 太完美了!是的,我知道一定有什么方法。使用resamplepadbfill似乎是完成这个任务的好方法。谢谢! - topher217

23

您可以通过构造函数和stack方法创建DataFrame

 df2 = pd.DataFrame(df.column1.tolist(), index=df.column2)
        .stack()
        .reset_index(level=1, drop=True)
        .reset_index(name='column1')[['column1','column2']]
print (df2)

  column1  column2
0       a        1
1       b        1
2       c        1
3       d        2
4       e        2
5       f        2
6       g        3
7       h        3
8       i        3

如果需要按子集 [['column1', 'column2']] 进行排序,您也可以省略第一个 reset_index

df2 = pd.DataFrame(df.column1.tolist(), index=df.column2)
        .stack()
        .reset_index(name='column1')[['column1','column2']]
print (df2)
  column1  column2
0       a        1
1       b        1
2       c        1
3       d        2
4       e        2
5       f        2
6       g        3
7       h        3
8       i        3

使用DataFrame.from_records可以从第一列创建DataFrame,然后通过stackjoin将其转换为Series,并将其与原始DataFrame连接以得到另一个解决方案:

df = pd.DataFrame({'column1': [['a','b','c'],['d','e','f'],['g','h','i']],
                   'column2':[1,2,3]})


a = pd.DataFrame.from_records(df.column1.tolist())
                .stack()
                .reset_index(level=1, drop=True)
                .rename('column1')

print (a)
0    a
0    b
0    c
1    d
1    e
1    f
2    g
2    h
2    i
Name: column1, dtype: object

print (df.drop('column1', axis=1)
         .join(a)
         .reset_index(drop=True)[['column1','column2']])

  column1  column2
0       a        1
1       b        1
2       c        1
3       d        2
4       e        2
5       f        2
6       g        3
7       h        3
8       i        3

典型的pandas风格,如果列为空列表,则会失败。完美。 - Henry Henrinson

8
另一种解决方法是使用自pandas 0.23版本起可用的pandas.apply函数的result_type='expand'参数。回答@splinter的问题,这种方法可以被推广--请参见下文:
import pandas as pd
from numpy import arange

df = pd.DataFrame(
    {'column1' : [['a','b','c'],['d','e','f'],['g','h','i']],
    'column2': [1,2,3]}
)

pd.melt(
    df.join(
        df.apply(lambda row: row['column1'], axis=1, result_type='expand')
        ),
 value_vars=arange(df['column1'].shape[0]), value_name='column1', var_name='column2')[['column1','column2']]

# can be generalized 

df = pd.DataFrame(
    {'column1' : [['a','b','c'],['d','e','f'],['g','h','i']],
    'column2': [1,2,3],
    'column3': [[1,2],[2,3],[3,4]],
    'column4': [42,23,321],
    'column5': ['a','b','c']}
)

(pd.melt(
    df.join(
        df.apply(lambda row: row['column1'], axis=1, result_type='expand')
        ),
 value_vars=arange(df['column1'].shape[0]), value_name='column1', id_vars=df.columns[1:])
 .drop(columns=['variable'])[list(df.columns[:1]) + list(df.columns[1:])]
 .sort_values(by=['column1']))

更新(针对Jwely的评论): 如果您有长度不同的列表,可以使用以下方法:

df = pd.DataFrame(
    {'column1' : [['a','b','c'],['d','f'],['g','h','i']],
    'column2': [1,2,3]}
)

longest = max(df['column1'].apply(lambda x: len(x)))

pd.melt(
    df.join(
        df.apply(lambda row: row['column1'] if len(row['column1']) >= longest else row['column1'] + [None] * (longest - len(row['column1'])), axis=1, result_type='expand')
    ),
 value_vars=arange(df['column1'].shape[0]), value_name='column1', var_name='column2').query("column1 == column1")[['column1','column2']]

我认为这个解决方案需要确保“column1”中的每个列表长度相同,在此情况下为3。 - Jwely
1
我认为问题是关于第一列具有相同长度的列表,但是通过轻微修改,您可以处理不同长度的列表--请参见我的编辑。 - bencekd

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