Есть несколько стратегий индикации пропусков в данных
Использование некоего значения-индикатора
К примеру, можно использовать число -9999 или редко встречающееся сочетание битов. Более часто встречающийся способ — условное обозначение через NaN. NaN — это специальное значение, определенное спецификацией IEEE для чисел с плавающей точкой и используется во многих ЯП.
У метода есть ограничения. Во-первых использование значений индикаторов может привести к дополнительным не оптимизированным расчетам. Во-вторых NaN доступен не для всех типов данных.
Использование масок
Можно создать отдельный булевый массив, индицирующий пропущенные значения. В ряде языков выделяется отдельный бит для разметки пропусков в массиве данных локально. Оба подхода влекут за собой перерасход памяти.
Как это реализовано в Pandas?
Pandas построена на NumPy, в котором отсутствует понятие пропуска для всех данных кроме данных с плавающей точкой. NumPy поддерживает маски, но использование такого подходжа в Pandas влечет значительные накладные расходы на хранение, вычисление и поддержку кода.
В итоге в Pandas используется:
-
индикаторы-числа
-
NaN из Numpy
-
None из Python
Объект None
None - объект python. Его нельзя использовать в NumPy и во всех производных массивах Pandas. None используется только в массивах с типом object. Когда мы создаем массив, используя None, автоматически создается массив с типом object.
Тип object означает, что NumPy не смог установить тип объектов массива, единственное что он знает — это то, что это объекты python. Операции с такими массивами будут производится на уровне языка python, т.е. со всеми накладными расходами, присущими языку с динамической типизацией. Оптимизация NumPy работать не будет.
Кроме того, функции агрегирования по массиву, например, massive.sum() или massive.min() выбросят ошибку, так как операции между численным значением и значением None не определены
Объект NaN
Объект NaN определяет отсутствие числового значения с плавающей точкой. Это вызывает некоторые проблемы — если NaN попадает в массив, все данные приводятся к числам с плавающей точкой. Кроме того, все операции с NaN приводят к NaN, в том числе и функции агрегирования.
Не забудьте, что для вызова объекта NaN нужен NumPy
0 / numpy.nan
>>> nan
Nan и None
Pandas преобразует None в NaN, если оба будут встречены в одном массиве. Естественно, осуществляется и повышающее преобразование с приведением всех непустых числовых значений к числу с плавающей точкой, а всех остальных к NaN.
pd.Series([1, np.nan, 3, None])
>>> 0 1.0
... 1 NaN
... 2 3.0
... 3 NaN
... dtype: float64
x = pd.Series([1, 2, 3], dtype='int8')
x
>>> 0 1
... 1 2
... 2 3
... dtype: int8
x[0] = None
x
>>> 0 NaN
... 1 2.0
... 2 3.0
... dtype: float64
Правила повышающих преобразований типов в Pandas (строки всегда хранятся как object)
Typeclass | Conversion When Storing NAN | NAN Sentinel Value |
---|---|---|
floating |
No change | np.nan |
object |
No change | None or np.nan |
integer |
Cast to float64 |
np.nan |
boolean |
Cast to object |
None or np.nan |
Операции над пустыми значениями
В Pandas доступно несколько методов:
-
isnull() — генерирует булеву маску для отсутствующих значений
-
notnull() — тоже для непустых
-
dropna() — фильтрация данных по отсутствующим значениям
-
fillna() — замена пропусков с возвратом копии
Методы доступны как для объектов Series так и для dataFrame (с выбором измерения).
Кроме того, для dropna() ожно задать дополнительные параметры. how=’any’ задан по дефолту, можно переопределить как ‘all’ — будут отбрасываться только полностью пустые строки/столбцы. thresh задает минимальное значение непустых значений, выше которого строки/столбцы не отбрасываются.
Для fillna() доступно несколько аргументов. method=’ffill’ и method=’bfill’ определяют какими значениями будут заполняться пропуски (предыдущими или последующими в массиве).