Изучаем pandas. Урок 4. Работа с пропусками в данных

Автор: | 21.11.2017

Очень часто большие объемы данных, которые подготавливаются для последующего анализа, имеют пропуски. Для того, чтобы можно было использовать алгоритмы машинного обучения, строящие модели по этим данным, в большинстве случаев, необходимо эти пропуски чем-то и как-то заполнить. На вопрос “чем заполнять?” мы не будем отвечать в рамках данного урока, а вот на вопрос “как заполнять?” ответим.

  1. pandas и отсутствующие данные
  2. Замена отсутствующих данных
  3. Удаление объектов/столбцов с отсутствующими данными

pandas и отсутствующие данные

Для начала, хочется сказать, что в документации по библиотеке pandas есть целый раздел, посвященный данной тематике

Для наших экспериментов создадим структуру DataFrame, которая будет содержать пропуски. Для этого импортируем необходимые нам библиотеки.

In [1]: import pandas as pd
In [2]: from io import StringIO

После этого создадим объект в формате csv. CSV – это один из наиболее простых и распространенных форматов хранения данных, в котором элементы отделяются друг от друга запятыми, более подробно о нем можете прочитать здесь.

In [3]: data = 'price,count,percent\n1,10,\n2,20,51\n3,30,'
In [4]: df = pd.read_csv(StringIO(data))

Полученный объект df – это DataFrame с пропусками.

In [5]: df
Out[5]:
  price count percent
0     1    10     NaN
1     2    20    51.0
2     3    30     NaN

В нашем примере, у объектов с индексами 0 и 2 отсутствуют данные в поле percent. Отсутствующие данные помечаются как NaNДобавим к существующей структуре еще один объект (запись), у которого будет отсутствовать значение в поле count.

In [6]: df.loc[3] = {'price':4, 'count':None, 'percent':26.3}

In [7]: df
Out[7]:
  price count percent
0   1.0  10.0     NaN
1   2.0  20.0    51.0
2   3.0  30.0     NaN
3   4.0   NaN    26.3

Для начала обратимся к методам из библиотеки pandas, которые позволяют быстро определить наличие элементов NaN в структурах. Если таблица небольшая, то можно использовать библиотечный метод isnull. Выглядит это так.

In [8]: pd.isnull(df)
Out[8]:
  price count percent
0 False False    True
1 False False   False
2 False False    True
3 False True    False

Таким образом мы получаем таблицу того же размера, но на месте реальных данных в ней находятся логические переменные, которые принимают значение False, если значение поля у объекта есть, или True, если значение в данном поле – это NaNВ дополнение к этому можно посмотреть подробную информацию об объекте, для этого можно воспользоваться методом info().

In [9]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4 entries, 0 to 3
Data columns (total 3 columns):
price 4 non-null float64
count 3 non-null float64
percent 2 non-null float64
dtypes: float64(3)
memory usage: 128.0 bytes

В нашем примере видно, что объект df имеет три столбца (count, percent и price), при этом в столбце price все объекты значимы – не NaN, в столбце count – один NaN объект, в поле percent – два NaN объекта. Можно воспользоваться следующим подходом для получения количества NaN элементов в записях.

In [10]: df.isnull().sum()
Out[10]:
price 0
count 1
percent 2
dtype: int64

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

Отсутствующие данные объектов можно заменить на конкретные числовые значения, для этого можно использовать метод fillna()Для экспериментов будем использовать структуру df, созданную в предыдущем разделе.

In [11]: df.isnull().sum()
Out[11]:
price 0
count 1
percent 2
dtype: int64

In [12]: df
Out[12]:
  price count percent
0   1.0  10.0     NaN
1   2.0  20.0    51.0
2   3.0  30.0     NaN
3   4.0   NaN    26.3

In [13]: df.fillna(0)
Out[13]:
  price count percent
0   1.0  10.0     0.0
1   2.0  20.0    51.0
2   3.0  30.0     0.0
3   4.0   0.0    26.3

Этот метод не изменяет текущую структуру, он возвращает структуру DataFrame, созданную на базе существующей, с заменой NaN значений на те, что переданы в метод в качестве аргумента. Данные можно заполнить средним значением по столбцу.

In [14]: df.fillna(df.mean())
Out[14]:
  price count percent
0   1.0  10.0   38.65
1   2.0  20.0   51.00
2   3.0  30.0   38.65
3   4.0  20.0   26.30

В зависимости от задачи используется тот или иной метод заполнения отсутствующих элементов, это может быть нулевое значение, математическое ожидание, медиана и т.п. Для замены NaN элементов на конкретные значения, можно использовать интерполяцию, которая реализована в методе interpolate(), алгоритм интерполяции задается через аргументы метода.

Удаление объектов/столбцов с отсутствующими данными

Довольно часто используемый подход при работе с отсутствующими данными – это удаление записей (строк) или полей (столбцов), в которых встречаются пропуски. Для того, чтобы удалить все объекты, которые содержат значения NaN воспользуйтесь методом dropna() без аргументов.

In [15]: df.dropna()
Out[15]:
  price count percent
1   2.0  20.0   51.0

Вместо записей, можно удалить поля, для этого нужно вызвать метод dropna с аргументом axis=1.

In [16]: df.dropna()
Out[16]:
  price count percent
1   2.0  20.0    51.0
In [17]: df.dropna(axis=1)
Out[17]:
  price
0   1.0
1   2.0
2   3.0
3   4.0

pandas позволяет задать порог на количество не-NaN элементов. В приведенном ниже примере будут удалены все столбцы, в которых количество не-NaN элементов меньше трех.

In [18]: df.dropna(axis = 1, thresh=3)
Out[18]:
  price count
0   1.0  10.0
1   2.0  20.0
2   3.0  30.0
3   4.0   NaN

P.S.

Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.

Книга: Pandas. Работа с данными

<<< Урок 3. Доступ к данным в структурах pandas

Изучаем pandas. Урок 4. Работа с пропусками в данных: 4 комментария

  1. Карл

    Полезная статья, решила одну из моих проблем с кодом)

    Интересно будет узнать, ЧЕМ заполнять пропуски?

    1. writer

      В начале написал комментарий, частично повторив содержимое статьи))) Чем заполнять, определяется задачей, т.е. если можно заполнить средним значением, то заполняйте средним, если данные чувствительны к таким махинациям, то можно просто их выбросить.

  2. кг

    Для этого импортируем необходимые нам библиотеки.

    In [1]: import pandas as pd
    In [2]: from io import StringIO

    с первым понятно, про второе ничего не сказано – что, для чего….

  3. Locf

    Огромное спасибо. Прямо спасли меня: нужно было пустые ячейки в столбце как-то идентифицировать для if- конструкции в цикле for, чтобы затем заполнить их соответствующими данными (не одинаковыми) из другого источника. Нигде не могла найти эту формулировку, только вы навели на идею . Сделала if pd.isnull( df[‘имя столбца’][ i ]): тогда записать туда пропущенные данные.
    Двадцать сайтов просмотрела, нигде не могла найти, как именно оформить isnull()

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *