Spec-Zone.ru › pandas 0.19

Работа с пропущенными данными

В этом разделе мы обсудим пропущенные (также обозначаемые как 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>
_images/series_interpolate.png

Интерполяция, учитывающая индексы, доступна через ключевое слово 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>
_images/compare_interpolations.png

Другой пример использования — интерполяция в новых значениях. Предположим, у вас есть 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

Spec-Zone.ru

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