Динамические оконные функции, более быстрое применение и многое другое.

pandas 1.0 был выпущен 29 января 2020 года. Версия подскочила с 0.25 до 1.0, никаких кардинальных изменений, как ожидают некоторые пользователи pandas, не произошло. Увеличение версии просто отражает зрелость библиотеки обработки данных.

Революционных изменений не так много, но есть некоторые, о которых вам следует знать.

Вот несколько ссылок, которые могут вас заинтересовать:

- Labeling and Data Engineering for Conversational AI and Analytics
- Data Science for Business Leaders [Course]
- Intro to Machine Learning with PyTorch [Course]
- Become a Growth Product Manager [Course]
- Deep Learning (Adaptive Computation and ML series) [Ebook]
- Free skill tests for Data Scientists & Machine Learning Engineers

Некоторые из приведенных выше ссылок являются партнерскими, и если вы перейдете по ним, чтобы совершить покупку, я буду получать комиссию. Имейте в виду, что я связываю курсы из-за их качества, а не из-за комиссии, которую я получаю от ваших покупок.

Чтобы улучшить свою игру Pandas, прочтите:



1. Динамический размер окна с функциями прокрутки

Функции скользящего окна очень полезны при работе с данными временных рядов (например, расчет скользящего среднего). Предыдущая версия pandas требовала, чтобы мы передавали параметр размера окна, например. рассчитать скользящую среднюю по 3 периодам. С помощью pandas 1.0 мы можем обойти это требование, как показано в примере ниже.

Давайте посчитаем скользящее среднее значений до тех пор, пока текущее число не станет больше 10. Сначала мы создаем DataFrame с 3 значениями, большими или равными 10.

df = pd.DataFrame({'col1': [1, 2, 3, 10, 2, 3, 11, 2, 3, 12, 1, 2]})
df

Функция окна должна расширяться до тех пор, пока не будет достигнуто значение больше или равное 10.

use_expanding =  (df.col1 >= 10).tolist()
use_expanding 
# output
[False,
 False,
 False,
 True,
 False,
 False,
 True,
 False,
 False,
 True,
 False,
 False]

Для оконных функций с динамическим размером нам нужно реализовать собственный индексатор, который наследуется от класса BaseIndexer pandas. В классе BaseIndexer есть функция get_window_bounds, которая вычисляет начало и конец каждого окна.

from pandas.api.indexers import BaseIndexer
class CustomIndexer(BaseIndexer):
    def get_window_bounds(self, num_values, min_periods, center, closed):
        start = np.empty(num_values, dtype=np.int64)
        end = np.empty(num_values, dtype=np.int64)
        start_i = 0
        for i in range(num_values):
            if self.use_expanding[i]:
                start[i] = start_i
                start_i = end[i] = i + 1
            else:
                start[i] = start_i
                end[i] = i + self.window_size
        print('start', start)
        print('end', end)
        return start, end

indexer = CustomIndexer(window_size=1, use_expanding=use_expanding)

Мы помещаем класс индексатора в функцию прокрутки и вычисляем среднее значение для каждого окна. Мы также можем наблюдать за начальным и конечным индексами каждого окна.

df.rolling(indexer).mean()

2. Применение Faster Rolling

Pandas использует Cython в качестве механизма выполнения по умолчанию с применением прокатки. В pandas 1.0 мы можем указать Numba в качестве механизма выполнения и получить приличное ускорение.

Следует отметить несколько моментов:

  • Необходимо установить зависимость Numba: pip install numba,
  • первый раз, когда функция запускается с использованием движка Numba, будет медленным, так как Numba будет иметь некоторые накладные расходы на компиляцию функций. Однако скользящие объекты будут кэшировать функцию, и последующие вызовы будут быстрыми,
  • движок Numba работает с большим количеством точек данных (например, более 1 миллиона),
  • необработанный аргумент должен быть установлен в True, что означает, что функция будет получать numpy объектов вместо серии pandas для достижения лучшей производительности.

Давайте создадим DataFrame с 1 миллионом значений.

df = pd.DataFrame({"col1": pd.Series(range(1_000_000))})
df.head()

some_function вычисляет сумму значений и добавляет 5.

def some_function(x):
    return np.sum(x) + 5

Давайте измерим время выполнения с помощью механизма выполнения Cython.

%%timeit
df.col1.rolling(100).apply(some_function, engine='cython', raw=True)
4.03 s ± 76.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Cython потребовалось 4,03 секунды для вычисления функции. Нумба быстрее? Давай попробуем.

%%timeit
df.col1.rolling(100).apply(some_function, engine='numba', raw=True)
500 ms ± 11.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Мы видим, что на этом игрушечном примере Numba работает в 8 раз быстрее.

3. Новое значение NA

pandas 1.0 представляет новое экспериментальное значение pd.NA для представления скалярных пропущенных значений.

Я знаю, о чем вы думаете - еще одно нулевое значение? Уже есть nan, None и NaT!

Цель pd.NA - обеспечить согласованность между типами данных. В настоящее время он используется типами данных Int64, boolean и new string.

Давайте создадим серию целых чисел с параметром None.

s = pd.Series([3, 6, 9, None], dtype="Int64")
s

Что меня удивляет, так это то, что NA == NA дает NA, а np.nan == np.nan дает False.

s.loc[3] == s.loc[3]
# output
<NA>

np.nan == np.nan
# output
False

4. Новый тип String

В pandas 1.0 наконец-то есть специальный (экспериментальный) строковый тип. До версии 1.0 строки хранились как объекты, поэтому мы не могли быть уверены, содержит ли серия только строки или она смешана с другими типами данных, как я демонстрирую ниже.

s = pd.Series(['an', 'ban', 'pet', 'podgan', None])
s

Сохранение строк в виде объектов становится проблемой, когда мы непреднамеренно смешиваем их с целыми числами или числами с плавающей запятой - тип данных остается объектом.

s = pd.Series(['an', 'ban', 5, 'pet', 5.0, 'podgan', None])
s

Чтобы протестировать новую строку dtype, нам нужно установить dtype = ’string’.

Новый строковый тип данных возвращает исключение с целыми числами или числами с плавающей запятой в серии. Отличное улучшение!

s = pd.Series(['an', 'ban', 'pet', 'podgan', None], dtype='string')
s

5. Игнорировать индекс в отсортированном DataFrame.

Когда мы сортируем DataFrame по определенному столбцу, индекс также сортируется. Иногда мы этого не хотим. В pandas 1.0 функция sort_values ​​принимает индекс игнорирования, что соответствует названию аргумента.

df = pd.DataFrame({"col1": [1, 3, 5, 2, 3, 7, 1, 2]})
df.sort_values('col1')

df.sort_values('col1', ignore_index=True)

Заключение

Это были 5 самых интересных функций панд, основанные на моем мнении. В долгосрочной перспективе новый NA для отсутствующих значений может внести большую ясность в pandas. Например. как функции обрабатывают пропущенные значения, пропускают они их или нет.

Также изменилась политика устаревания:

  • устаревшие версии будут добавлены в второстепенные выпуски (например, 1.1.0),
  • устаревшие и критические изменения API будут применяться в основных выпусках (например, 2.0.0). Стоит ли нам обновлять или оставаться с текущей версией pandas

Новая политика устаревания ставит вопрос: Следует ли мне обновлять панды? легче ответить. Также кажется, что в будущем мы можем ожидать более частых крупных выпусков. Чтобы узнать больше о новых функциях в pandas 1.0, прочтите Что нового в 1.0.0.

Прежде чем ты уйдешь

Следуйте за мной в Twitter, где я регулярно пишу твиты о Data Science и машинном обучении.