2014-09-26 5 views
4

У меня есть dataframe pandas со столбцом, содержащим значения или списки значений (неравной длины). Я хочу «развернуть» строки, поэтому каждое значение в списке становится единственным значением в столбце. Пример говорит, что это все:Итерация по строкам и расширение pandas dataframe

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 

Я первый попытался с помощью применить, но это не возможно, чтобы вернуться несколько серии, насколько я знаю. Кажется, это и есть трюк. Но ниже код дает мне пустой dataframe ...

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 

You затем может складывать этот 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: переиндексирование действует только с однозначно оцененными объектами индекса – bowlby

+0

Это может произойти, если один из ваших списков местоположений содержит повторяющиеся элементы. Как вы хотите, чтобы дублированные адреса (для одного и того же человека) обрабатывались? – unutbu

+0

повторяющиеся значения должны быть «объединены» (например, только один должен быть сохранен в результате), пустые значения должны игнорироваться. – bowlby

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

Это о 40x быстрее чем подход apply/stack/reindex. Насколько я могу судить, это соотношение выполняется почти во всех размерах данных (не проверяет, как он масштабируется с размером списков в каждой строке). Если вы можете гарантировать, что все записи location уже являются итерабельными, вы можете удалить вызов atleast_1d, который дает около 20% ускорения.

+0

Это решение более элегантно. –

Смежные вопросы