Работа с отсутствующими данными
В этом разделе мы обсудим отсутствующие (также обозначаемые как NA) значения в pandas.
Примечание
Выбор использования NaN для обозначения отсутствующих данных в основном обусловлен простотой и производительностью. Это отличается от подхода MaskedArray, например, scikits.timeseries. Мы надеемся, что NumPy вскоре предоставит решение с родным типом NA (аналогичное R), достаточно производительное для использования в pandas.
См. справочник для некоторых продвинутых стратегий
Основы отсутствующих данных
Когда/почему данные могут отсутствовать?
Некоторые могут поспорить о нашем использовании термина «отсутствующий». Под «отсутствующим» мы просто подразумеваем нулевое значение или «отсутствующее по любой причине». Многие наборы данных просто содержат отсутствующие данные, либо потому, что они существовали, но не были собраны, либо потому, что они никогда не существовали. Например, в коллекции финансовых временных рядов некоторые временные ряды могут начинаться в разные даты. Таким образом, значения до даты начала обычно отмечаются как отсутствующие.
В 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 и мы также хотим рассматривать его как «отсутствующий» или «нулевой».
Примечание
До версии v0.10.0 inf и -inf также считались «нулевыми» в вычислениях. Это больше не так по умолчанию; используйте опцию 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
Дата и время
Для типов 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.
Для контейнеров типа «объект» 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
Дополнительную информацию см. в разделе groupby здесь.
Очистка/заполнение отсутствующих данных
Объекты 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 0x135238210>
Для плавающей точки индекса используйте 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 0x1353432d0>
Пределы интерполяции
Как и другие методы заполнения 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 каждый раз, когда хотите использовать регулярное выражение.
Примечание
В любых примерах replace выше, где вы видите регулярное выражение, допустимо и скомпилированное регулярное выражение.
Числовая замена
Аналогично 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. Вот они:
| тип данных | Приведение к |
|---|---|
| целочисленный | float |
| булевый | объект |
| float | без приведения |
| объект | без приведения |
Например:
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]
/Users/tom.augspurger/miniconda3/envs/docs/lib/python2.7/site-packages/pandas/pandas/core/series.py in __getitem__(self, key)
619 key = list(key)
620
--> 621 if is_bool_indexer(key):
622 key = check_bool_indexer(self.index, key)
623
/Users/tom.augspurger/miniconda3/envs/docs/lib/python2.7/site-packages/pandas/pandas/core/common.pyc in is_bool_indexer(key)
1208 if not lib.is_bool_array(key):
1209 if isnull(key).any():
-> 1210 raise ValueError('cannot index with vector containing '
1211 'NA / NaN values')
1212 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.18.1/missing_data.html