如何将Dataframe单元格内的列表分解为单独的行
我正在寻找将每个包含一个列表的pandas细胞变成行。
所以,拿这个:
如果我想在“nearest_neighbors”列中解压和堆叠值,以使每个值都成为每个“对手”索引中的一行,那么我最好怎么去做呢?是否有pandas的方法,这个我只是不知道
在此先感谢,伙计们。
在下面的代码中,我首先重置索引,使行迭代更容易。
我创build了一个列表,其中外部列表的每个元素都是目标DataFrame的一行,而内部列表的每个元素都是其中一列。 这个嵌套列表最终将被连接起来以创build所需的DataFrame。
我使用lambda
函数与列表迭代一起为与相关name
和opponent
配对的nearest_neighbors
每个元素创build一行。
最后,我从这个列表中创build一个新的DataFrame(使用原始列名称并将索引设置为name
和opponent
)。
df = (pd.DataFrame({'name': ['AJ 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 AJ 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 AJ 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年6月
另一种方法如下:
>>> (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() )
我认为这是一个非常好的问题,在Hive中你会使用EXPLODE
,我认为有一个情况是pandas应该在默认情况下包含这个function。 你可以像这样爆炸你的列表列:
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()])
然后,您可以使用pd.merge
将其添加回您的原始数据框,就像在原始问题的注释中build议的@helpanderr一样。
更好的替代解决scheme(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)
类似于Hive的EXPLODEfunction:
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
使用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 AJ 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 AJ 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]
这是对较大数据框的潜在优化。 当“exploding”字段中有几个相等的值时,这会运行得更快。 (dataframe与字段中的唯一值计数相比越大,代码就越好。)
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