Chcę zamienić komórkę pandy zawierającą listę w wiersze dla każdej z tych wartości.
Więc weź to:
Jeśli chciałbym rozpakować i ułożyć wartości w nearest_neighbors
kolumnie, tak aby każda wartość była wierszem w każdym opponent
indeksie, jak najlepiej się do tego zabrać? Czy istnieją metody pandy przeznaczone do takich operacji?
pd.DataFrame(df.nearest_neighbors.values.tolist())
do rozpakowania tej kolumny, a następniepd.merge
skleić ją z innymi.values.tolist()
że coś tutaj robi; kolumna jest już listąOdpowiedzi:
W poniższym kodzie najpierw zresetowałem indeks, aby ułatwić iterację wiersza.
Tworzę listę list, w których każdy element listy zewnętrznej jest wierszem docelowej ramki DataFrame, a każdy element listy wewnętrznej jest jedną z kolumn. Ta zagnieżdżona lista zostanie ostatecznie połączona w celu utworzenia żądanej ramki DataFrame.
Używam
lambda
funkcji wraz z iteracją listy, aby utworzyć wiersz dla każdego elementunearest_neighbors
sparowanego z odpowiednimname
iopponent
.Na koniec tworzę nowy DataFrame z tej listy (używając oryginalnych nazw kolumn i ustawiając indeks z powrotem na
name
iopponent
).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
EDYCJA CZERWIEC 2017
Alternatywna metoda jest następująca:
>>> (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() )
źródło
apply(pd.Series)
sprawdza się w najmniejszych ramkach, ale w przypadku ramek o rozsądnych rozmiarach należy ponownie rozważyć bardziej wydajne rozwiązanie. Zobacz Kiedy powinienem używać pandy Apply () w moim kodzie? (Lepszym rozwiązaniem jest najpierw wymienienie kolumny).explode()
metody. Dodałem odpowiedź z przykładem używając tej samej konfiguracji df jak tutaj.explode()
metody: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.explode('nearest_neighbors')
Na zewnątrz:
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
źródło
df = df.explode(...)
Użyj
apply(pd.Series)
istack
, a następniereset_index
ito_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
Detale
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]
źródło
df.nearest_neighbors.apply(pd.Series)
jest dla mnie bardzo zdumiewający;Myślę, że to naprawdę dobre pytanie, w Hive byś użył
EXPLODE
, myślę, że jest argument, że Pandy powinny domyślnie zawierać tę funkcję. Prawdopodobnie rozbiłbym kolumnę listy za pomocą zagnieżdżonego rozumienia generatora w następujący sposób:pd.DataFrame({ "name": i[0], "opponent": i[1], "nearest_neighbor": neighbour } for i, row in df.iterrows() for neighbour in row.nearest_neighbors ).set_index(["name", "opponent"])
źródło
Najszybszy sposób, że do tej pory znaleziono rozszerza DataFrame z
.iloc
i przypisanie powrotem spłaszczony kolumny docelowej.Biorąc pod uwagę zwykłe dane wejściowe (trochę replikowane):
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] ...
Biorąc pod uwagę następujące sugerowane alternatywy:
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 = [i for i,c in enumerate(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))
Uważam, że
extend_iloc()
jest to najszybsze :%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)
źródło
cols = [c for c in df.columns if c != col_target]
powinno być:cols = [i for i,c in enumerate(df.columns) if c != col_target]
Thedf.iloc[ilocations, cols].copy()
błędy, jeśli nie przedstawiane z indeksu kolumny.Ładniejsze rozwiązanie alternatywne z aplikacją (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)
źródło
Podobna do funkcji WYBUCHANIA Hive:
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
źródło
NameError: global name 'copy' is not defined
Więc wszystkie te odpowiedzi są dobre, ale chciałem czegoś ^ naprawdę prostego ^ więc oto mój wkład:
def explode(series): return pd.Series([x for _list in series for x in _list])
To wszystko… po prostu użyj tego, gdy chcesz nową serię, w której listy są „eksplodowane”. Oto przykład, w którym wykonujemy value_counts () na wybór taco :)
In [1]: my_df = pd.DataFrame(pd.Series([['a','b','c'],['b','c'],['c']]), columns=['tacos']) In [2]: my_df.head() Out[2]: tacos 0 [a, b, c] 1 [b, c] 2 [c] In [3]: explode(my_df['tacos']).value_counts() Out[3]: c 3 b 2 a 1
źródło
Oto potencjalna optymalizacja dla większych ramek danych. Działa to szybciej, gdy w polu „eksplodującym” jest kilka równych wartości. (Im większa ramka danych jest porównywana z liczbą unikatowych wartości w polu, tym lepszy będzie ten kod).
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
źródło
Rozszerzanie
.iloc
odpowiedzi Olega, aby automatycznie spłaszczyć wszystkie kolumny list:def extend_iloc(df): cols_to_flatten = [colname for colname in df.columns if isinstance(df.iloc[0][colname], list)] # Row numbers to repeat lens = df[cols_to_flatten[0]].apply(len) vals = range(df.shape[0]) ilocations = np.repeat(vals, lens) # Replicate rows and add flattened column of lists with_idxs = [(i, c) for (i, c) in enumerate(df.columns) if c not in cols_to_flatten] col_idxs = list(zip(*with_idxs)[0]) new_df = df.iloc[ilocations, col_idxs].copy() # Flatten columns of lists for col_target in cols_to_flatten: col_flat = [item for sublist in df[col_target] for item in sublist] new_df[col_target] = col_flat return new_df
Zakłada się, że każda kolumna listy ma taką samą długość listy.
źródło
Zamiast używać apply (pd.Series), możesz spłaszczyć kolumnę. Poprawia to wydajność.
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'])) %timeit (pd.DataFrame(df['nearest_neighbors'].values.tolist(), index = df.index) .stack() .reset_index(level = 2, drop=True).to_frame('nearest_neighbors')) 1.87 ms ± 9.74 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) %timeit (df.nearest_neighbors.apply(pd.Series) .stack() .reset_index(level=2, drop=True) .to_frame('nearest_neighbors')) 2.73 ms ± 16.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
źródło