У меня есть данные с большим количеством NaN:
train_feat["portarbre"].value_counts(dropna=False)
portarbre
NaN 12958
Libre 6070
Semi-libre 4449
Architecturé 316
Pyramidale 7
Pleureur 4
Name: count, dtype: int64
И столбец без NaN:
data["adr_secteur"].value_counts(dropna=False)
adr_secteur
6 6958
5 6728
2 6066
3 4249
4 4128
1 3376
38309 180
38151 92
38421 68
38249 41
38158 9
Name: count, dtype: int64
У меня есть и другие столбцы с меньшим количеством NaN, но этот проиллюстрирует проблему. Несмотря на то, что существует много NaN, самым простым решением было бы просто удалить столбец, заполнение этих 12 тысяч нулевых значений может быть, по крайней мере, иллюстративным упражнением. Поэтому я решил заполнить нулевые значения режимом, указанным в столбце sous_categorie
, как показано ниже:
mode_sect_df = train_feat.groupby("portarbre")["adr_secteur"].agg(lambda x: x.mode().max()).reset_index()
mode_sect_df.columns = ["portarbre", "mode_sect"]
for index, row in mode_sect_df.iterrows():
haut_value = row["portarbre"]
mode_sect = row["mode_sect"]
train_feat.loc[train_feat["adr_secteur"] == mode_sect, "portarbre"] = \
train_feat.loc[train_feat["adr_secteur"] == mode_sect,"portarbre"].fillna(value=haut_value)
То есть я заполняю нулевые значения в столбце portarbre
, принимая режим, соответствующий столбцу adr_secteur
:
train_feat.pivot_table(index='adr_secteur', columns='portarbre', aggfunc='size')
portarbre Architecturé Libre Pleureur Pyramidale Semi-libre
adr_secteur
1 27.0 385.0 NaN 1.0 842.0
2 2691.0 1094.0 1.0 3.0 917.0
3 20.0 622.0 NaN NaN 655.0
4 61.0 653.0 NaN NaN 534.0
5 79.0 1627.0 3120.0 NaN 358.0
6 17.0 4069.0 1.0 3.0 1079.0
38151 NaN 3.0 NaN NaN 57.0
38158 NaN NaN NaN NaN 7.0
Проблема в том, что после запуска кода я заканчиваю:
train_feat["portarbre"].value_counts(dropna=False)
portarbre
Libre 8453
NaN 4878
Semi-libre 4449
Pleureur 3122
Architecturé 2895
Pyramidale 7
Name: count, dtype: int64
Я вижу здесь две проблемы (возможно, вы увидите много других):
1. Во-первых, это очевидно: не все нулевые значения заполняются. При первом подсчете в столбце «portarbre» было 12958 нулей. Во втором, после вменения, 4878. Ошибки в коде не вижу. В столбце adr_secteur', the one I am using as support to fill the nulls in the
adr_secteur` нет нулей.
2- Во-вторых, один и тот же режим может быть у нескольких категорий:
mode_stade_dev = train_feat.groupby("hauteurarbre")["stadededeveloppement"].agg(lambda x: x.mode().max()).reset_index()
mode_stade_dev.columns = ["hauteurarbre", "mode_stade"]
hauteurarbre mode_stade
0 Moins de 10 m Arbre adulte
1 Plus de 20 m Arbre adulte
2 de 10 m à 20 m Arbre adulte
То есть при первом взаимодействии в цикле for при выборе Arbre adulte
для заполнения значений все значения nan будут заполнены Moins de 10 m
, и тогда для других режимов больше не будет нулевых значений для заполнения. Как я могу это улучшить? Я не хочу просто выбирать режим столбца, чтобы заполнить все нулевые значения в этом столбце. Возможно, это сработает для этого конкретного столбца, но у меня в наборе данных есть другие, что это не очень хорошая идея (например, категория дерева и стадия его развития имеют разные режимы в зависимости от категории, но некоторые из них будут повторять поднятие той же проблемы, которую я здесь обсуждаю).
Я думаю, что столбцы в groupby должны быть инвертированы: вы хотите train_feat.groupby("adr_secteur")["portarbre"].agg(lambda x: x.mode().max()).reset_index()
, а затем измените имя столбца. это также должно решить ваш второй вопрос, поскольку mode_stade тогда будет иметь только уникальные значения.
Вам следует перевернуть сгруппированный portarbre<->adr_secteur
фрейм данных:
mode_sect_df = train_feat.dropna(
subset=["portarbre"] # if NA is most popular for some values
).groupby("adr_secteur")["portarbre"].agg(
lambda x: x.mode().max()
)
train_feat.fillna(
{"portarbre": train_feat["adr_secteur"].map(mode_sect_df)},
inline=True
)
А затем в каждой строке, где пропущен portarbre
, вы заполняете ее самым популярным значением для строк с этим значением adr_secteur
.
Чтобы понять, почему это так, представьте, что у вас есть фрейм данных, в котором указан пол и рост человека, и есть 100 строк для мужчин и 10 000 для женщин, а некоторые высоты пропущены. Вы заполните пропущенные данные о росте по среднему росту для каждого пола, а не по популярному полу для каждого роста (для всех ростов самым популярным полом будет «женщина» из-за сильного дисбаланса данных).
Также, если вы хотите узнать об этом больше, вы можете прочитать о «пропавших без вести не случайно» (МНАР). Это поможет вам решить, имеет ли этот метод смысл для ваших данных. https://www.ncbi.nlm.nih.gov/books/NBK493614/
Большое спасибо за ваше время и за этот ответ. Я уже тестировал здесь и исправил проблемы. Спасибо :)
какова форма
mode_sect_df
, есть ли в ней все отличные значения от adr_secteur? что возвращаетprint(set( data["adr_secteur"])-set(mode_sect_df['mode_sect']))
? Я предполагаю, что поскольку у вас есть только 5 различных значений (кроме nan) в portarbre, вы получите только 5 режимов, то есть 5 значений из adr_secteur, которые будут использоваться для заполнения nan в вашем цикле, пока у вас 11 различных значений. Таким образом, ваш цикл не работает для всех значений, отличающихся от adr_secteur, поэтому вы сохраняете nan в другом столбце.