熊猫从列中可用的列表数据中扩展行

Posted

技术标签:

【中文标题】熊猫从列中可用的列表数据中扩展行【英文标题】:Pandas expand rows from list data available in column 【发布时间】:2016-12-24 23:45:14 【问题描述】:

我在 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

如何处理这些数据?

【问题讨论】:

print (type(df.ix[0, 'column1']) 是什么? print (type(df.ix[0, 'column1']) :--- 是列表 【参考方案1】:

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 gist.github.com/BurakaKrishna/538cdad998247b95f9b2898015360a8e下面运行 我看到你使用了很多 for loops,我不建议人们使用这种方法,here 是 pandas < 0.25.0 @ShivaRamaKrishna 更好的矢量化替代品 在没有列表作为索引的情况下,有没有一种好方法可以做到这一点?例如,假设我必须使用时间戳以秒精度为一个日期帧,而另一个只有分钟精度。我想通过将所有值复制 60 次来以微小的精度扩展它,以便我可以合并它们。我想我可以创建一个新索引,每个索引的长度为 60,然后执行这个 explode 方法,但想知道是否有更多的 pandas 方式来执行此操作。 这看起来像是resample 从一分钟到一秒的问题,而不是自爆@topher217。 @Erfan 完美!是的,我知道一定有什么。 resamplepadbfill 看起来是完成此任务的好方法。谢谢!【参考方案2】:

另一种解决方案是使用自 pandas 0.23 起可用的 pandas.apply 函数的 result_type='expand' 参数。回复@splinter's question这个方法可以泛化——见下:

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。 我认为问题是关于第一列中长度相同的列表,但稍作修改,您可以做不同的列表长度 - 请参阅我的编辑【参考方案3】:

您可以通过其构造函数和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

【讨论】:

我喜欢你的第二个解决方案 @Kris - 谢谢,这更好,所以现在是第一个。 如果我们有多个column2,同样的方法会起作用吗?也就是说,如果我们有很多列同时保持column1 就像问题一样? @splinter:查看我对 pandas 0.23 方法的回答,我已经包含了泛化 ***.com/a/53570916/5356704 在典型的 pandas 方式中,如果列包含空列表,则此操作将失败。完美。

以上是关于熊猫从列中可用的列表数据中扩展行的主要内容,如果未能解决你的问题,请参考以下文章

根据 Pandas 中另一列中的索引从列中获取数据

如何比较熊猫数据框列中可用的十进制数?

从列A数据框A到数据框B中的C的匹配值,并使用熊猫从数据框A创建不匹配的列表

Sqlite3 从列中具有相似值的行获取数据

如何检查熊猫数据框行在各个列中是不是具有某些值,可扩展性

从列中的字符串中提取每行的最大值