Spec-Zone.ru › pandas 2

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

Значения, считающиеся отсутствующими

pandas использует разные значения-маркеры для представления отсутствующих (также обозначаемых как NA) данных в зависимости от типа данных.

numpy.nan для типов данных NumPy. Недостатком использования типов данных NumPy является то, что исходный тип данных будет приведён к np.float64 или object.

In [1]: pd.Series([1, 2], dtype=np.int64).reindex([0, 1, 2])
Out[1]: 
0    1.0
1    2.0
2    NaN
dtype: float64

In [2]: pd.Series([True, False], dtype=np.bool_).reindex([0, 1, 2])
Out[2]: 
0     True
1    False
2      NaN
dtype: object

NaT для типов данных NumPy np.datetime64, np.timedelta64, и PeriodDtype. Для приложений типизации используйте api.types.NaTType.

In [3]: pd.Series([1, 2], dtype=np.dtype("timedelta64[ns]")).reindex([0, 1, 2])
Out[3]: 
0   0 days 00:00:00.000000001
1   0 days 00:00:00.000000002
2                         NaT
dtype: timedelta64[ns]

In [4]: pd.Series([1, 2], dtype=np.dtype("datetime64[ns]")).reindex([0, 1, 2])
Out[4]: 
0   1970-01-01 00:00:00.000000001
1   1970-01-01 00:00:00.000000002
2                             NaT
dtype: datetime64[ns]

In [5]: pd.Series(["2020", "2020"], dtype=pd.PeriodDtype("D")).reindex([0, 1, 2])
Out[5]: 
0    2020-01-01
1    2020-01-01
2           NaT
dtype: period[D]

NA для StringDtype, Int64Dtype (и других разрядов ширины), Float64Dtype`(and other bit widths), :class:`BooleanDtype и ArrowDtype. Эти типы сохранят исходный тип данных. Для приложений типизации используйте api.types.NAType.

In [6]: pd.Series([1, 2], dtype="Int64").reindex([0, 1, 2])
Out[6]: 
0       1
1       2
2    <NA>
dtype: Int64

In [7]: pd.Series([True, False], dtype="boolean[pyarrow]").reindex([0, 1, 2])
Out[7]: 
0     True
1    False
2     <NA>
dtype: bool[pyarrow]

Для обнаружения этих отсутствующих значений используйте методы isna() или notna().

In [8]: ser = pd.Series([pd.Timestamp("2020-01-01"), pd.NaT])

In [9]: ser
Out[9]: 
0   2020-01-01
1          NaT
dtype: datetime64[ns]

In [10]: pd.isna(ser)
Out[10]: 
0    False
1     True
dtype: bool

Примечание

isna() или notna() также будут рассматривать None как отсутствующее значение.

In [11]: ser = pd.Series([1, None], dtype=object)

In [12]: ser
Out[12]: 
0       1
1    None
dtype: object

In [13]: pd.isna(ser)
Out[13]: 
0    False
1     True
dtype: bool

Предупреждение

Сравнения на равенство между np.nan, NaT и NA не ведут себя как None

In [14]: None == None  # noqa: E711
Out[14]: True

In [15]: np.nan == np.nan
Out[15]: False

In [16]: pd.NaT == pd.NaT
Out[16]: False

In [17]: pd.NA == pd.NA
Out[17]: <NA>

Поэтому сравнение на равенство между DataFrame или Series с одним из этих отсутствующих значений не предоставляет той же информации, что и isna() или notna().

In [18]: ser = pd.Series([True, None], dtype="boolean[pyarrow]")

In [19]: ser == pd.NA
Out[19]: 
0    <NA>
1    <NA>
dtype: bool[pyarrow]

In [20]: pd.isna(ser)
Out[20]: 
0    False
1     True
dtype: bool

NA семантика

Предупреждение

Экспериментально: поведение NA` может измениться без предупреждения.

Начиная с pandas 1.0, доступен экспериментальный NA (синглтон) для представления скалярных пропущенных значений. Цель NA — предоставить индикатор «пропуска» (missing), который можно последовательно использовать в различных типах данных (вместо np.nan, None или pd.NaT в зависимости от типа данных).

Например, при наличии пропущенных значений в Series с типом данных nullable integer, будет использоваться NA:

In [21]: s = pd.Series([1, 2, None], dtype="Int64")

In [22]: s
Out[22]: 
0       1
1       2
2    <NA>
dtype: Int64

In [23]: s[2]
Out[23]: <NA>

In [24]: s[2] is pd.NA
Out[24]: True

В настоящее время pandas по умолчанию не использует эти типы данных с NA в DataFrame или Series, поэтому вам нужно явно указать тип данных. Легкий способ преобразования в эти типы данных описан в разделе преобразования.

Распространение в арифметических и сравнительных операциях

В целом, пропущенные значения распространяются в операциях, включающих NA. Когда один из операндов неизвестен, результат операции также неизвестен.

Например, NA распространяется в арифметических операциях, аналогично np.nan:

In [25]: pd.NA + 1
Out[25]: <NA>

In [26]: "a" * pd.NA
Out[26]: <NA>

Существуют некоторые частные случаи, когда результат известен, даже если один из операндов NA.

In [27]: pd.NA ** 0
Out[27]: 1

In [28]: 1 ** pd.NA
Out[28]: 1

В операциях сравнения и равенства NA также распространяется. Это отличается от поведения np.nan, где сравнения с np.nan всегда возвращают False.

In [29]: pd.NA == 1
Out[29]: <NA>

In [30]: pd.NA == pd.NA
Out[30]: <NA>

In [31]: pd.NA < 2.5
Out[31]: <NA>

Чтобы проверить, равно ли значение NA, используйте isna()

In [32]: pd.isna(pd.NA)
Out[32]: True

Примечание

Исключение из этого основного правила распространения — редукции (такие как среднее значение или минимум), где pandas по умолчанию пропускает пропущенные значения. Дополнительная информация приведена в разделе вычислений.

Логические операции

Для логических операций NA следуют правилам трехзначной логики (или логики Клини, аналогично R, SQL и Julia). Эта логика означает распространение пропущенных значений только когда это логически необходимо.

Например, для логической операции «или» (|), если один из операндов True, то результат уже известен и равен True, независимо от другого значения (также независимо от того, является ли пропущенное значение True или False). В этом случае NA не распространяется:

In [33]: True | False
Out[33]: True

In [34]: True | pd.NA
Out[34]: True

In [35]: pd.NA | True
Out[35]: True

С другой стороны, если один из операндов False, результат зависит от значения другого операнда. Поэтому в этом случае NA распространяется:

In [36]: False | True
Out[36]: True

In [37]: False | False
Out[37]: False

In [38]: False | pd.NA
Out[38]: <NA>

Поведение логической операции «и» (&) можно вывести, используя аналогичную логику (теперь NA не будет распространяться, если один из операндов уже False):

In [39]: False & True
Out[39]: False

In [40]: False & False
Out[40]: False

In [41]: False & pd.NA
Out[41]: False
In [42]: True & True
Out[42]: True

In [43]: True & False
Out[43]: False

In [44]: True & pd.NA
Out[44]: <NA>

NA в контексте булевых значений

Так как фактическое значение NA неизвестно, то неявное преобразование NA в булевое значение неоднозначно.

In [45]: bool(pd.NA)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[45], line 1
----> 1 bool(pd.NA)

File missing.pyx:392, in pandas._libs.missing.NAType.__bool__()

TypeError: boolean value of NA is ambiguous

Это также означает, что NA не может быть использовано в контексте, где оно оценивается как булево, таком как if condition: ..., где condition потенциально может быть NA. В таких случаях isna() можно использовать для проверки на NA или condition являющимся NA. Это можно избежать, например, предварительно заполнив пропущенные значения.

Аналогичная ситуация возникает при использовании объектов Series или DataFrame в if операторах, см. Использование операторов if/truth с pandas.

NumPy ufuncs

pandas.NA реализует протокол NumPy __array_ufunc__. Большинство ufuncs работают с NA, и обычно возвращают NA:

In [46]: np.log(pd.NA)
Out[46]: <NA>

In [47]: np.add(pd.NA, 1)
Out[47]: <NA>

Предупреждение

В настоящее время ufuncs, включающие ndarray и NA будут возвращать объект-тип, заполненный значениями NA.

In [48]: a = np.array([1, 2, 3])

In [49]: np.greater(a, pd.NA)
Out[49]: array([<NA>, <NA>, <NA>], dtype=object)

Тип возвращаемого значения может быть изменён в будущем.

См. Взаимодействие DataFrame с функциями NumPy для получения дополнительной информации о ufuncs.

Преобразование

Если у вас есть DataFrame или Series с использованием np.nan, Series.convert_dtypes() и DataFrame.convert_dtypes() в DataFrame, которые могут преобразовать данные для использования типов данных, использующих NA, таких как Int64Dtype или ArrowDtype. Это особенно полезно после чтения наборов данных из методов ввода-вывода, где типы данных были определены автоматически.

В этом примере, хотя типы данных всех столбцов меняются, мы показываем результаты для первых 10 столбцов.

In [50]: import io

In [51]: data = io.StringIO("a,b\n,True\n2,")

In [52]: df = pd.read_csv(data)

In [53]: df.dtypes
Out[53]: 
a    float64
b     object
dtype: object

In [54]: df_conv = df.convert_dtypes()

In [55]: df_conv
Out[55]: 
      a     b
0  <NA>  True
1     2  <NA>

In [56]: df_conv.dtypes
Out[56]: 
a      Int64
b    boolean
dtype: object

Вставка пропущенных данных

Вы можете вставить пропущенные значения, просто присвоив значение Series или DataFrame. Значение, используемое для пропущенных значений, будет выбрано на основе типа данных.

In [57]: ser = pd.Series([1., 2., 3.])

In [58]: ser.loc[0] = None

In [59]: ser
Out[59]: 
0    NaN
1    2.0
2    3.0
dtype: float64

In [60]: ser = pd.Series([pd.Timestamp("2021"), pd.Timestamp("2021")])

In [61]: ser.iloc[0] = np.nan

In [62]: ser
Out[62]: 
0          NaT
1   2021-01-01
dtype: datetime64[ns]

In [63]: ser = pd.Series([True, False], dtype="boolean[pyarrow]")

In [64]: ser.iloc[0] = None

In [65]: ser
Out[65]: 
0     <NA>
1    False
dtype: bool[pyarrow]

Для object типов pandas будет использовать заданное значение:

In [66]: s = pd.Series(["a", "b", "c"], dtype=object)

In [67]: s.loc[0] = None

In [68]: s.loc[1] = np.nan

In [69]: s
Out[69]: 
0    None
1     NaN
2       c
dtype: object

Вычисления с отсутствующими данными

Отсутствующие значения передаются через арифметические операции между объектами pandas.

In [70]: ser1 = pd.Series([np.nan, np.nan, 2, 3])

In [71]: ser2 = pd.Series([np.nan, 1, np.nan, 4])

In [72]: ser1
Out[72]: 
0    NaN
1    NaN
2    2.0
3    3.0
dtype: float64

In [73]: ser2
Out[73]: 
0    NaN
1    1.0
2    NaN
3    4.0
dtype: float64

In [74]: ser1 + ser2
Out[74]: 
0    NaN
1    NaN
2    NaN
3    7.0
dtype: float64

Дескриптивные статистики и вычислительные методы, обсуждаемые в обзоре структуры данных (и перечисленные в здесь и здесь), учитывают пропущенные данные.

При суммировании данных значения NA или пустые данные будут обрабатываться как ноль.

In [75]: pd.Series([np.nan]).sum()
Out[75]: 0.0

In [76]: pd.Series([], dtype="float64").sum()
Out[76]: 0.0

При умножении значения NA или пустые данные будут обрабатываться как 1.

In [77]: pd.Series([np.nan]).prod()
Out[77]: 1.0

In [78]: pd.Series([], dtype="float64").prod()
Out[78]: 1.0

Кумулятивные методы, такие как cumsum() и cumprod(), по умолчанию игнорируют значения NA, но сохраняют их в результате. Это поведение можно изменить с помощью skipna

  • Кумулятивные методы, такие как cumsum() и cumprod(), по умолчанию игнорируют значения NA, но сохраняют их в результирующих массивах. Чтобы переопределить это поведение и включить значения NA, используйте skipna=False.

In [79]: ser = pd.Series([1, np.nan, 3, np.nan])

In [80]: ser
Out[80]: 
0    1.0
1    NaN
2    3.0
3    NaN
dtype: float64

In [81]: ser.cumsum()
Out[81]: 
0    1.0
1    NaN
2    4.0
3    NaN
dtype: float64

In [82]: ser.cumsum(skipna=False)
Out[82]: 
0    1.0
1    NaN
2    NaN
3    NaN
dtype: float64

Удаление пропущенных данных

dropna() удаляет строки или столбцы с отсутствующими данными.

In [83]: df = pd.DataFrame([[np.nan, 1, 2], [1, 2, np.nan], [1, 2, 3]])

In [84]: df
Out[84]: 
     0  1    2
0  NaN  1  2.0
1  1.0  2  NaN
2  1.0  2  3.0

In [85]: df.dropna()
Out[85]: 
     0  1    2
2  1.0  2  3.0

In [86]: df.dropna(axis=1)
Out[86]: 
   1
0  1
1  2
2  2

In [87]: ser = pd.Series([1, pd.NA], dtype="int64[pyarrow]")

In [88]: ser.dropna()
Out[88]: 
0    1
dtype: int64[pyarrow]

Заполнение отсутствующих данных

Заполнение по значению

fillna() заменяет значения NA на данные, отличные от NA.

Замена NA на скалярное значение

In [89]: data = {"np": [1.0, np.nan, np.nan, 2], "arrow": pd.array([1.0, pd.NA, pd.NA, 2], dtype="float64[pyarrow]")}

In [90]: df = pd.DataFrame(data)

In [91]: df
Out[91]: 
    np  arrow
0  1.0    1.0
1  NaN   <NA>
2  NaN   <NA>
3  2.0    2.0

In [92]: df.fillna(0)
Out[92]: 
    np  arrow
0  1.0    1.0
1  0.0    0.0
2  0.0    0.0
3  2.0    2.0

Заполнение пропусков вперед или назад

In [93]: df.ffill()
Out[93]: 
    np  arrow
0  1.0    1.0
1  1.0    1.0
2  1.0    1.0
3  2.0    2.0

In [94]: df.bfill()
Out[94]: 
    np  arrow
0  1.0    1.0
1  2.0    2.0
2  2.0    2.0
3  2.0    2.0

Ограничение количества заполненных значений NA

In [95]: df.ffill(limit=1)
Out[95]: 
    np  arrow
0  1.0    1.0
1  1.0    1.0
2  NaN   <NA>
3  2.0    2.0

Значения NA можно заменить соответствующим значением из Series или DataFrame, где индекс и столбец совпадают между исходным объектом и заполненным объектом.

In [96]: dff = pd.DataFrame(np.arange(30, dtype=np.float64).reshape(10, 3), columns=list("ABC"))

In [97]: dff.iloc[3:5, 0] = np.nan

In [98]: dff.iloc[4:6, 1] = np.nan

In [99]: dff.iloc[5:8, 2] = np.nan

In [100]: dff
Out[100]: 
      A     B     C
0   0.0   1.0   2.0
1   3.0   4.0   5.0
2   6.0   7.0   8.0
3   NaN  10.0  11.0
4   NaN   NaN  14.0
5  15.0   NaN   NaN
6  18.0  19.0   NaN
7  21.0  22.0   NaN
8  24.0  25.0  26.0
9  27.0  28.0  29.0

In [101]: dff.fillna(dff.mean())
Out[101]: 
       A     B          C
0   0.00   1.0   2.000000
1   3.00   4.0   5.000000
2   6.00   7.0   8.000000
3  14.25  10.0  11.000000
4  14.25  14.5  14.000000
5  15.00  14.5  13.571429
6  18.00  19.0  13.571429
7  21.00  22.0  13.571429
8  24.00  25.0  26.000000
9  27.00  28.0  29.000000

Примечание

DataFrame.where() также можно использовать для заполнения значений NA. Результат аналогичен вышеописанному.

In [102]: dff.where(pd.notna(dff), dff.mean(), axis="columns")
Out[102]: 
       A     B          C
0   0.00   1.0   2.000000
1   3.00   4.0   5.000000
2   6.00   7.0   8.000000
3  14.25  10.0  11.000000
4  14.25  14.5  14.000000
5  15.00  14.5  13.571429
6  18.00  19.0  13.571429
7  21.00  22.0  13.571429
8  24.00  25.0  26.000000
9  27.00  28.0  29.000000

Интерполяция

DataFrame.interpolate() и Series.interpolate() заполняют значения NA с помощью различных методов интерполяции.

In [103]: df = pd.DataFrame(
   .....:     {
   .....:         "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
   .....:         "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
   .....:     }
   .....: )
   .....: 

In [104]: df
Out[104]: 
     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 [105]: df.interpolate()
Out[105]: 
     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

In [106]: idx = pd.date_range("2020-01-01", periods=10, freq="D")

In [107]: data = np.random.default_rng(2).integers(0, 10, 10).astype(np.float64)

In [108]: ts = pd.Series(data, index=idx)

In [109]: ts.iloc[[1, 2, 5, 6, 9]] = np.nan

In [110]: ts
Out[110]: 
2020-01-01    8.0
2020-01-02    NaN
2020-01-03    NaN
2020-01-04    2.0
2020-01-05    4.0
2020-01-06    NaN
2020-01-07    NaN
2020-01-08    0.0
2020-01-09    3.0
2020-01-10    NaN
Freq: D, dtype: float64

In [111]: ts.plot()
Out[111]: <Axes: >
../_images/series_before_interpolate.png
In [112]: ts.interpolate()
Out[112]: 
2020-01-01    8.000000
2020-01-02    6.000000
2020-01-03    4.000000
2020-01-04    2.000000
2020-01-05    4.000000
2020-01-06    2.666667
2020-01-07    1.333333
2020-01-08    0.000000
2020-01-09    3.000000
2020-01-10    3.000000
Freq: D, dtype: float64

In [113]: ts.interpolate().plot()
Out[113]: <Axes: >
../_images/series_interpolate.png

Интерполяция, относящаяся к Timestamp в DatetimeIndex, доступна путем установки method="time"

In [114]: ts2 = ts.iloc[[0, 1, 3, 7, 9]]

In [115]: ts2
Out[115]: 
2020-01-01    8.0
2020-01-02    NaN
2020-01-04    2.0
2020-01-08    0.0
2020-01-10    NaN
dtype: float64

In [116]: ts2.interpolate()
Out[116]: 
2020-01-01    8.0
2020-01-02    5.0
2020-01-04    2.0
2020-01-08    0.0
2020-01-10    0.0
dtype: float64

In [117]: ts2.interpolate(method="time")
Out[117]: 
2020-01-01    8.0
2020-01-02    6.0
2020-01-04    2.0
2020-01-08    0.0
2020-01-10    0.0
dtype: float64

Для плавающего индекса используйте method='values':

In [118]: idx = [0.0, 1.0, 10.0]

In [119]: ser = pd.Series([0.0, np.nan, 10.0], idx)

In [120]: ser
Out[120]: 
0.0      0.0
1.0      NaN
10.0    10.0
dtype: float64

In [121]: ser.interpolate()
Out[121]: 
0.0      0.0
1.0      5.0
10.0    10.0
dtype: float64

In [122]: ser.interpolate(method="values")
Out[122]: 
0.0      0.0
1.0      1.0
10.0    10.0
dtype: float64

Если у вас установлен scipy, вы можете передать имя 1-мерной процедуры интерполяции в method. как указано в документации по интерполяции scipy документация и руководстве пособие. Соответствующий метод интерполяции будет зависеть от типа данных.

Совет

Если вы работаете с временным рядом, который растет с возрастающей скоростью, используйте method='barycentric'.

Если у вас есть значения, приближающиеся к кумулятивной функции распределения, используйте method='pchip'.

Для заполнения пропущенных значений с целью плавного построения графиков используйте method='akima'.

In [123]: df = pd.DataFrame(
   .....:    {
   .....:       "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
   .....:       "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
   .....:    }
   .....: )
   .....: 

In [124]: df
Out[124]: 
     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 [125]: df.interpolate(method="barycentric")
Out[125]: 
      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 [126]: df.interpolate(method="pchip")
Out[126]: 
         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 [127]: df.interpolate(method="akima")
Out[127]: 
          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 [128]: df.interpolate(method="spline", order=2)
Out[128]: 
          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 [129]: df.interpolate(method="polynomial", order=2)
Out[129]: 
          A          B
0  1.000000   0.250000
1  2.100000  -2.703846
2  3.451351  -1.453846
3  4.700000   4.000000
4  5.600000  12.200000
5  6.800000  14.400000

Сравнение нескольких методов.

In [130]: np.random.seed(2)

In [131]: ser = pd.Series(np.arange(1, 10.1, 0.25) ** 2 + np.random.randn(37))

In [132]: missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])

In [133]: ser.iloc[missing] = np.nan

In [134]: methods = ["linear", "quadratic", "cubic"]

In [135]: df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})

In [136]: df.plot()
Out[136]: <Axes: >
../_images/compare_interpolations.png

Интерполяция новых наблюдений из расширяемых данных с помощью Series.reindex().

In [137]: ser = pd.Series(np.sort(np.random.uniform(size=100)))

# interpolate at new_index
In [138]: new_index = ser.index.union(pd.Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]))

In [139]: interp_s = ser.reindex(new_index).interpolate(method="pchip")

In [140]: interp_s.loc[49:51]
Out[140]: 
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

Пределы интерполяции

interpolate() принимает ключевое слово limit для ограничения количества последовательных NaN значений, заполненных с момента последнего действительного наблюдения.

In [141]: ser = pd.Series([np.nan, np.nan, 5, np.nan, np.nan, np.nan, 13, np.nan, np.nan])

In [142]: ser
Out[142]: 
0     NaN
1     NaN
2     5.0
3     NaN
4     NaN
5     NaN
6    13.0
7     NaN
8     NaN
dtype: float64

In [143]: ser.interpolate()
Out[143]: 
0     NaN
1     NaN
2     5.0
3     7.0
4     9.0
5    11.0
6    13.0
7    13.0
8    13.0
dtype: float64

In [144]: ser.interpolate(limit=1)
Out[144]: 
0     NaN
1     NaN
2     5.0
3     7.0
4     NaN
5     NaN
6    13.0
7    13.0
8     NaN
dtype: float64

По умолчанию, значения NaN заполняются в forward направлении. Используйте параметр limit_direction для заполнения backward или из both направлений.

In [145]: ser.interpolate(limit=1, limit_direction="backward")
Out[145]: 
0     NaN
1     5.0
2     5.0
3     NaN
4     NaN
5    11.0
6    13.0
7     NaN
8     NaN
dtype: float64

In [146]: ser.interpolate(limit=1, limit_direction="both")
Out[146]: 
0     NaN
1     5.0
2     5.0
3     7.0
4     NaN
5    11.0
6    13.0
7    13.0
8     NaN
dtype: float64

In [147]: ser.interpolate(limit_direction="both")
Out[147]: 
0     5.0
1     5.0
2     5.0
3     7.0
4     9.0
5    11.0
6    13.0
7    13.0
8    13.0
dtype: float64

По умолчанию значения NaN заполняются, независимо от того, окружены ли они существующими действительными значениями или находятся вне существующих действительных значений. Параметр limit_area ограничивает заполнение значениями внутри или вне значений.

# fill one consecutive inside value in both directions
In [148]: ser.interpolate(limit_direction="both", limit_area="inside", limit=1)
Out[148]: 
0     NaN
1     NaN
2     5.0
3     7.0
4     NaN
5    11.0
6    13.0
7     NaN
8     NaN
dtype: float64

# fill all consecutive outside values backward
In [149]: ser.interpolate(limit_direction="backward", limit_area="outside")
Out[149]: 
0     5.0
1     5.0
2     5.0
3     NaN
4     NaN
5     NaN
6    13.0
7     NaN
8     NaN
dtype: float64

# fill all consecutive outside values in both directions
In [150]: ser.interpolate(limit_direction="both", limit_area="outside")
Out[150]: 
0     5.0
1     5.0
2     5.0
3     NaN
4     NaN
5     NaN
6    13.0
7    13.0
8    13.0
dtype: float64

Замена значений

Series.replace() и DataFrame.replace() можно использовать аналогично Series.fillna() и DataFrame.fillna() для замены или вставки пропущенных значений.

In [151]: df = pd.DataFrame(np.eye(3))

In [152]: df
Out[152]: 
     0    1    2
0  1.0  0.0  0.0
1  0.0  1.0  0.0
2  0.0  0.0  1.0

In [153]: df_missing = df.replace(0, np.nan)

In [154]: df_missing
Out[154]: 
     0    1    2
0  1.0  NaN  NaN
1  NaN  1.0  NaN
2  NaN  NaN  1.0

In [155]: df_filled = df_missing.replace(np.nan, 2)

In [156]: df_filled
Out[156]: 
     0    1    2
0  1.0  2.0  2.0
1  2.0  1.0  2.0
2  2.0  2.0  1.0

Замена нескольких значений возможна путем передачи списка.

In [157]: df_filled.replace([1, 44], [2, 28])
Out[157]: 
     0    1    2
0  2.0  2.0  2.0
1  2.0  2.0  2.0
2  2.0  2.0  2.0

Замена с помощью отображаемого словаря.

In [158]: df_filled.replace({1: 44, 2: 28})
Out[158]: 
      0     1     2
0  44.0  28.0  28.0
1  28.0  44.0  28.0
2  28.0  28.0  44.0

Замена с использованием регулярных выражений

Примечание

Строки Python, начинающиеся с символа r, такие как r'hello world', являются “сырыми” строками. Они имеют другую семантику относительно обратных слэшей, чем строки без этого префикса. Обратные слэши в сырых строках интерпретируются как экранированный обратный слэш, например, r'\' == '\\'.

Замена ‘.’ на NaN

In [159]: d = {"a": list(range(4)), "b": list("ab.."), "c": ["a", "b", np.nan, "d"]}

In [160]: df = pd.DataFrame(d)

In [161]: df.replace(".", np.nan)
Out[161]: 
   a    b    c
0  0    a    a
1  1    b    b
2  2  NaN  NaN
3  3  NaN    d

Замена ‘.’ на NaN с помощью регулярного выражения, удаляющего окружающие пробелы

In [162]: df.replace(r"\s*\.\s*", np.nan, regex=True)
Out[162]: 
   a    b    c
0  0    a    a
1  1    b    b
2  2  NaN  NaN
3  3  NaN    d

Замена списком регулярных выражений.

In [163]: df.replace([r"\.", r"(a)"], ["dot", r"\1stuff"], regex=True)
Out[163]: 
   a       b       c
0  0  astuff  astuff
1  1       b       b
2  2     dot     NaN
3  3     dot       d

Замена с помощью регулярного выражения в отображаемом словаре.

In [164]: df.replace({"b": r"\s*\.\s*"}, {"b": np.nan}, regex=True)
Out[164]: 
   a    b    c
0  0    a    a
1  1    b    b
2  2  NaN  NaN
3  3  NaN    d

Передача вложенных словарей регулярных выражений, использующих ключевое слово regex.

In [165]: df.replace({"b": {"b": r""}}, regex=True)
Out[165]: 
   a  b    c
0  0  a    a
1  1       b
2  2  .  NaN
3  3  .    d

In [166]: df.replace(regex={"b": {r"\s*\.\s*": np.nan}})
Out[166]: 
   a    b    c
0  0    a    a
1  1    b    b
2  2  NaN  NaN
3  3  NaN    d

In [167]: df.replace({"b": r"\s*(\.)\s*"}, {"b": r"\1ty"}, regex=True)
Out[167]: 
   a    b    c
0  0    a    a
1  1    b    b
2  2  .ty  NaN
3  3  .ty    d

Передача списка регулярных выражений, которые заменят совпадения на скаляр.

In [168]: df.replace([r"\s*\.\s*", r"a|b"], "placeholder", regex=True)
Out[168]: 
   a            b            c
0  0  placeholder  placeholder
1  1  placeholder  placeholder
2  2  placeholder          NaN
3  3  placeholder            d

Все примеры регулярных выражений также можно передать с аргументом to_replace, как аргумент regex. В этом случае аргумент value должен быть явно передан по имени, или regex должен быть вложенным словарем.

In [169]: df.replace(regex=[r"\s*\.\s*", r"a|b"], value="placeholder")
Out[169]: 
   a            b            c
0  0  placeholder  placeholder
1  1  placeholder  placeholder
2  2  placeholder          NaN
3  3  placeholder            d

Примечание

Объект регулярного выражения из re.compile также является допустимым входом.

© 2008–2022, 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/2.2.2/user_guide/missing_data.html

Spec-Zone.ru

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