Я хочу превратить ячейку pandas, содержащую список, в строки для каждого из этих значений.
Итак, возьмите это:
Если я хочу распаковать и сложить значения в nearest_neighbors
столбце, чтобы каждое значение было строкой в каждом opponent
индексе, как мне лучше всего это сделать? Существуют ли методы pandas, предназначенные для подобных операций?
pd.DataFrame(df.nearest_neighbors.values.tolist())
распаковать эту колонку, а затемpd.merge
склеить ее с другими.values.tolist()
, что здесь что-нибудь делает; столбец уже является спискомОтветы:
В приведенном ниже коде я сначала сбрасываю индекс, чтобы упростить итерацию строки.
Я создаю список списков, в котором каждый элемент внешнего списка является строкой целевого DataFrame, а каждый элемент внутреннего списка является одним из столбцов. Этот вложенный список в конечном итоге будет объединен для создания желаемого DataFrame.
Я использую
lambda
функцию вместе с итерацией списка, чтобы создать строку для каждого элемента вnearest_neighbors
паре с соответствующимиname
иopponent
.Наконец, я создаю новый DataFrame из этого списка (используя исходные имена столбцов и устанавливая индекс обратно на
name
и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
ИЗМЕНИТЬ ИЮНЬ 2017
Альтернативный метод выглядит следующим образом:
>>> (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() )
источник
apply(pd.Series)
подходит для самых маленьких рамок, но для рамок любого разумного размера вам следует пересмотреть более производительное решение. См. Когда я должен использовать pandas apply () в своем коде? (Лучшее решение - сначала прослушать столбец.)explode()
метода. Я добавил ответ с примером, используя ту же настройку df, что и здесь.explode()
метода: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')
Вне:
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
источник
df = df.explode(...)
Используйте
apply(pd.Series)
иstack
, затемreset_index
и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
Детали
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]
источник
df.nearest_neighbors.apply(pd.Series)
меня очень удивил;Я думаю, что это действительно хороший вопрос, в Hive вы могли бы использовать
EXPLODE
, я думаю, что есть основания полагать, что Pandas должен включать эту функцию по умолчанию. Я бы, вероятно, взорвал столбец списка с помощью понимания вложенного генератора следующим образом: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"])
источник
Самый быстрый метод, который я нашел до сих пор, - это расширение DataFrame с
.iloc
помощью сглаженного целевого столбца и его возвращение .Учитывая обычный ввод (немного воспроизведенный):
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] ...
Учитывая следующие предлагаемые альтернативы:
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))
Я считаю, что
extend_iloc()
это самый быстрый :%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)
источник
cols = [c for c in df.columns if c != col_target]
должно быть:cols = [i for i,c in enumerate(df.columns) if c != col_target]
Thedf.iloc[ilocations, cols].copy()
ошибка , если не представлен с индексом столбца.Более приятное альтернативное решение с помощью apply (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)
источник
Подобно функциональности EXPLODE 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
источник
NameError: global name 'copy' is not defined
Итак, все эти ответы хороши, но я хотел чего-то действительно простого ^ так что вот мой вклад:
def explode(series): return pd.Series([x for _list in series for x in _list])
Вот и все ... просто используйте это, когда вам нужна новая серия, в которой списки "разнесены". Вот пример, в котором мы делаем value_counts () для выбора тако :)
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
источник
Вот потенциальная оптимизация для больших фреймов данных. Это происходит быстрее, когда в поле «взрывающееся» несколько одинаковых значений. (Чем больше фрейм данных по сравнению с количеством уникальных значений в поле, тем лучше будет работать этот код.)
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
источник
Расширение
.iloc
ответа Олега для автоматического выравнивания всех столбцов списка: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
Это предполагает, что каждый столбец списка имеет одинаковую длину.
источник
Вместо использования apply (pd.Series) вы можете сгладить столбец. Это улучшает производительность.
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)
источник