Преобразование всех значений, не относящихся к NaNs, в 1 с использованием понимания списка по нескольким столбцам

#python #pandas

Вопрос:

Вот некоторые данные:

 test = pd.DataFrame([[np.nan,"cat","mouse", 'tiger'],
    ["tiger","dog","elephant", "mouse"],
    ["cat",np.nan,"giraffe", "cat"],
    [np.nan,np.nan,"ant", "ant"]],  columns=["animal1","animal2","animal3", "animal4"])
 

Я хочу преобразовать все NAN в 0, а все ответы-в 1.

 #First I convert all NaNs to 0 and make everything string
test = test.fillna(0)
test = test.astype(str)
 

Затем я создаю список интересующих столбцов (в данном примере это не имеет смысла, потому что всего 2 столбца, но в моем конкретном случае их много).

 op = test.iloc[:,0:2].columns.tolist()
 

Я бы подумал, что смогу просто сделать это:

 test[op] = [1 if x != '0' else 0 for x in test[op]]
 

Но это не работает, так как преобразует все в 1.

Затем я попытался сделать по каждому столбцу вручную, и это действительно работает:

 test['animal1'] = [1 if x != '0' else 0 for x in test['animal1']]
 

Кто-нибудь знает, почему работает последний способ, но не первый? И любые рекомендации о том, как заставить его работать, будут оценены с благодарностью.

Редактировать/обновление: SeaBean предоставил решение, которое работает (спасибо!!). Мне все равно было бы интересно узнать, почему метод, который я использовал, работал только при выполнении по одному столбцу за раз (вручную).

Комментарии:

1. вы проверили тип этого x ?

2. Смотрите мое редактирование, чтобы объяснить свой запрос о том, почему понимание списка для нескольких столбцов не сработало.

Ответ №1:

Вы можете использовать .notna() и преобразовать в 0/1 .astype() следующим образом:

 test.notna().astype(int)
 

Результат:

    animal1  animal2  animal3  animal4
0        0        1        1        1
1        1        1        1        1
2        1        0        1        1
3        0        0        1        1
 

Редактировать

Чтобы объяснить, почему ваш опробованный метод работал только при выполнении его по одному столбцу за раз, но не для нескольких столбцов:

Когда вы работаете над одним столбцом за раз и указываете, например test['animal1'] , в понимании списка, вы повторяете элементы серии Pandas соответствующего столбца. Это позволит выполнить задачу так, как вы ожидаете.

Однако, когда вы делаете это в нескольких столбцах, включая test[op] в список понимание, здесь вместо серии Панд используется test[op] фрейм данных. Когда вы повторяете этот фрейм данных, вы получаете только метки столбцов фрейма данных. Вы поймете это, когда попробуете понять следующий список:

 [x for x in test[op]]
 

что дает:

 ['animal1', 'animal2']
 

Таким образом, в вашем понимании списка для нескольких столбцов ваше сравнение x != '0' всегда будет возвращать значение true и давать все 1, поскольку метки столбцов, с которыми вы сравниваете, не содержат «0».

Комментарии:

1. Спасибо за редактирование дополнительного объяснения. Это было очень понятно и полезно!

Ответ №2:

Вы можете использовать .isna() и инвертировать результаты:

 print(~test.isna())

   animal1  animal2  animal3  animal4
0    False     True     True     True
1     True     True     True     True
2     True    False     True     True
3    False    False     True     True
 

Если вы предпочитаете иметь 0 и 1, умножьте это на 1:

 print((~test.isna())*1)

   animal1  animal2  animal3  animal4
0        0        1        1        1
1        1        1        1        1
2        1        0        1        1
3        0        0        1        1