【问题标题】:Why fillna not working as expected for mode为什么fillna不能按预期模式工作
【发布时间】:2019-06-29 17:46:58
【问题描述】:

我正在研究一个汽车销售数据集,其中包含以下列:'car'、'price'、'body'、'mileage'、'engV'、'engType'、'registration'、'year'、'model'、 '驱动'

列 'drive' 和 'engType' 有 NaN 缺失值,我想根据 ['car', 'model'] 的分组来计算“drive”的模式,然后这个组落在哪里,我想根据这个 groupby 替换那里的 NaN 值

我已经尝试了这些方法:

  • 对于数值数据

    carsale['engV2'] = (carsale.groupby(['car','body','model']))['engV'].transform(lambda x: x.fillna(x.median() ))

    • 这工作正常,准确地填充/替换数据
  • 分类数据

    carsale['driveT'] = (carsale.groupby(['car','model']))['drive'].transform(lambda x: x.fillna(x.mode())) carsale['driveT'] = (carsale.groupby(['car','model']))['drive'].transform(lambda x: x.fillna(pd.Series.mode(x)))

两者都给出相同的结果


  • 这是完整的代码:

# carsale['price2'] = (carsale.groupby(['car','model','year']))['price'].transform(lambda x: x.fillna(x.median()))

# carsale['engV2'] = (carsale.groupby(['car','body','model']))['engV'].transform(lambda x: x.fillna(x.median()))
# carsale['mileage2'] = (carsale.groupby(['car','model','year']))['mileage'].transform(lambda x: x.fillna(x.median()))
# mode = carsale.filter(['car','drive']).mode()
# carsale[['test1','test2']] = carsale[['car','engType']].fillna(carsale.mode().iloc[0])

**carsale.groupby(['car', 'model'])['engType'].apply(pd.Series.mode)**

# carsale.apply()
# carsale
# carsale['engType2'] = carsale.groupby('car').engType.transform(lambda x: x.fillna(x.mode()))

**carsale['driveT'] = carsale.groupby(['car', 'model'])['drive'
        ].transform(lambda x: x.fillna(x.mode()))
carsale['driveT'] = carsale.groupby(['car', 'model'])['drive'
        ].transform(lambda x: x.fillna(pd.Series.mode(x)))**

# carsale[carsale.car == 'Mercedes-Benz'].sort_values(['body','engType','model','mileage']).tail(50)
# carsale[carsale.engV.isnull()]
# carsale.sort_values(['car','body','model'])

**carsale**

从上面这两种方法给出相同的结果,它只是在新列 driveT 中替换/添加值,就像我们在原始列“驱动器”中一样。就像如果我们在某些索引中有 NaN,那么它在 driveT 中也显示相同的 NaN,对于其他值也相同。

但是对于数值数据,如果我应用了中位数,它会添加/替换正确的值。

所以问题是它实际上不是基于 ['car', 'model'] 组计算模式,而是对 'drive' 中的单个值执行模式,但是如果您运行此命令

**carsale.groupby(['car','model'])['engType'].apply(pd.Series.mode)**

这是基于 groupby (car, model) 的正确计算模式

谁能帮忙解决这个问题?

【问题讨论】:

  • 我认为您对此感到困惑。您应该使用 groupby 操作的结果填充该列。您正在填充 groupby 操作内的列。所以你会想这样做carsale['drive_T'] = carsale['drive'].fillna(carsale.groupby(['car', 'm odel'])['drive'].transform(lambda x: x.mode()))
  • 谢谢,会查的。

标签: python python-3.x pandas jupyter-notebook pandas-groupby


【解决方案1】:

我的方法是:

  1. 使用 .groupby() 为每个 car/model 组合创建包含 drive 功能模式的查找数据框。
  2. drive 中的汽车/模型的值为空时,编写一个在此数据帧中查找模式并返回给定汽车/模型的方法。

然而,有两个特定于 OP 数据集的关键极端情况需要处理:

  • 当特定汽车/型号组合没有模式时(因为此组合的 drive 列中的所有条目都是 NaN)。
  • 当特定汽车品牌没有模式时。

以下是我遵循的步骤。如果我从问题中示例数据帧的前几行扩展的示例开始:

carsale = pd.DataFrame({'car': ['Ford', 'Mercedes-Benz', 'Mercedes-Benz', 'Mercedes-Benz', 'Mercedes-Benz', 'Nissan', 'Honda','Renault', 'Mercedes-Benz', 'Mercedes-Benz', 'Toyota', 'Toyota', 'Ferrari'],
                   'price': [15500.000, 20500.000, 35000.000, 17800.000, 33000.000, 16600.000, 6500.000, 10500.000, 21500.000, 21500.000, 1280.000, 2005.00, 300000.000],
                   'body': ['crossover', 'sedan', 'other', 'van', 'vagon', 'crossover', 'sedan', 'vagon', 'sedan', 'sedan', 'compact', 'compact', 'sport'],
                   'mileage': [68.0, 173.0, 135.0, 162.0, 91.0, 83.0, 199.0, 185.0, 146.0, 146.0, 200.0, 134, 123.0],
                   'engType': ['Gas', 'Gas', 'Petrol', 'Diesel', np.nan, 'Petrol', 'Petrol', 'Diesel', 'Gas', 'Gas', 'Hybrid', 'Gas', 'Gas'],
                   'registration':['yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes'],
                   'year': [2010, 2011, 2008, 2012, 2013, 2013, 2003, 2011, 2012, 2012, 2009, 2003, 1988],
                   'model': ['Kuga', 'E-Class', 'CL 550', 'B 180', 'E-Class', 'X-Trail', 'Accord', 'Megane', 'E-Class', 'E-Class', 'Prius', 'Corolla', 'Testarossa'],
                   'drive': ['full', 'rear', 'rear', 'front', np.nan, 'full', 'front', 'front', 'rear', np.nan, np.nan, 'front', np.nan],
                  })
carsale

    car               price  body       mileage   engType   registration  year  model       drive
0   Ford            15500.0  crossover     68.0   Gas       yes           2010  Kuga        full
1   Mercedes-Benz   20500.0  sedan        173.0   Gas       yes           2011  E-Class     rear
2   Mercedes-Benz   35000.0  other        135.0   Petrol    yes           2008  CL 550      rear
3   Mercedes-Benz   17800.0  van          162.0   Diesel    yes           2012  B 180       front
4   Mercedes-Benz   33000.0  vagon         91.0   NaN       yes           2013  E-Class     NaN
5   Nissan          16600.0  crossover     83.0   Petrol    yes           2013  X-Trail     full
6   Honda            6500.0  sedan        199.0   Petrol    yes           2003  Accord      front
7   Renault         10500.0  vagon        185.0   Diesel    yes           2011  Megane      front
8   Mercedes-Benz   21500.0  sedan        146.0   Gas       yes           2012  E-Class     rear
9   Mercedes-Benz   21500.0  sedan        146.0   Gas       yes           2012  E-Class     NaN
10  Toyota           1280.0  compact      200.0   Hybrid    yes           2009  Prius       NaN
11  Toyota           2005.0  compact      134.0   Gas       yes           2003  Corolla     front
12  Ferrari        300000.0  sport        123.0   Gas       yes           1988  Testarossa  NaN
  1. 创建一个数据框,以显示每个 car/model 组合的 drive 功能的模式。

    如果汽车/模型组合没有模式(例如丰田普锐斯的行),我会填写该特定汽车品牌(丰田)的模式。

    但是,如果汽车品牌本身(例如我的示例中的法拉利)没有模式,我会为 drive 功能填充数据集的模式。

def get_drive_mode(x):
    brand = x.name[0]
    if x.count() > 0:
        return x.mode() # Return mode for a brand/model if the mode exists.
    elif carsale.groupby(['car'])['drive'].count()[brand] > 0:
        brand_mode = carsale.groupby(['car'])['drive'].apply(lambda x: x.mode())[brand]
        return brand_mode # Return mode of brand if particular brand/model combo has no mode,
    else:                 # but brand itself has a mode for the 'drive' feature. 
        return carsale['drive'].mode() # Otherwise return dataset's mode for the 'drive' feature.

drive_modes = carsale.groupby(['car','model'])['drive'].apply(get_drive_mode).reset_index().drop('level_2', axis=1)
drive_modes.rename(columns={'drive': 'drive_mode'}, inplace=True)
drive_modes

    car             model        drive_mode
0   Ferrari         Testarossa   front
1   Ford            Kuga         full
2   Honda           Accord       front
3   Mercedes-Benz   B 180        front
4   Mercedes-Benz   CL 550       rear
5   Mercedes-Benz   E-Class      rear
6   Nissan          X-Trail      full
7   Renault         Megane       front
8   Toyota          Corolla      front
9   Toyota          Prius        front
  1. 如果drive 的行的值为NaN,则编写一个方法来查找给定行中给定汽车/模型的drive 模式值:
def fill_with_mode(x):
    if pd.isnull(x['drive']):
        return drive_modes[(drive_modes['car'] == x['car']) & (drive_modes['model'] == x['model'])]['drive_mode'].values[0]
    else:
        return x['drive']
  1. 将上述方法应用于carsale 数据框中的行以创建driveT 功能:
carsale['driveT'] = carsale.apply(fill_with_mode, axis=1)
del(drive_modes)

这会产生以下数据框:

carsale

    car               price  body       mileage   engType   registration  year  model       drive   driveT
0   Ford            15500.0  crossover     68.0   Gas       yes           2010  Kuga        full    full
1   Mercedes-Benz   20500.0  sedan        173.0   Gas       yes           2011  E-Class     rear    rear
2   Mercedes-Benz   35000.0  other        135.0   Petrol    yes           2008  CL 550      rear    rear
3   Mercedes-Benz   17800.0  van          162.0   Diesel    yes           2012  B 180       front   front
4   Mercedes-Benz   33000.0  vagon         91.0   NaN       yes           2013  E-Class     NaN     rear
5   Nissan          16600.0  crossover     83.0   Petrol    yes           2013  X-Trail     full    full
6   Honda            6500.0  sedan        199.0   Petrol    yes           2003  Accord      front   front
7   Renault         10500.0  vagon        185.0   Diesel    yes           2011  Megane      front   front
8   Mercedes-Benz   21500.0  sedan        146.0   Gas       yes           2012  E-Class     rear    rear
9   Mercedes-Benz   21500.0  sedan        146.0   Gas       yes           2012  E-Class     NaN     rear
10  Toyota           1280.0  compact      200.0   Hybrid    yes           2009  Prius       NaN     front
11  Toyota           2005.0  compact      134.0   Gas       yes           2003  Corolla     front   front
12  Ferrari        300000.0  sport        123.0   Gas       yes           1988  Testarossa  NaN     front

请注意,在driveT 列的第 4 行和第 9 行中,drive 列中的 NaN 值已被字符串 rear 替换,正如我们所料,它是 @987654342 的模式@ 代表梅赛德斯 E 级轿车。

此外,在第 11 行中,由于 Toyota Prius 汽车/车型组合没有模式,我们填充了 Toyota 品牌的模式,即front

最后,在第 12 行,由于没有法拉利汽车品牌的模式,我们填充整个数据集的 drive 列的模式,也就是 front

【讨论】:

  • 你好@james-dellinger 你第一步中的“level_2”是什么,它到底是做什么的 drive_modes = carale.groupby(['car','model'])['drive'] .apply(lambda x: x.mode()).reset_index().drop('level_2', axis=1)
  • 该行获取由 grouby() 对象生成的 df 并将其索引从多索引转换为仅包含 carmodel 列的普通单索引 df。多索引中的一个索引称为level_2。它的值都是 0。没有必要保留此列,因此我将其删除。目标是让drive_modes df 有一个简单的结构,我可以用它来查找给定汽车/型号的驾驶模式值。
  • 但是我得到 KeyError: "['level_2'] not found in axis",我把它用作 carsale['drive_T'] = carsale['drive'].fillna(carsale.groupby (['car', 'model'])['drive'].transform(lambda x: x.mode().reset_index().drop('level_2', axis=1))) 你能帮忙@james吗-dellinger
  • 乐于提供帮助:我不相信您正在按照我在回答中提供的步骤进行操作。我没有写carsale['drive_T'] = carsale['drive'].fillna(carsale.groupby(['car', 'model'])['drive'].transform(lambda x: x.mode().reset_index().drop('level_2', axis=1))) 这一行。我首先创建了一个查找数据框。 drive_modes = carsale.groupby(['car','model'])['drive'].apply(lambda x: x.mode()).reset_index().drop('level_2', axis=1) 这行是我这样做的。
  • 我现在已经按照你的步骤,现在从你定义函数的第 2 步得到:KeyError: ('mode', 'occured at index 4')。我需要替换 ['mode'] 如果我确实用 ['drive_mode'] 替换它,因为这是 drive_modes 数据框中的列,那么我会收到这些错误:IndexError: ('index 0 is out of bound for axis 0 大小为 0', '发生在索引 849') @james-dellinger
猜你喜欢
  • 2020-06-22
  • 2021-05-30
  • 2020-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多