跨行比较DataFrame同列值报错:Columns must be same length as key
DataFrame跨行对比同品类年度价格变化问题
问题说明
需要为DataFrame添加change列,规则是:每个水果当年价格较上年下降时填"Reduced",上升时填"Increased";原代码运行时报错Columns must be same length as key,且存在索引匹配隐患。
原代码如下:
df = pd.DataFrame({ 'Fruit': ['Apple', 'Orange', 'Grapes', 'Bananas', 'Apple', 'Orange', 'Grapes', 'Bananas'], 'year': [2015, 2015, 2015, 2015, 2016, 2016, 2016, 2016], 'price': [5, 7, 7, 9, 12, 9, 9, 4], 'stock': [11, 8, 10, 6, 6, 5, 9, 12]}) df['change'] = '' df['change'] = df.apply( lambda x: 'Reduced' if x['price'] < df.loc[ (df['Fruit'] == x['Fruit']) & (df['year'] == (x['year']-1)) ]['price'].any() else x['change'], axis=1)
错误原因
- 逻辑错误:
df.loc[...]['price'].any()仅判断是否存在上一年的价格数据,返回布尔值,而非具体的价格数值,无法和当前行价格做大小对比。 - 效率与匹配问题:
apply逐行处理效率极低,且如果数据排序混乱,会出现索引不匹配的问题;同时逐行返回的结果可能因逻辑错误导致长度不匹配,触发Columns must be same length as key报错。
正确解决方案
使用groupby+shift实现分组跨行取数,这是Pandas处理同类分组跨行对比的标准高效方法:
方法1:结合shift与numpy.where
import pandas as pd import numpy as np df = pd.DataFrame({ 'Fruit': ['Apple', 'Orange', 'Grapes', 'Bananas', 'Apple', 'Orange', 'Grapes', 'Bananas'], 'year': [2015, 2015, 2015, 2015, 2016, 2016, 2016, 2016], 'price': [5, 7, 7, 9, 12, 9, 9, 4], 'stock': [11, 8, 10, 6, 6, 5, 9, 12] }) # 按水果分组,获取上一年的价格(shift(1)将组内上一行数据下移) df['prev_price'] = df.groupby('Fruit')['price'].shift(1) # 对比当前价格与上一年价格,生成change列 df['change'] = np.where( df['price'] < df['prev_price'], 'Reduced', np.where( df['price'] > df['prev_price'], 'Increased', 'No Change' ) ) # 为第一年(无上年数据)的行单独赋值 df.loc[df['year'] == 2015, 'change'] = 'No Previous Data' # 可选:删除辅助列prev_price # df = df.drop('prev_price', axis=1) print(df)
方法2:使用pd.cut生成标签
import pandas as pd df = pd.DataFrame({ 'Fruit': ['Apple', 'Orange', 'Grapes', 'Bananas', 'Apple', 'Orange', 'Grapes', 'Bananas'], 'year': [2015, 2015, 2015, 2015, 2016, 2016, 2016, 2016], 'price': [5, 7, 7, 9, 12, 9, 9, 4], 'stock': [11, 8, 10, 6, 6, 5, 9, 12] }) df['prev_price'] = df.groupby('Fruit')['price'].shift(1) # 用cut根据价格区间生成标签 df['change'] = pd.cut( df['price'], bins=[-float('inf'), df['prev_price'], float('inf')], labels=['Reduced', 'Increased'] ) # 填充无上年数据的行 df['change'] = df['change'].fillna('No Previous Data') print(df)
注意事项
如果数据中存在年份不连续、水果年份缺失的情况,需先对数据按Fruit和year排序,确保shift能正确获取上一年数据:
df = df.sort_values(['Fruit', 'year'])
内容的提问来源于stack exchange,提问作者Nitin
相关产品推荐
相关产品推荐

