Заполнение NaN по режиму

У меня есть данные с большим количеством 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, и тогда для других режимов больше не будет нулевых значений для заполнения. Как я могу это улучшить? Я не хочу просто выбирать режим столбца, чтобы заполнить все нулевые значения в этом столбце. Возможно, это сработает для этого конкретного столбца, но у меня в наборе данных есть другие, что это не очень хорошая идея (например, категория дерева и стадия его развития имеют разные режимы в зависимости от категории, но некоторые из них будут повторять поднятие той же проблемы, которую я здесь обсуждаю).

какова форма 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 в другом столбце.

Ben.T 08.08.2024 03:13

Я думаю, что столбцы в groupby должны быть инвертированы: вы хотите train_feat.groupby("adr_secteur")["portarbre"].agg(lambda x: x.mode().max()).reset_index(), а затем измените имя столбца. это также должно решить ваш второй вопрос, поскольку mode_stade тогда будет иметь только уникальные значения.

Ben.T 08.08.2024 03:16
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам следует перевернуть сгруппированный 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/

Большое спасибо за ваше время и за этот ответ. Я уже тестировал здесь и исправил проблемы. Спасибо :)

Dimitri 08.08.2024 19:41

Другие вопросы по теме