Pandas: своеобразное падение производительности для переименования на месте после dropna

Я сообщил об этом как о проблеме с пандами. Тем временем я публикую это здесь в надежде сэкономить время других, если они столкнутся с похожими проблемами.

После профилирования процесса, который необходимо оптимизировать, я обнаружил, что переименование столбцов НЕ на месте повышает производительность (время выполнения) на x120. Профилирование указывает, что это связано со сборкой мусора (см. Ниже).

Кроме того, ожидаемая производительность восстанавливается, избегая метода dropna.

Следующий короткий пример демонстрирует коэффициент x12:

import pandas as pd
import numpy as np

Inplace = True

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)

100 циклов, лучшее из 3: 15,6 мс на цикл

первая строка вывода %%prun:

ncalls totaltime percall cumtime percall имя файла:lineno(функция)

1  0.018 0.018 0.018 0.018 {gc.collect}

Inplace = False

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})

1000 циклов, лучшее из 3: 1,24 мс на цикл

избегать дропна

Ожидаемая производительность восстанавливается, избегая dropna метод:

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
#no dropna:
df = (df1-df2)#.dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)

1000 петель, лучшее из 3: 865 мкс на петлю

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
## no dropna
df = (df1-df2)#.dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})

1000 петель, лучшее из 3: 902 мкс на петлю

1 ответ

Решение

Это копия объяснения на GitHub.

Там нет никакой гарантии, что inplace операция на самом деле быстрее. Часто это фактически та же операция, которая работает с копией, но ссылка верхнего уровня переназначается.

Причина различия в производительности в этом случае заключается в следующем.

(df1-df2).dropna() Вызов создает фрагмент кадра данных. Когда вы применяете новую операцию, это вызывает SettingWithCopy проверьте, потому что это может быть копия (но часто это не так).

Эта проверка должна выполнить сборку мусора, чтобы стереть некоторые ссылки на кэш, чтобы увидеть, является ли это копией. К сожалению, синтаксис Python делает это неизбежным.

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

df = (df1-df2).dropna().copy()

с последующим inplace операция будет такой же производительной, как и раньше.

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

Другие вопросы по тегам