Замініть недійсні значення на None в Pandas DataFrame


80

Чи існує якийсь метод заміни значень Noneна Pandas у Python?

Ви можете використовувати df.replace('pre', 'post')і можете замінити значення іншим, але цього не можна зробити, якщо ви хочете замінити Noneзначенням, що при спробі ви отримаєте дивний результат.

Ось приклад:

df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)

що повертає успішний результат.

Але,

df.replace('-', None)

який повертає такий результат:

0
0   - // this isn't replaced
1   3
2   2
3   5
4   1
5  -5
6  -1
7  -1 // this is changed to `-1`...
8   9

Чому повертається такий дивний результат?

Оскільки я хочу перелити цей фрейм даних у базу даних MySQL, я не можу помістити NaNзначення в будь-який елемент у своєму фреймі даних, а хочу надати None. Звичайно, ви можете спочатку змінити '-'на, NaNа потім перетворити NaNна None, але я хочу знати, чому кадр даних діє так жахливо.

Перевірено на пандах 0.12.0 dev на Python 2.7 та OS X 10.8. Python - це попередньо встановлена ​​версія в OS X, і я для вашої інформації встановив панди за допомогою сценарію SciPy Superpack.


Чи write_frameне розбирає NaNs на nones?
Andy Hayden

Так. Ви стикаєтеся з InternalError: (1054, u"Unknown column 'nan' in 'field list'")помилкою. Я не знаю жодних рішень на ньому, крім перетворення NaNна Noneдо виконання write_frameметоду.
Blaszard

Яку версію панд ви використовуєте?
Andy Hayden

Scipy super pack видає розробника? Добре, я точно вважаю, що ви повинні підняти це як проблему на github , це не повинно бути надто складно виправити.
Andy Hayden

Якщо ви читаєте ці дані з CSV / Excel , ви можете прочитати ці значення як NaN, використовуючи na_valuesаргумент. Більше інформації у цій відповіді.
cs95

Відповіді:


111

Насправді в пізніших версіях панд це дасть TypeError:

df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping

Ви можете зробити це, передавши список або словник:

In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', {0: None})
Out[11]:
      0
0  None
1     3
2     2
3     5
4     1
5    -5
6    -1
7  None
8     9

Але я рекомендую використовувати NaN, а не None:

In [12]: df.replace('-', np.nan)
Out[12]:
     0
0  NaN
1    3
2    2
3    5
4    1
5   -5
6   -1
7  NaN
8    9

15
Або просто список, наприклад df.replace(['-'], [None]), або df.replace({'-': None}), я думаю. Використання Noneяк сторожового виключає використання його також як значення ..
DSM

@ user2360798 replace - насправді дуже багата на функції (читання складна) функція, документація (dev) дійсно хороша.
Енді Хайден,

4
Не знаю, чи це очевидно, але довелося dfповернути собі, як:df = df.replace({'?': np.nan})
luckyging3r

3
@AndyHayden df.replace('-', df.replace(['-'], [None])виглядає забавно , це друкарська помилка?
lin_bug

2
@lin_bug Хоча, здається, це не працює в останніх версіях панд. df.where (df! = '-', None) працює
Енді Хейден

17

Я віддаю перевагу використанню рішення replaceз-за dictйого простоти та елегантності:

df.replace({'-': None})

Ви також можете замінити більше:

df.replace({'-': None, 'None': None})

І навіть для більших замін завжди очевидно і зрозуміло, що чим замінено - що, на мій погляд, складніше для довгих списків.


1
Варто зазначити, що частина того, чому працює ця техніка, полягає в тому, що використання dictтипу in to_replaceпризводить до того, що methodпараметр не оцінюється, і, отже, за method='pad'замовчуванням не має негативних наслідків.
bsplosion

15

whereце, мабуть, те, що ви шукаєте. Так

data=data.where(data=='-', None) 

З документів panda :

where [повертає] об'єкт тієї самої форми, що і self, відповідні записи якого надходять від self, де cond є True, а в іншому - від інших).


5
Це насправді неточно. data = data.where (data == '-', None) замінить все, що НЕ РІВНЕ '-', на None. Версія Pandas де зберігає значення першого аргументу (в даному випадку дані == '-'), а будь-що інше замінює другим аргументом (у цьому випадку - Ні). Це трохи заплутано, оскільки np.where є більш явним у тому, що він запитує умовне значення в першому аргументі, потім якщо true у другому аргументі, то якщо false у третьому аргументі.
clg4,

8

Перш ніж продовжувати цю публікацію, важливо зрозуміти різницю між NaN та None . Один - тип плаваючого, інший - об’єктного типу. Pandas краще підходить для роботи зі скалярними типами, оскільки багато методів для цих типів можна векторизувати. Панди намагаються послідовно обробляти None та NaN, але NumPy не може.

Моя пропозиція ( і Енді ) - дотримуватися NaN.

Але щоб відповісти на ваше запитання ...

pandas> = 0,18: Використовуйте na_values=['-']аргумент зread_csv

Якщо ви завантажили ці дані з CSV / Excel, я маю для вас хороші новини. Ви можете скасувати це в корені під час завантаження даних, замість того, щоб писати виправлення з кодом як наступний крок.

Більшість pd.read_*функцій (таких як read_csvі read_excel) приймають na_valuesатрибут.

file.csv

A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0

Тепер, щоб перетворити -символи в NaN, зробіть,

import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df

     A    B
0  NaN  1.0
1  3.0  NaN
2  2.0  NaN
3  5.0  3.0
4  1.0 -2.0
5 -5.0  4.0
6 -1.0 -1.0
7  NaN  0.0
8  9.0  0.0

І подібне для інших функцій / форматів файлів.

PS: На v0.24 + ви можете зберегти цілочисельний тип, навіть якщо у вашій колонці є NaN (так, поговоримо про те, щоб мати пиріг і з'їсти його теж). Ви можете вказатиdtype='Int32'

df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

df.dtypes

A    Int32
B    Int32
dtype: object

Dtype не є звичайним типом int ..., а навпаки, Nullable Integer Type. Є й інші варіанти.


Обробка числових даних: pd.to_numericсerrors='coerce

Якщо ви маєте справу з числовими даними, швидшим рішенням є використання pd.to_numericз errors='coerce'аргументом, який примушує до NaN недійсні значення (значення, які неможливо передати числовим).

pd.to_numeric(df['A'], errors='coerce')

0    NaN
1    3.0
2    2.0
3    5.0
4    1.0
5   -5.0
6   -1.0
7    NaN
8    9.0
Name: A, dtype: float64

Щоб зберегти (з нульовим значенням) ціле число dtype, використовуйте

pd.to_numeric(df['A'], errors='coerce').astype('Int32')

0    NaN
1      3
2      2
3      5
4      1
5     -5
6     -1
7    NaN
8      9
Name: A, dtype: Int32 

Щоб примусити кілька стовпців, використовуйте apply:

df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

... і призначити результат назад після.

Більше інформації можна знайти в цій відповіді .



0

Встановлення нульових значень можна виконати за допомогою np.nan:

import numpy as np
df.replace('-', np.nan)

Перевага полягає в тому, що вони df.last_valid_index()визнають їх недійсними.


0

Використання заміни та призначення нового df:

import pandas as pd
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
dfnew = df.replace('-', 0)
print(dfnew)


(venv) D:\assets>py teste2.py
   0
0  0
1  3
2  2
3  5
4  1
5 -5

0
df.replace('-', np.nan).astype("object")

Це забезпечить вам можливість використовувати isnull()пізніше на вашому фреймі даних


0

З версією Pandas ≥1.0.0 я б використовував DataFrame.replaceабо Series.replace:

df.replace(old_val, pd.NA, inplace=True)

Це краще з двох причин:

  1. Він використовує pd.NAзамість Noneабо np.nan.
  2. Він замінює значення на місці, що може бути більш ефективним для пам'яті.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.