Работа с пропущенными данными
В этом разделе мы обсудим пропущенные (также обозначаемые как NA) значения в pandas.
Примечание
Выбор использования NaN для обозначения пропущенных данных в основном обусловлен соображениями простоты и производительности. Это отличается от подхода MaskedArray, например, scikits.timeseries. Мы надеемся, что NumPy вскоре предоставит решение с родным типом NA (аналогичное R), достаточно производительное для использования в pandas.
См. пособие для некоторых расширенных стратегий
Основы пропущенных данных
Когда/почему данные пропадают?
Некоторые могут спорить о нашем использовании слова «пропущенные». Под «пропущенными» мы просто подразумеваем значения NULL или «отсутствующие по какой-либо причине». Многие наборы данных поставляются с пропущенными данными, либо потому, что они существовали, но не были собраны, либо они никогда не существовали. Например, в наборе финансовых временных рядов некоторые временные ряды могут начинаться в разные даты. Таким образом, значения до даты начала обычно будут помечены как пропущенные.
В pandas одним из наиболее распространенных способов введения пропущенных данных в набор данных является переиндексация. Например
In [1]: df = pd.DataFrame(np.random.randn(5, 3), index=['a', 'c', 'e', 'f', 'h'],
...: columns=['one', 'two', 'three'])
...:
In [2]: df['four'] = 'bar'
In [3]: df['five'] = df['one'] > 0
In [4]: df
Out[4]:
one two three four five
a 0.469112 -0.282863 -1.509059 bar True
c -1.135632 1.212112 -0.173215 bar False
e 0.119209 -1.044236 -0.861849 bar True
f -2.104569 -0.494929 1.071804 bar False
h 0.721555 -0.706771 -1.039575 bar True
In [5]: df2 = df.reindex(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
In [6]: df2
Out[6]:
one two three four five
a 0.469112 -0.282863 -1.509059 bar True
b NaN NaN NaN NaN NaN
c -1.135632 1.212112 -0.173215 bar False
d NaN NaN NaN NaN NaN
e 0.119209 -1.044236 -0.861849 bar True
f -2.104569 -0.494929 1.071804 bar False
g NaN NaN NaN NaN NaN
h 0.721555 -0.706771 -1.039575 bar True
Значения, считающиеся «пропущенными»
Поскольку данные имеют множество форм, pandas стремится к гибкости в обработке пропущенных данных. Хотя NaN является стандартным маркером пропущенных значений по соображениям вычислительной скорости и удобства, нам необходимо иметь возможность легко обнаруживать это значение с данными разных типов: с плавающей точкой, целыми числами, булевыми и общими объектами. Однако во многих случаях возникает Python None и мы также хотим считать его «пропущенным» или «NULL».
Примечание
До версии v0.10.0 inf и -inf также считались «NULL» в вычислениях. Сейчас это не так по умолчанию; используйте параметр mode.use_inf_as_null для восстановления.
Чтобы упростить обнаружение пропущенных значений (и для разных типов массивов данных), pandas предоставляет функции isnull() и notnull(), которые также являются методами объектов Series и DataFrame:
In [7]: df2['one']
Out[7]:
a 0.469112
b NaN
c -1.135632
d NaN
e 0.119209
f -2.104569
g NaN
h 0.721555
Name: one, dtype: float64
In [8]: pd.isnull(df2['one'])
Out[8]:
a False
b True
c False
d True
e False
f False
g True
h False
Name: one, dtype: bool
In [9]: df2['four'].notnull()
Out[9]:
a True
b False
c True
d False
e True
f True
g False
h True
Name: four, dtype: bool
In [10]: df2.isnull()
Out[10]:
one two three four five
a False False False False False
b True True True True True
c False False False False False
d True True True True True
e False False False False False
f False False False False False
g True True True True True
h False False False False False
Предупреждение
Следует учитывать, что в Python (и NumPy) значения nan's не сравниваются как равные, но None's сравниваются как равные. Обратите внимание, что Pandas/NumPy использует тот факт, что np.nan != np.nan, и обрабатывает None как np.nan.
In [11]: None == None Out[11]: True In [12]: np.nan == np.nan Out[12]: False
Таким образом, по сравнению с вышеизложенным, сравнение скаляров на равенство с None/np.nan не дает полезной информации.
In [13]: df2['one'] == np.nan Out[13]: a False b False c False d False e False f False g False h False Name: one, dtype: bool
Типы datetime
Для типов datetime64[ns] NaT представляет пропущенные значения. Это псевдородное значение-сентинэл, которое может быть представлено NumPy в одном типе данных (datetime64[ns]). Объекты pandas обеспечивают совместимость между NaT и NaN.
In [14]: df2 = df.copy()
In [15]: df2['timestamp'] = pd.Timestamp('20120101')
In [16]: df2
Out[16]:
one two three four five timestamp
a 0.469112 -0.282863 -1.509059 bar True 2012-01-01
c -1.135632 1.212112 -0.173215 bar False 2012-01-01
e 0.119209 -1.044236 -0.861849 bar True 2012-01-01
f -2.104569 -0.494929 1.071804 bar False 2012-01-01
h 0.721555 -0.706771 -1.039575 bar True 2012-01-01
In [17]: df2.ix[['a','c','h'],['one','timestamp']] = np.nan
In [18]: df2
Out[18]:
one two three four five timestamp
a NaN -0.282863 -1.509059 bar True NaT
c NaN 1.212112 -0.173215 bar False NaT
e 0.119209 -1.044236 -0.861849 bar True 2012-01-01
f -2.104569 -0.494929 1.071804 bar False 2012-01-01
h NaN -0.706771 -1.039575 bar True NaT
In [19]: df2.get_dtype_counts()
Out[19]:
bool 1
datetime64[ns] 1
float64 3
object 1
dtype: int64
Вставка пропущенных данных
Вы можете вставить пропущенные значения, просто присвоив их контейнерам. Фактическое используемое пропущенное значение будет выбрано на основе типа данных.
Например, числовые контейнеры всегда будут использовать NaN независимо от выбранного типа пропущенного значения:
In [20]: s = pd.Series([1, 2, 3]) In [21]: s.loc[0] = None In [22]: s Out[22]: 0 NaN 1 2.0 2 3.0 dtype: float64
Точно так же контейнеры datetime всегда будут использовать NaT.
Для контейнеров типа object pandas будет использовать предоставленное значение:
In [23]: s = pd.Series(["a", "b", "c"]) In [24]: s.loc[0] = None In [25]: s.loc[1] = np.nan In [26]: s Out[26]: 0 None 1 NaN 2 c dtype: object
Вычисления с пропущенными данными
Пропущенные значения естественным образом распространяются через арифметические операции между объектами pandas.
In [27]: a
Out[27]:
one two
a NaN -0.282863
c NaN 1.212112
e 0.119209 -1.044236
f -2.104569 -0.494929
h -2.104569 -0.706771
In [28]: b
Out[28]:
one two three
a NaN -0.282863 -1.509059
c NaN 1.212112 -0.173215
e 0.119209 -1.044236 -0.861849
f -2.104569 -0.494929 1.071804
h NaN -0.706771 -1.039575
In [29]: a + b
Out[29]:
one three two
a NaN NaN -0.565727
c NaN NaN 2.424224
e 0.238417 NaN -2.088472
f -4.209138 NaN -0.989859
h NaN NaN -1.413542
Статистические описательные данные и вычислительные методы, обсуждаемые в обзоре структуры данных (и перечисленные в здесь и здесь), все написаны с учетом пропущенных данных. Например:
- При суммировании данных значения NA (пропущенные) будут обрабатываться как нули
- Если все данные являются NA, результат будет NA
- Методы, такие как cumsum и cumprod, игнорируют значения NA, но сохраняют их в результирующих массивах
In [30]: df
Out[30]:
one two three
a NaN -0.282863 -1.509059
c NaN 1.212112 -0.173215
e 0.119209 -1.044236 -0.861849
f -2.104569 -0.494929 1.071804
h NaN -0.706771 -1.039575
In [31]: df['one'].sum()
Out[31]: -1.9853605075978744
In [32]: df.mean(1)
Out[32]:
a -0.895961
c 0.519449
e -0.595625
f -0.509232
h -0.873173
dtype: float64
In [33]: df.cumsum()
Out[33]:
one two three
a NaN -0.282863 -1.509059
c NaN 0.929249 -1.682273
e 0.119209 -0.114987 -2.544122
f -1.985361 -0.609917 -1.472318
h NaN -1.316688 -2.511893
Значения NA в GroupBy
Группы NA в GroupBy автоматически исключаются. Это поведение соответствует R, например:
In [34]: df
Out[34]:
one two three
a NaN -0.282863 -1.509059
c NaN 1.212112 -0.173215
e 0.119209 -1.044236 -0.861849
f -2.104569 -0.494929 1.071804
h NaN -0.706771 -1.039575
In [35]: df.groupby('one').mean()
Out[35]:
two three
one
-2.104569 -0.494929 1.071804
0.119209 -1.044236 -0.861849
См. раздел о группировке здесь для получения дополнительной информации.
Очистка/заполнение пропущенных данных
Объекты pandas оснащены различными методами обработки данных для работы с пропущенными данными.
Заполнение пропущенных значений: fillna
Функция fillna может «заполнить» значения NA непропущенными данными несколькими способами, которые мы проиллюстрируем:
Замена NA скалярным значением
In [36]: df2
Out[36]:
one two three four five timestamp
a NaN -0.282863 -1.509059 bar True NaT
c NaN 1.212112 -0.173215 bar False NaT
e 0.119209 -1.044236 -0.861849 bar True 2012-01-01
f -2.104569 -0.494929 1.071804 bar False 2012-01-01
h NaN -0.706771 -1.039575 bar True NaT
In [37]: df2.fillna(0)
Out[37]:
one two three four five timestamp
a 0.000000 -0.282863 -1.509059 bar True 1970-01-01
c 0.000000 1.212112 -0.173215 bar False 1970-01-01
e 0.119209 -1.044236 -0.861849 bar True 2012-01-01
f -2.104569 -0.494929 1.071804 bar False 2012-01-01
h 0.000000 -0.706771 -1.039575 bar True 1970-01-01
In [38]: df2['four'].fillna('missing')
Out[38]:
a bar
c bar
e bar
f bar
h bar
Name: four, dtype: object
Заполнение пробелов вперед или назад
Используя те же аргументы заполнения, что и в переиндексации, мы можем распространять непропущенные значения вперед или назад:
In [39]: df
Out[39]:
one two three
a NaN -0.282863 -1.509059
c NaN 1.212112 -0.173215
e 0.119209 -1.044236 -0.861849
f -2.104569 -0.494929 1.071804
h NaN -0.706771 -1.039575
In [40]: df.fillna(method='pad')
Out[40]:
one two three
a NaN -0.282863 -1.509059
c NaN 1.212112 -0.173215
e 0.119209 -1.044236 -0.861849
f -2.104569 -0.494929 1.071804
h -2.104569 -0.706771 -1.039575
Ограничение объема заполнения
Если мы хотим заполнить только последовательные пробелы до определенного числа точек данных, мы можем использовать ключевое слово limit:
In [41]: df Out[41]: one two three a NaN -0.282863 -1.509059 c NaN 1.212112 -0.173215 e NaN NaN NaN f NaN NaN NaN h NaN -0.706771 -1.039575 In [42]: df.fillna(method='pad', limit=1) Out[42]: one two three a NaN -0.282863 -1.509059 c NaN 1.212112 -0.173215 e NaN 1.212112 -0.173215 f NaN NaN NaN h NaN -0.706771 -1.039575
Напоминаем, что доступны следующие методы заполнения:
| Метод | Действие |
|---|---|
| pad / ffill | Заполнение значений вперед |
| bfill / backfill | Заполнение значений назад |
При работе со временными рядами использование pad/ffill очень распространено, так как «последнее известное значение» доступно в каждой точке времени.
Функция ffill() эквивалентна fillna(method='ffill'), а bfill() эквивалентна fillna(method='bfill')
Заполнение с помощью PandasObject
Новое в версии 0.12.
Вы также можете использовать fillna с помощью словаря или Series, которые могут быть выровнены. Метки словаря или индекс Series должны соответствовать столбцам таблицы, которую вы хотите заполнить. Это используется для заполнения DataFrame средним значением столбца.
In [43]: dff = pd.DataFrame(np.random.randn(10,3), columns=list('ABC'))
In [44]: dff.iloc[3:5,0] = np.nan
In [45]: dff.iloc[4:6,1] = np.nan
In [46]: dff.iloc[5:8,2] = np.nan
In [47]: dff
Out[47]:
A B C
0 0.271860 -0.424972 0.567020
1 0.276232 -1.087401 -0.673690
2 0.113648 -1.478427 0.524988
3 NaN 0.577046 -1.715002
4 NaN NaN -1.157892
5 -1.344312 NaN NaN
6 -0.109050 1.643563 NaN
7 0.357021 -0.674600 NaN
8 -0.968914 -1.294524 0.413738
9 0.276662 -0.472035 -0.013960
In [48]: dff.fillna(dff.mean())
Out[48]:
A B C
0 0.271860 -0.424972 0.567020
1 0.276232 -1.087401 -0.673690
2 0.113648 -1.478427 0.524988
3 -0.140857 0.577046 -1.715002
4 -0.140857 -0.401419 -1.157892
5 -1.344312 -0.401419 -0.293543
6 -0.109050 1.643563 -0.293543
7 0.357021 -0.674600 -0.293543
8 -0.968914 -1.294524 0.413738
9 0.276662 -0.472035 -0.013960
In [49]: dff.fillna(dff.mean()['B':'C'])
Out[49]:
A B C
0 0.271860 -0.424972 0.567020
1 0.276232 -1.087401 -0.673690
2 0.113648 -1.478427 0.524988
3 NaN 0.577046 -1.715002
4 NaN -0.401419 -1.157892
5 -1.344312 -0.401419 -0.293543
6 -0.109050 1.643563 -0.293543
7 0.357021 -0.674600 -0.293543
8 -0.968914 -1.294524 0.413738
9 0.276662 -0.472035 -0.013960
Новое в версии 0.13.
Результат такой же, как выше, но выравнивается значение «заполнения», которое в данном случае является Series.
In [50]: dff.where(pd.notnull(dff), dff.mean(), axis='columns')
Out[50]:
A B C
0 0.271860 -0.424972 0.567020
1 0.276232 -1.087401 -0.673690
2 0.113648 -1.478427 0.524988
3 -0.140857 0.577046 -1.715002
4 -0.140857 -0.401419 -1.157892
5 -1.344312 -0.401419 -0.293543
6 -0.109050 1.643563 -0.293543
7 0.357021 -0.674600 -0.293543
8 -0.968914 -1.294524 0.413738
9 0.276662 -0.472035 -0.013960
Удаление меток осей с пропущенными данными: dropna
Возможно, вам нужно просто исключить метки из набора данных, которые относятся к пропущенным данным. Для этого используйте метод dropna:
In [51]: df
Out[51]:
one two three
a NaN -0.282863 -1.509059
c NaN 1.212112 -0.173215
e NaN 0.000000 0.000000
f NaN 0.000000 0.000000
h NaN -0.706771 -1.039575
In [52]: df.dropna(axis=0)
Out[52]:
Empty DataFrame
Columns: [one, two, three]
Index: []
In [53]: df.dropna(axis=1)
Out[53]:
two three
a -0.282863 -1.509059
c 1.212112 -0.173215
e 0.000000 0.000000
f 0.000000 0.000000
h -0.706771 -1.039575
In [54]: df['one'].dropna()
Out[54]: Series([], Name: one, dtype: float64)
Series.dropna — более простой метод, так как он рассматривает только одну ось. DataFrame.dropna имеет значительно больше вариантов, чем Series.dropna, которые можно изучить в API.
Интерполяция
Новое в версии 0.13.0: interpolate() и interpolate() имеют переработанные методы и функциональность интерполяции.
Новое в версии 0.17.0: был добавлен ключевой аргумент limit_direction.
Как объекты Series, так и DataFrame имеют метод interpolate , который по умолчанию выполняет линейную интерполяцию в точках с пропущенными данными.
In [55]: ts
Out[55]:
2000-01-31 0.469112
2000-02-29 NaN
2000-03-31 NaN
2000-04-28 NaN
2000-05-31 NaN
2000-06-30 NaN
2000-07-31 NaN
...
2007-10-31 -3.305259
2007-11-30 -5.485119
2007-12-31 -6.854968
2008-01-31 -7.809176
2008-02-29 -6.346480
2008-03-31 -8.089641
2008-04-30 -8.916232
Freq: BM, dtype: float64
In [56]: ts.count()
Out[56]: 61
In [57]: ts.interpolate().count()
Out[57]: 100
In [58]: ts.interpolate().plot()
Out[58]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff2667af150>
Интерполяция, учитывающая индексы, доступна через ключевое слово method:
In [59]: ts2 Out[59]: 2000-01-31 0.469112 2000-02-29 NaN 2002-07-31 -5.689738 2005-01-31 NaN 2008-04-30 -8.916232 dtype: float64 In [60]: ts2.interpolate() Out[60]: 2000-01-31 0.469112 2000-02-29 -2.610313 2002-07-31 -5.689738 2005-01-31 -7.302985 2008-04-30 -8.916232 dtype: float64 In [61]: ts2.interpolate(method='time') Out[61]: 2000-01-31 0.469112 2000-02-29 0.273272 2002-07-31 -5.689738 2005-01-31 -7.095568 2008-04-30 -8.916232 dtype: float64
Для индекса с плавающей точкой используйте method='values':
In [62]: ser Out[62]: 0.0 0.0 1.0 NaN 10.0 10.0 dtype: float64 In [63]: ser.interpolate() Out[63]: 0.0 0.0 1.0 5.0 10.0 10.0 dtype: float64 In [64]: ser.interpolate(method='values') Out[64]: 0.0 0.0 1.0 1.0 10.0 10.0 dtype: float64
Вы также можете интерполировать с помощью DataFrame:
In [65]: df = pd.DataFrame({'A': [1, 2.1, np.nan, 4.7, 5.6, 6.8],
....: 'B': [.25, np.nan, np.nan, 4, 12.2, 14.4]})
....:
In [66]: df
Out[66]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [67]: df.interpolate()
Out[67]:
A B
0 1.0 0.25
1 2.1 1.50
2 3.4 2.75
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
Аргумент method предоставляет доступ к более сложным методам интерполяции. Если у вас установлен scipy, вы можете передать имя 1-мерной интерполяционной функции в method. Для получения подробностей обратитесь к полной документации по интерполяции scipy и руководству по интерполяции. Соответствующий метод интерполяции будет зависеть от типа данных, с которыми вы работаете.
- Если вы работаете со временным рядом, который растет с возрастающей скоростью,
method='quadratic'может быть подходящим. - Если у вас есть значения, приближающиеся к кумулятивной функции распределения, тогда
method='pchip'будет работать хорошо. - Для заполнения пропущенных значений с целью плавного построения графиков используйте
method='akima'.
Предупреждение
Эти методы требуют scipy.
In [68]: df.interpolate(method='barycentric')
Out[68]:
A B
0 1.00 0.250
1 2.10 -7.660
2 3.53 -4.515
3 4.70 4.000
4 5.60 12.200
5 6.80 14.400
In [69]: df.interpolate(method='pchip')
Out[69]:
A B
0 1.00000 0.250000
1 2.10000 0.672808
2 3.43454 1.928950
3 4.70000 4.000000
4 5.60000 12.200000
5 6.80000 14.400000
In [70]: df.interpolate(method='akima')
Out[70]:
A B
0 1.000000 0.250000
1 2.100000 -0.873316
2 3.406667 0.320034
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
При интерполяции с помощью полиномиального или сплайнового приближения также необходимо указать степень или порядок приближения:
In [71]: df.interpolate(method='spline', order=2)
Out[71]:
A B
0 1.000000 0.250000
1 2.100000 -0.428598
2 3.404545 1.206900
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
In [72]: df.interpolate(method='polynomial', order=2)
Out[72]:
A B
0 1.000000 0.250000
1 2.100000 -4.161538
2 3.547059 -2.911538
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
Сравните несколько методов:
In [73]: np.random.seed(2)
In [74]: ser = pd.Series(np.arange(1, 10.1, .25)**2 + np.random.randn(37))
In [75]: bad = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
In [76]: ser[bad] = np.nan
In [77]: methods = ['linear', 'quadratic', 'cubic']
In [78]: df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})
In [79]: df.plot()
Out[79]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff2666771d0>
Другой пример использования — интерполяция в новых значениях. Предположим, у вас есть 100 наблюдений из некоторого распределения. И предположим, что вас особенно интересует, что происходит в середине. Вы можете объединить методы pandas reindex и interpolate для интерполяции в новых значениях.
In [80]: ser = pd.Series(np.sort(np.random.uniform(size=100))) # interpolate at new_index In [81]: new_index = ser.index | pd.Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]) In [82]: interp_s = ser.reindex(new_index).interpolate(method='pchip') In [83]: interp_s[49:51] Out[83]: 49.00 0.471410 49.25 0.476841 49.50 0.481780 49.75 0.485998 50.00 0.489266 50.25 0.491814 50.50 0.493995 50.75 0.495763 51.00 0.497074 dtype: float64
Пределы интерполяции
Как и другие методы заполнения pandas, interpolate принимает ключевой аргумент limit. Используйте этот аргумент для ограничения количества последовательных интерполяций, сохраняя значения NaN для интерполяций, которые слишком далеки от последнего действительного наблюдения:
In [84]: ser = pd.Series([np.nan, np.nan, 5, np.nan, np.nan, np.nan, 13]) In [85]: ser.interpolate(limit=2) Out[85]: 0 NaN 1 NaN 2 5.0 3 7.0 4 9.0 5 NaN 6 13.0 dtype: float64
По умолчанию, limit применяется в прямом направлении, так что значения NaN после значения, отличного от NaN, могут быть заполнены. Если вы предоставите 'backward' или 'both' для аргумента ключевого слова limit_direction, вы можете заполнить значения NaN перед значениями, отличными от NaN, или и перед, и после значений, отличных от NaN соответственно:
In [86]: ser.interpolate(limit=1) # limit_direction == 'forward' Out[86]: 0 NaN 1 NaN 2 5.0 3 7.0 4 NaN 5 NaN 6 13.0 dtype: float64 In [87]: ser.interpolate(limit=1, limit_direction='backward') Out[87]: 0 NaN 1 5.0 2 5.0 3 NaN 4 NaN 5 11.0 6 13.0 dtype: float64 In [88]: ser.interpolate(limit=1, limit_direction='both') Out[88]: 0 NaN 1 5.0 2 5.0 3 7.0 4 NaN 5 11.0 6 13.0 dtype: float64
Замена общих значений
Часто мы хотим заменить произвольные значения на другие значения. Новым в версии v0.8 является метод replace в Series/DataFrame, который предоставляет эффективный и гибкий способ выполнения таких замен.
Для Series вы можете заменить одно значение или список значений другим значением:
In [89]: ser = pd.Series([0., 1., 2., 3., 4.]) In [90]: ser.replace(0, 5) Out[90]: 0 5.0 1 1.0 2 2.0 3 3.0 4 4.0 dtype: float64
Вы можете заменить список значений списком других значений:
In [91]: ser.replace([0, 1, 2, 3, 4], [4, 3, 2, 1, 0]) Out[91]: 0 4.0 1 3.0 2 2.0 3 1.0 4 0.0 dtype: float64
Вы также можете указать словарь сопоставлений:
In [92]: ser.replace({0: 10, 1: 100})
Out[92]:
0 10.0
1 100.0
2 2.0
3 3.0
4 4.0
dtype: float64
Для DataFrame вы можете указать отдельные значения по столбцу:
In [93]: df = pd.DataFrame({'a': [0, 1, 2, 3, 4], 'b': [5, 6, 7, 8, 9]})
In [94]: df.replace({'a': 0, 'b': 5}, 100)
Out[94]:
a b
0 100 100
1 1 6
2 2 7
3 3 8
4 4 9
Вместо замены указанными значениями, вы можете рассматривать все заданные значения как пропущенные и выполнять интерполяцию по ним:
In [95]: ser.replace([1, 2, 3], method='pad') Out[95]: 0 0.0 1 0.0 2 0.0 3 0.0 4 4.0 dtype: float64
Замена строк/регулярных выражений
Примечание
Строки Python, начинающиеся с символа r, такие как r'hello world', являются так называемыми "сырыми" строками. Они имеют другую семантику по отношению к обратным слэшам, чем строки без этого префикса. Обратные слэши в сырых строках будут интерпретироваться как экранированный обратный слэш, например, r'\' == '\\'. Вы должны прочитать об этом, если это неясно.
Замените ‘.’ на nan (str -> str)
In [96]: d = {'a': list(range(4)), 'b': list('ab..'), 'c': ['a', 'b', np.nan, 'd']}
In [97]: df = pd.DataFrame(d)
In [98]: df.replace('.', np.nan)
Out[98]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
Теперь выполните это с регулярным выражением, удаляющим окружающие пробелы (regex -> regex)
In [99]: df.replace(r'\s*\.\s*', np.nan, regex=True) Out[99]: a b c 0 0 a a 1 1 b b 2 2 NaN NaN 3 3 NaN d
Замените несколько различных значений (список -> список)
In [100]: df.replace(['a', '.'], ['b', np.nan]) Out[100]: a b c 0 0 b b 1 1 b b 2 2 NaN NaN 3 3 NaN d
список regex -> список regex
In [101]: df.replace([r'\.', r'(a)'], ['dot', '\1stuff'], regex=True) Out[101]: a b c 0 0 stuff stuff 1 1 b b 2 2 dot NaN 3 3 dot d
Искать только в столбце 'b' (словарь -> словарь)
In [102]: df.replace({'b': '.'}, {'b': np.nan})
Out[102]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
То же, что и в предыдущем примере, но использовать регулярное выражение для поиска вместо (словарь regex -> словарь)
In [103]: df.replace({'b': r'\s*\.\s*'}, {'b': np.nan}, regex=True)
Out[103]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
Вы можете передать вложенные словари регулярных выражений, которые используют regex=True
In [104]: df.replace({'b': {'b': r''}}, regex=True)
Out[104]:
a b c
0 0 a a
1 1 b
2 2 . NaN
3 3 . d
или вы можете передать вложенный словарь таким образом
In [105]: df.replace(regex={'b': {r'\s*\.\s*': np.nan}})
Out[105]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
Вы также можете использовать группу совпадения регулярного выражения при замене (словарь regex -> словарь regex), это работает и для списков
In [106]: df.replace({'b': r'\s*(\.)\s*'}, {'b': r'\1ty'}, regex=True)
Out[106]:
a b c
0 0 a a
1 1 b b
2 2 .ty NaN
3 3 .ty d
Вы можете передать список регулярных выражений, которые при совпадении будут заменены скаляром (список regex -> regex)
In [107]: df.replace([r'\s*\.\s*', r'a|b'], np.nan, regex=True) Out[107]: a b c 0 0 NaN NaN 1 1 NaN NaN 2 2 NaN NaN 3 3 NaN d
Все примеры с регулярными выражениями также могут быть переданы с аргументом to_replace в качестве аргумента regex. В этом случае аргумент value должен быть явно передан по имени, или regex должен быть вложенным словарем. В этом случае предыдущий пример будет выглядеть следующим образом:
In [108]: df.replace(regex=[r'\s*\.\s*', r'a|b'], value=np.nan) Out[108]: a b c 0 0 NaN NaN 1 1 NaN NaN 2 2 NaN NaN 3 3 NaN d
Это может быть удобно, если вы не хотите передавать regex=True каждый раз, когда хотите использовать регулярное выражение.
Примечание
В примерах выше, где вы видите регулярное выражение, допустимо и скомпилированное регулярное выражение.
Числовая замена
Аналогично DataFrame.fillna
In [109]: df = pd.DataFrame(np.random.randn(10, 2))
In [110]: df[np.random.rand(df.shape[0]) > 0.5] = 1.5
In [111]: df.replace(1.5, np.nan)
Out[111]:
0 1
0 -0.844214 -1.021415
1 0.432396 -0.323580
2 0.423825 0.799180
3 1.262614 0.751965
4 NaN NaN
5 NaN NaN
6 -0.498174 -1.060799
7 0.591667 -0.183257
8 1.019855 -1.482465
9 NaN NaN
Замена нескольких значений с помощью списков также работает
In [112]: df00 = df.values[0, 0]
In [113]: df.replace([1.5, df00], [np.nan, 'a'])
Out[113]:
0 1
0 a -1.021415
1 0.432396 -0.323580
2 0.423825 0.799180
3 1.26261 0.751965
4 NaN NaN
5 NaN NaN
6 -0.498174 -1.060799
7 0.591667 -0.183257
8 1.01985 -1.482465
9 NaN NaN
In [114]: df[1].dtype
Out[114]: dtype('float64')
Вы также можете работать с DataFrame на месте
In [115]: df.replace(1.5, np.nan, inplace=True)
Предупреждение
При замене нескольких bool или datetime64 объектов, первый аргумент к replace (to_replace) должен соответствовать типу заменяемого значения. Например,
s = pd.Series([True, False, True])
s.replace({'a string': 'new value', True: False}) # raises
TypeError: Cannot compare types 'ndarray(dtype=bool)' and 'str'
вызовет TypeError, потому что один из dict ключей не имеет правильного типа для замены.
Однако при замене одного объекта, такого как,
In [116]: s = pd.Series([True, False, True])
In [117]: s.replace('a string', 'another string')
Out[117]:
0 True
1 False
2 True
dtype: bool
исходный объект NDFrame вернется без изменений. Мы работаем над унификацией этого API, но по причинам обратной совместимости мы не можем нарушить последнее поведение. Подробнее см. GH6354.
Правила приведения типов и индексации пропущенных данных
Хотя pandas поддерживает хранение массивов целого и булевого типов, эти типы не могут хранить пропущенные данные. Пока мы не сможем перейти к использованию родного типа NA в NumPy, мы установили некоторые «правила приведения типов» при повторной индексации, которые могут вводить пропущенные данные, например, в Series или DataFrame. Вот они:
| тип данных | Приведение к |
|---|---|
| целый | вещественный |
| булевый | объект |
| вещественный | без приведения |
| объект | без приведения |
Например:
In [118]: s = pd.Series(np.random.randn(5), index=[0, 2, 4, 6, 7])
In [119]: s > 0
Out[119]:
0 True
2 True
4 True
6 True
7 True
dtype: bool
In [120]: (s > 0).dtype
Out[120]: dtype('bool')
In [121]: crit = (s > 0).reindex(list(range(8)))
In [122]: crit
Out[122]:
0 True
1 NaN
2 True
3 NaN
4 True
5 NaN
6 True
7 True
dtype: object
In [123]: crit.dtype
Out[123]: dtype('O')
Обычно NumPy будет жаловаться, если вы попытаетесь использовать массив объектов (даже если он содержит булевы значения) вместо булевого массива для получения или установки значений из ndarray (например, для выбора значений на основе определенных критериев). Если булевый вектор содержит значения NA, будет сгенерировано исключение:
In [124]: reindexed = s.reindex(list(range(8))).fillna(0)
In [125]: reindexed[crit]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-125-2da204ed1ac7> in <module>()
----> 1 reindexed[crit]
/home/joris/scipy/pandas/pandas/core/series.pyc in __getitem__(self, key)
639 key = list(key)
640
--> 641 if com.is_bool_indexer(key):
642 key = check_bool_indexer(self.index, key)
643
/home/joris/scipy/pandas/pandas/core/common.pyc in is_bool_indexer(key)
199 if not lib.is_bool_array(key):
200 if isnull(key).any():
--> 201 raise ValueError('cannot index with vector containing '
202 'NA / NaN values')
203 return False
ValueError: cannot index with vector containing NA / NaN values
Однако их можно заполнить с помощью fillna, и это будет работать нормально:
In [126]: reindexed[crit.fillna(False)] Out[126]: 0 0.126504 2 0.696198 4 0.697416 6 0.601516 7 0.003659 dtype: float64 In [127]: reindexed[crit.fillna(True)] Out[127]: 0 0.126504 1 0.000000 2 0.696198 3 0.000000 4 0.697416 5 0.000000 6 0.601516 7 0.003659 dtype: float64
© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/missing_data.html