2014-09-26 116 views
4

我有熊猫数据框,其中包含值或值列表(不等长度)的列。我想“展开”行,因此列表中的每个值都成为列中的单个值。一个例子说明了一切:遍历行并扩展熊猫数据帧

dfIn = pd.DataFrame({u'name': ['Tom', 'Jim', 'Claus'], 
u'location': ['Amsterdam', ['Berlin','Paris'], ['Antwerp','Barcelona','Pisa'] ]}) 

    location  name 
0 Amsterdam Tom 
1 [Berlin, Paris] Jim 
2 [Antwerp, Barcelona, Pisa] Claus 

我想变成:

dfOut = pd.DataFrame({u'name': ['Tom', 'Jim', 'Jim', 'Claus','Claus','Claus'], 
u'location': ['Amsterdam', 'Berlin','Paris', 'Antwerp','Barcelona','Pisa']}) 

    location  name 
0 Amsterdam Tom 
1 Berlin Jim 
2 Paris Jim 
3 Antwerp Claus 
4 Barcelona Claus 
5 Pisa Claus 

我第一次使用应用尝试,但它不可能为据我所知返回多个系列。看起来好像是诡计。但是,下面的代码给我一个空数据框...

def duplicator(series): 
    if type(series['location']) == list: 
     for location in series['location']: 
      subSeries = series 
      subSeries['location'] = location 
      dfOut.append(subSeries) 
    else: 
     dfOut.append(series) 

for index, row in dfIn.iterrows(): 
    duplicator(row) 

回答

4

如果返回了一系列其index是位置列表,然后将dfIn.apply整理这些串联成一个表:

import pandas as pd 
dfIn = pd.DataFrame({u'name': ['Tom', 'Jim', 'Claus'], 
        u'location': ['Amsterdam', ['Berlin','Paris'], 
            ['Antwerp','Barcelona','Pisa'] ]}) 

def expand(row): 
    locations = row['location'] if isinstance(row['location'], list) else [row['location']] 
    s = pd.Series(row['name'], index=list(set(locations))) 
    return s 

In [156]: dfIn.apply(expand, axis=1) 
Out[156]: 
    Amsterdam Antwerp Barcelona Berlin Paris Pisa 
0  Tom  NaN  NaN NaN NaN NaN 
1  NaN  NaN  NaN Jim Jim NaN 
2  NaN Claus  Claus NaN NaN Claus 

你然后可以堆叠此DataFrame以获得:

In [157]: dfIn.apply(expand, axis=1).stack() 
Out[157]: 
0 Amsterdam  Tom 
1 Berlin   Jim 
    Paris   Jim 
2 Antwerp  Claus 
    Barcelona Claus 
    Pisa   Claus 
dtype: object 

这是一个系列,而您需要一个DataFrame。与reset_index一个小按摩让你期望的结果:

dfOut = dfIn.apply(expand, axis=1).stack() 
dfOut = dfOut.to_frame().reset_index(level=1, drop=False) 
dfOut.columns = ['location', 'name'] 
dfOut.reset_index(drop=True, inplace=True) 
print(dfOut) 

产生

location name 
0 Amsterdam Tom 
1  Berlin Jim 
2  Paris Jim 
3 Amsterdam Claus 
4 Antwerp Claus 
5 Barcelona Claus 
+0

我得到一个错误,可能是因为奇怪的数据(这可能是由某些列表中的emtpy值引起的):InvalidIndexError:Reindexing只对有唯一值的索引对象有效 – bowlby 2014-09-27 09:45:21

+0

如果您的位置列表之一包含重复项目。您如何处理重复地点(针对同一个人)? – unutbu 2014-09-27 09:48:26

+0

重复值应该是'合并'(例如,只有1个应该保留在结果中),空值应该被忽略。 – bowlby 2014-09-27 11:24:30

5

还不如多有趣/花式大熊猫的用法,但这个工程:

import numpy as np 
dfIn.loc[:, 'location'] = dfIn.location.apply(np.atleast_1d) 
all_locations = np.hstack(dfIn.location) 
all_names = np.hstack([[n]*len(l) for n, l in dfIn[['name', 'location']].values]) 
dfOut = pd.DataFrame({'location':all_locations, 'name':all_names}) 

这是关于提高40倍比apply/stack/reindex方法要好。据我所知,这个比率几乎保留了所有的数据帧大小(没有测试它如何随着每行中列表的大小而变化)。如果您可以保证所有location条目都已经迭代,您可以删除atleast_1d呼叫,从而可以再次提高20%的速度。

+0

这个解决方案更加优雅。 – 2016-05-10 13:45:29