Spec-Zone.ru › pandas 0.18

Работа с отсутствующими данными

В этом разделе мы обсудим отсутствующие (также обозначаемые как 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>
_images/series_interpolate.pngIn [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 0x1353432d0>
_images/compare_interpolations.pngIn [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 каждый раз, когда хотите использовать регулярное выражение.

Примечание

В любых примерах 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API