
本文详细介绍了如何利用pandas库高效地将dataframe中的多列数值数据聚合为以特定列为键、以数值列表为值的结构。通过避免低效的循环操作,本教程展示了如何结合使用`iloc`、`apply`和`concat`函数,将源dataframe的指定值列转换成一个包含列表的新列,并与原始键列进行合并,从而实现数据结构的优化,便于后续的数据处理和连接操作。
在数据分析和处理中,我们经常会遇到需要将DataFrame中分散在多列的数值数据整合到单个列表中,并与某个标识列(键)关联起来的场景。例如,一个DataFrame可能包含一个ID列、一个名称列以及多个表示不同测量或属性的数值列。为了方便后续的数据合并或简化数据结构,我们可能需要将这些数值列聚合成一个列表,并以名称列作为其唯一标识。
原始数据结构示例
假设我们有一个Pandas DataFrame,其结构如下:
id name value1 value2 value3 0 1 AAA 1.0 1.5 1.8 1 2 BBB 2.0 2.3 2.5 2 3 CCC 3.0 3.6 3.7
在这个DataFrame中,id和name是标识列,而value1、value2和value3是需要聚合的数值列。
目标数据结构
我们希望将上述DataFrame转换为以下形式,其中name作为键,而value列包含一个由原始value1、value2、value3组成的列表:
name value 0 AAA [1.0, 1.5, 1.8] 1 BBB [2.0, 2.3, 2.5] 2 CCC [3.0, 3.6, 3.7]
更理想的情况是保留原始的id列,同时将值列聚合:
id name value 0 1 AAA [1.0, 1.5, 1.8] 1 2 BBB [2.0, 2.3, 2.5] 3 3 CCC [3.0, 3.6, 3.7]
高效的Pandas解决方案
为了避免使用效率低下的循环语句,我们可以利用Pandas提供的强大向量化操作。核心思想是选择需要聚合的数值列,对每一行应用list函数,然后将生成的新列与原始的标识列合并。
以下是实现这一转换的python代码:
import pandas as pd # 示例DataFrame data = { 'id': [1, 2, 3], 'name': ['AAA', 'BBB', 'CCC'], 'value1': [1.0, 2.0, 3.0], 'value2': [1.5, 2.3, 3.6], 'value3': [1.8, 2.5, 3.7] } df = pd.DataFrame(data) print("原始DataFrame:") print(df) print("-" * 30) # 转换操作 # 1. 选择前两列(id和name)作为标识列 id_name_cols = df.iloc[:, :2] # 2. 选择从第三列开始的所有数值列 value_cols = df.iloc[:, 2:] # 3. 对数值列的每一行应用list函数,将其转换为一个列表 # axis=1 表示按行操作 # .rename('value') 将新生成的Series命名为'value' list_values_series = value_cols.apply(list, axis=1).rename('value') # 4. 使用pd.concat将标识列和新生成的列表列合并 transformed_df = pd.concat([id_name_cols, list_values_series], axis=1) print("转换后的DataFrame:") print(transformed_df)
代码输出:
原始DataFrame: id name value1 value2 value3 0 1 AAA 1.0 1.5 1.8 1 2 BBB 2.0 2.3 2.5 2 3 CCC 3.0 3.6 3.7 ------------------------------ 转换后的DataFrame: id name value 0 1 AAA [1.0, 1.5, 1.8] 1 2 BBB [2.0, 2.3, 2.5] 2 3 CCC [3.0, 3.6, 3.7]
代码解析
-
df.iloc[:, :2]: 这部分代码使用基于整数位置的索引(iloc)来选择DataFrame中的列。
- : 表示选择所有行。
- :2 表示选择从索引0开始到索引2之前(不包括2)的列,即第一列和第二列(在本例中是id和name)。
- 这会返回一个包含id和name列的新DataFrame。
-
df.iloc[:, 2:]: 同样使用iloc,这部分代码选择从索引2开始到末尾的所有列,即value1、value2和value3。这会返回一个包含所有数值列的新DataFrame。
-
.apply(list, axis=1):
- apply() 方法用于在DataFrame的行或列上应用一个函数。
- list 是Python内置的函数,它将一个可迭代对象转换为列表。
- axis=1 参数至关重要,它指示apply函数按行操作。这意味着list函数将应用于value_cols DataFrame的每一行,将该行中的所有元素收集到一个列表中。
- 此操作的结果是一个Pandas Series,其中每个元素都是一个列表。
-
.rename(‘value’): apply操作返回的Series默认没有一个有意义的名称。rename(‘value’)方法将其名称设置为’value’,这正是我们希望新列的名称。
-
pd.concat([id_name_cols, list_values_series], axis=1):
- pd.concat() 函数用于沿着特定轴连接Pandas对象。
- [id_name_cols, list_values_series] 是一个包含要连接的DataFrame和Series的列表。
- axis=1 参数指示concat函数按列连接。它会将id_name_cols DataFrame和list_values_series Series横向拼接起来,形成最终的DataFrame。
总结与注意事项
- 避免循环: 这种方法充分利用了Pandas的向量化操作,避免了显式的Python循环,从而在处理大型DataFrame时提供了显著的性能优势。
- 灵活性: iloc的强大之处在于可以根据列的整数位置灵活选择任意范围的列。如果列名是固定的,也可以使用df[[‘col1’, ‘col2’]]和df.drop([‘col1’, ‘col2’], axis=1)等方法。
- 数据类型: apply(list)会直接将原始列中的数据类型保留在列表中。如果原始列包含不同类型的数据,列表也将包含混合类型。
- 后续应用: 这种转换后的数据结构非常适合于后续的数据合并(例如,通过name列与其他DataFrame进行merge操作),或者在需要将一组相关值作为一个整体进行处理时。
通过掌握这种高效的数据重塑技巧,您将能够更有效地处理和准备Pandas DataFrame中的数据,为后续的分析工作打下坚实的基础。