Pandas Параметр fillna () inplace не работает без использования тройного цикла For - PullRequest
1 голос
/ 13 июля 2020

Я пытаюсь разбить фрейм данных на четыре части и рассчитать округленные средние значения для каждой части, используя fillna(). У меня есть два столбца, main_campus и degree_type, которые я хочу отфильтровать, каждый из которых имеет по два уникальных значения. Таким образом, между ними я должен иметь возможность фильтровать фрейм данных на две группы.

Сначала я сделал это с тройкой для l oop (см. Ниже), что, похоже, работает, но когда я попытался это сделать более элегантным способом я получил предупреждение SettingWithCopy, которое я не мог исправить, используя .loc или .copy(), а отсутствующие значения не были бы заполнены, даже если inplace было установлено на True. Вот код для последнего метода:

# Imputing mean values for main campus BA students
df[(df.main_campus == 1) &
            (df.degree_type == 'BA')] = df[(df.main_campus == 1) &
            (df.degree_type == 'BA')].fillna(
                df[(nulled_data.main_campus == 1) &
                            (df.degree_type == 'BA')
                            ].mean(),
                     inplace=True)
            
# Imputing mean values for main campus BS students
df[(df.main_campus == 1) &
            (df.degree_type == 'BS')] = df[(df.main_campus == 1) &
            (df.degree_type == 'BS')].fillna(
                df[(df.main_campus == 1) &
                            (df.degree_type == 'BS')
                            ].mean(),
                     inplace=True)
            
# Imputing mean values for downtown campus BA students
df[(df.main_campus == 0) &
            (df.degree_type == 'BA')] = df[(df.main_campus == 0) &
            (df.degree_type == 'BA')].fillna(
                df[(df.main_campus == 0) &
                            (df.degree_type == 'BA')
                            ].mean(),
                     inplace=True)

# Imputing mean values for downtown campus BS students          
df[(df.main_campus == 0) &
            (df.degree_type == 'BS')] = df[(df.main_campus == 0) &
            (df.degree_type == 'BS')].fillna(
                df[(df.main_campus == 0) &
                            (df.degree_type == 'BS')
                            ].mean(),
                     inplace=True)      

Я должен упомянуть, что предыдущий код прошел несколько итераций, пробуя его без возврата к срезу, с inplace, et c и без него.

Вот код с тройкой для l oop, который работает:

imputation_cols = [# all the columns I want to impute]

for col in imputation_cols:

  for i in [1, 0]:

    for path in ['BA', 'BS']:

      group = ndf.loc[((df.main_campus == i) &
                               (df.degree_type == path)), :]
      
      group = group.fillna(value=round(group.mean()))

      df.loc[((df.main_campus == i) &
                               (df.degree_type == path)), :] = group

Стоит упомянуть, что я думаю использование переменной group в тройке для l oop код также должен помочь заполненным значениям NaN фактически вернуться в фрейм данных, но мне нужно будет дважды проверить это.

Кто-нибудь знает, что здесь происходит?

Сообщите мне, если в моем объяснении / вопросе чего-то не хватает. Спасибо!

1 Ответ

1 голос
/ 13 июля 2020

Хороший способ решить такую ​​проблему - упростить код. Упрощение кода облегчает поиск источника предупреждения:

group1 = (df.main_campus == 1) & (df.degree_type == 'BA')
group2 = (df.main_campus == 1) & (df.degree_type == 'BS')
group3 = (df.main_campus == 0) & (df.degree_type == 'BA')
group4 = (df.main_campus == 0) & (df.degree_type == 'BS')

# Imputing mean values for main campus BA students
df.loc[group1, :] = df.loc[group1, :].fillna(df.loc[group1, :].mean())  # repeat for other groups

Теперь вы можете более четко увидеть проблему. Вы пытаетесь записать среднее значение df обратно в df. Pandas выдает предупреждение, потому что срез, который вы используете для вычисления среднего, может быть несовместим с измененным фреймом данных. В вашем случае это дает правильный результат. Но согласованность вашего фрейма данных находится под угрозой.

Вы можете решить эту проблему, предварительно вычислив среднее значение:

group1_mean = df.loc[group1, :].mean()
df.loc[group1, :] = df.loc[group1, :].fillna(group1_mean)

На мой взгляд, это делает код более понятным. Но у вас все еще есть четыре группы (group1, group2, ...). Явный знак использовать al oop:

from itertools import product

for campus, degree in product([1, 0], ['BS', 'BA']):
    group = (df.main_campus == campus) & (df.degree_type == degree)
    group_mean = df.loc[group, :].mean()
    df.loc[group, :] = df.loc[group, :].fillna(group_mean)

Я использовал product из itertools, чтобы избавиться от уродливого вложенного l oop. Это очень похоже на ваше «неизящное» первое решение. Итак, вы были почти там в первый раз.

В итоге мы получили четыре строки кода и al oop. Я уверен, что с некоторыми pandas magi c вы могли бы преобразовать его в одну строку. Однако вы все равно поймете эти четыре строчки через неделю, месяц или год. Кроме того, другие люди, читающие ваш код, легко поймут его. Учитывается читаемость.

Заявление об ограничении ответственности : я не смог протестировать код, так как вы не предоставили образец фрейма данных. Так что мой код может выдать ошибку из-за опечатки. Минимальный воспроизводимый пример значительно упрощает ответы на вопросы. Учтите это, когда в следующий раз будете задавать вопрос о SO.

...