熊猫从列中可用的列表数据中扩展行
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 完美!是的,我知道一定有什么。 resample
与 pad
或 bfill
看起来是完成此任务的好方法。谢谢!【参考方案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
,然后通过stack
和join
创建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 方式中,如果列包含空列表,则此操作将失败。完美。以上是关于熊猫从列中可用的列表数据中扩展行的主要内容,如果未能解决你的问题,请参考以下文章