2015-09-08 5 views
32

Ich suche nach einer Pandabaumzelle, die eine Liste enthält, in Zeilen für jeden dieser Werte.Wie man eine Liste in einer Datareframe-Zelle in separate Zeilen auflöst

Also, nehmen Sie dies:

enter image description here

Wenn ich die Werte im auspacken möchten, und stapeln Gegner ‚index‚nearest_neighbors“Spalte, so dass jeder Wert eine Zeile innerhalb jeder sein würde‘, wie würde ich mich am besten über diese? gibt es Pandas Methoden, die für Operationen wie das gemeint? ich bin einfach nicht bewusst.

Vielen Dank im Voraus, Jungs.

+0

Können Sie ein Beispiel für Ihre gewünschte Ausgabe geben, und wha Hast du es bisher versucht? Am einfachsten können andere Ihnen helfen, wenn Sie einige Beispieldaten bereitstellen, die ebenfalls ausgeschnitten werden können. – dagrha

+0

Sie können 'pd.DataFrame (df.nearest_neighbors.values.tolist())' verwenden, um diese Spalte zu entpacken und dann 'pd.merge', um sie mit den anderen zu kleben. – hellpanderrr

+0

@helpanderr ich denke nicht, dass 'values.tolist()' hier irgendwas macht; die Spalte ist bereits eine Liste – maxymoo

Antwort

28

Im folgenden Code habe ich zuerst den Index zurückgesetzt, um die Zeileniteration zu vereinfachen.

Ich erstelle eine Liste von Listen, wobei jedes Element der äußeren Liste eine Zeile des Zieldatenrahmens ist und jedes Element der inneren Liste eine der Spalten ist. Diese verschachtelte Liste wird letztendlich verkettet, um den gewünschten Datenrahmen zu erstellen.

verwende ich eine lambda-Funktion zusammen mit der Liste Iteration eine Zeile für jedes Element der nearest_neighbors mit der entsprechenden name und opponent gepaart zu schaffen.

Schließlich erstelle ich einen neuen Datenrahmen aus dieser Liste (unter Verwendung der ursprünglichen Spaltennamen und setze den Index zurück auf name und opponent).

df = (pd.DataFrame({'name': ['A.J. Price'] * 3, 
        'opponent': ['76ers', 'blazers', 'bobcats'], 
        'nearest_neighbors': [['Zach LaVine', 'Jeremy Lin', 'Nate Robinson', 'Isaia']] * 3}) 
     .set_index(['name', 'opponent'])) 

>>> df 
                nearest_neighbors 
name  opponent             
A.J. Price 76ers  [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      blazers [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      bobcats [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 

df.reset_index(inplace=True) 
rows = [] 
_ = df.apply(lambda row: [rows.append([row['name'], row['opponent'], nn]) 
         for nn in row.nearest_neighbors], axis=1) 
df_new = pd.DataFrame(rows, columns=df.columns).set_index(['name', 'opponent']) 

>>> df_new 
        nearest_neighbors 
name  opponent     
A.J. Price 76ers   Zach LaVine 
      76ers   Jeremy Lin 
      76ers  Nate Robinson 
      76ers    Isaia 
      blazers  Zach LaVine 
      blazers   Jeremy Lin 
      blazers  Nate Robinson 
      blazers    Isaia 
      bobcats  Zach LaVine 
      bobcats   Jeremy Lin 
      bobcats  Nate Robinson 
      bobcats    Isaia 

EDIT Juni 2017

Eine alternative Methode ist wie folgt:

>>> (pd.melt(df.nearest_neighbors.apply(pd.Series).reset_index(), 
      id_vars=['name', 'opponent'], 
      value_name='nearest_neighbors') 
    .set_index(['name', 'opponent']) 
    .drop('variable', axis=1) 
    .dropna() 
    .sort_index() 
    ) 
9

ich denke, das eine wirklich gute Frage, in Hive würden Sieverwenden, ich denke, es gibt einen Fall zu machen, dass Pandas diese Funktionalität standardmäßig enthalten sollte. Sie können Ihre Liste Spalte wie diese explodieren:

import numpy as np 

df = pd.DataFrame({'listcol':[[1,2,3],[4,5,6]]}) 
X = pd.concat([pd.DataFrame(v, index=np.repeat(k,len(v))) 
      for k,v in df.listcol.to_dict().items()])  

Dann können Sie pd.merge verwenden diese zurück zu Ihrem ursprünglichen Datenrahmen zu verbinden, wie @helpanderr im Kommentar zu Ihrer ursprünglichen Frage vorgeschlagen.

7

Netteres alternative Lösung mit Übernehmen (pd.Series):

df = pd.DataFrame({'listcol':[[1,2,3],[4,5,6]]}) 

# expand df.listcol into its own dataframe 
tags = df['listcol'].apply(pd.Series) 

# rename each variable is listcol 
tags = tags.rename(columns = lambda x : 'listcol_' + str(x)) 

# join the tags dataframe back to the original dataframe 
df = pd.concat([df[:], tags[:]], axis=1) 
+0

Dieser erweitert Spalten nicht Zeilen. – Oleg

3

Ähnlich Hives EXPLODE-Funktionalität:

import copy 

def pandas_explode(df, column_to_explode): 
    """ 
    Similar to Hive's EXPLODE function, take a column with iterable elements, and flatten the iterable to one element 
    per observation in the output table 

    :param df: A dataframe to explod 
    :type df: pandas.DataFrame 
    :param column_to_explode: 
    :type column_to_explode: str 
    :return: An exploded data frame 
    :rtype: pandas.DataFrame 
    """ 

    # Create a list of new observations 
    new_observations = list() 

    # Iterate through existing observations 
    for row in df.to_dict(orient='records'): 

     # Take out the exploding iterable 
     explode_values = row[column_to_explode] 
     del row[column_to_explode] 

     # Create a new observation for every entry in the exploding iterable & add all of the other columns 
     for explode_value in explode_values: 

      # Deep copy existing observation 
      new_observation = copy.deepcopy(row) 

      # Add one (newly flattened) value from exploding iterable 
      new_observation[column_to_explode] = explode_value 

      # Add to the list of new observations 
      new_observations.append(new_observation) 

    # Create a DataFrame 
    return_df = pandas.DataFrame(new_observations) 

    # Return 
    return return_df 
+1

Wenn ich das ausführe, erhalte ich den folgenden Fehler: 'NameError: globaler Name 'Kopie' ist nicht definiert ' – frmsaul

6

Verwenden apply(pd.Series) und stack, dann reset_index und to_frame

In [1803]: (df.nearest_neighbors.apply(pd.Series) 
       .stack() 
       .reset_index(level=2, drop=True) 
       .to_frame('nearest_neighbors')) 
Out[1803]: 
        nearest_neighbors 
name  opponent 
A.J. Price 76ers   Zach LaVine 
      76ers   Jeremy Lin 
      76ers  Nate Robinson 
      76ers    Isaia 
      blazers  Zach LaVine 
      blazers   Jeremy Lin 
      blazers  Nate Robinson 
      blazers    Isaia 
      bobcats  Zach LaVine 
      bobcats   Jeremy Lin 
      bobcats  Nate Robinson 
      bobcats    Isaia 

Einzelheiten

In [1804]: df 
Out[1804]: 
                nearest_neighbors 
name  opponent 
A.J. Price 76ers  [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      blazers [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      bobcats [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
+0

Liebe die Eleganz Ihrer Lösung! Haben Sie es zufällig mit anderen Ansätzen verglichen? – rpyzh

0

Hier ist eine mögliche Optimierung für größere Datenrahmen. Dies läuft schneller, wenn im Feld "Exploding" mehrere gleiche Werte vorhanden sind. (Je größer der Datenrahmen mit der Anzahl der eindeutigen Werte im Feld verglichen wird, desto besser wird dieser Code funktionieren.

)
def lateral_explode(dataframe, fieldname): 
    temp_fieldname = fieldname + '_made_tuple_' 
    dataframe[temp_fieldname] = dataframe[fieldname].apply(tuple)  
    list_of_dataframes = [] 
    for values in dataframe[temp_fieldname].unique().tolist(): 
     list_of_dataframes.append(pd.DataFrame({ 
      temp_fieldname: [values] * len(values), 
      fieldname: list(values), 
     })) 
    dataframe = dataframe[list(set(dataframe.columns) - set([fieldname]))]\ 
     .merge(pd.concat(list_of_dataframes), how='left', on=temp_fieldname) 
    del dataframe[temp_fieldname] 

    return dataframe 
2

Die schnellsten Methode I bisher gefunden wird, um den Datenrahmen mit .iloc erstreckt, und Zuweisen der zurück Zielspalte abgeflacht.

die übliche Eingabe Given (ein wenig repliziert):

df = (pd.DataFrame({'name': ['A.J. Price'] * 3, 
        'opponent': ['76ers', 'blazers', 'bobcats'], 
        'nearest_neighbors': [['Zach LaVine', 'Jeremy Lin', 'Nate Robinson', 'Isaia']] * 3}) 
     .set_index(['name', 'opponent'])) 
df = pd.concat([df]*10) 

df 
Out[3]: 
                nearest_neighbors 
name  opponent             
A.J. Price 76ers  [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      blazers [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      bobcats [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      76ers  [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
      blazers [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia] 
... 

Angesichts der folgenden vorgeschlagenen Alternativen:

col_target = 'nearest_neighbors' 

def extend_iloc(): 
    # Flatten columns of lists 
    col_flat = [item for sublist in df[col_target] for item in sublist] 
    # Row numbers to repeat 
    lens = df[col_target].apply(len) 
    vals = range(df.shape[0]) 
    ilocations = np.repeat(vals, lens) 
    # Replicate rows and add flattened column of lists 
    cols = [c for c in df.columns if c != col_target] 
    new_df = df.iloc[ilocations, cols].copy() 
    new_df[col_target] = col_flat 
    return new_df 

def melt(): 
    return (pd.melt(df[col_target].apply(pd.Series).reset_index(), 
      id_vars=['name', 'opponent'], 
      value_name=col_target) 
      .set_index(['name', 'opponent']) 
      .drop('variable', axis=1) 
      .dropna() 
      .sort_index()) 

def stack_unstack(): 
    return (df[col_target].apply(pd.Series) 
      .stack() 
      .reset_index(level=2, drop=True) 
      .to_frame(col_target)) 

Ich finde, dass extend_iloc() dieschnellste ist:

%timeit extend_iloc() 
3.11 ms ± 544 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

%timeit melt() 
22.5 ms ± 1.25 ms per loop (mean ± std. dev. of 7 runs, 100 loops each) 

%timeit stack_unstack() 
11.5 ms ± 410 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 
+0

nette Bewertung – javadba

Verwandte Themen