Я заметил очень плохую производительность при использовании строк от панд.
Это то, что переживают другие? Это специфично для iterrows, и следует ли избегать этой функции для данных определенного размера (я работаю с 2-3 миллионами строк)?
Это обсуждение на GitHub привело меня к мысли, что это вызвано смешиванием dtypes в фрейме данных, однако простой пример ниже показывает, что он присутствует даже при использовании одного dtype (float64). На моей машине это занимает 36 секунд:
import pandas as pd
import numpy as np
import time
s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})
start = time.time()
i=0
for rowindex, row in dfa.iterrows():
i+=1
end = time.time()
print end - start
Почему векторизованные операции вроде применяются намного быстрее? Я полагаю, что там должна быть итерация строка за строкой.
Я не могу понять, как не использовать iterrows в моем случае (это я сохраню для будущего вопроса). Поэтому я был бы признателен, если бы вам постоянно удавалось избегать этой итерации. Я делаю расчеты на основе данных в отдельных фреймах данных. Спасибо!
--- Изменить: упрощенная версия того, что я хочу запустить, добавлена ниже ---
import pandas as pd
import numpy as np
#%% Create the original tables
t1 = {'letter':['a','b'],
'number1':[50,-10]}
t2 = {'letter':['a','a','b','b'],
'number2':[0.2,0.5,0.1,0.4]}
table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)
#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])
#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():
t2info = table2[table2.letter == row['letter']].reset_index()
table3.ix[row_index,] = optimize(t2info,row['number1'])
#%% Define optimization
def optimize(t2info, t1info):
calculation = []
for index, r in t2info.iterrows():
calculation.append(r['number2']*t1info)
maxrow = calculation.index(max(calculation))
return t2info.ix[maxrow]
источник
apply
НЕ векторизуется.iterrows
еще хуже, поскольку он упаковывает все (то есть перфомансapply
). Вы должны использовать его толькоiterrows
в очень редких ситуациях. ИМХО никогда. Покажите, что вы на самом деле делаетеiterrows
.DatetimeIndex
вTimestamps
(была реализована в пространстве Python), и это было значительно улучшено в master.Ответы:
Как правило,
iterrows
следует использовать только в очень и очень конкретных случаях. Это общий порядок приоритета для выполнения различных операций:1) vectorization 2) using a custom cython routine 3) apply a) reductions that can be performed in cython b) iteration in python space 4) itertuples 5) iterrows 6) updating an empty frame (e.g. using loc one-row-at-a-time)
Использование настраиваемой процедуры Cython обычно слишком сложно, поэтому давайте пока это пропустим.
1) Векторизация - ВСЕГДА, ВСЕГДА первый и лучший выбор. Однако есть небольшой набор случаев (обычно связанных с повторением), которые нельзя векторизовать очевидными способами. Кроме того, на небольшом
DataFrame
может быть быстрее использовать другие методы.3)
apply
обычно может обрабатываться итератором в пространстве Cython. Это обрабатывается внутри pandas, хотя это зависит от того, что происходит внутриapply
выражения. Например,df.apply(lambda x: np.sum(x))
выполняется довольно быстро, хотя, конечно,df.sum(1)
даже лучше. Однако что-то вродеdf.apply(lambda x: x['b'] + 1)
будет выполняться в пространстве Python и, следовательно, намного медленнее.4)
itertuples
не помещает данные вSeries
. Он просто возвращает данные в виде кортежей.5)
iterrows
ЗАКРЫВАЕТ данные вSeries
. Если вам это действительно не нужно, используйте другой метод.6) Обновление пустого кадра по одной строке за раз. Я видел, как этот метод слишком часто используется. Это самый медленный вариант. Вероятно, это обычное место (и достаточно быстрое для некоторых структур Python), но a
DataFrame
выполняет изрядное количество проверок индексации, поэтому обновление строки за раз всегда будет очень медленным. Намного лучше создавать новые конструкции иconcat
.источник
itertuples
быстрее, чемapply
:(pd.DataFrame.apply
часто медленнее, чемitertuples
. Кроме того, стоит рассмотреть понимание списков,map
плохо названныхnp.vectorize
иnumba
(без определенного порядка) для невекторизуемых вычислений, например, см. Этот ответ .Векторные операции в Numpy и pandas намного быстрее скалярных операций в ванильном Python по нескольким причинам:
Поиск амортизированного типа : Python - это язык с динамической типизацией, поэтому для каждого элемента в массиве существуют накладные расходы времени выполнения. Однако Numpy (и, следовательно, pandas) выполняют вычисления на C (часто через Cython). Тип массива определяется только в начале итерации; Одна только эта экономия - одна из самых больших побед.
Лучшее кеширование : итерация по массиву C удобна для кеширования и, следовательно, очень быстро. DataFrame pandas - это «таблица, ориентированная на столбцы», что означает, что каждый столбец на самом деле является просто массивом. Таким образом, собственные действия, которые вы можете выполнять с DataFrame (например, суммирование всех элементов в столбце), будут иметь несколько промахов в кеше.
Больше возможностей для параллелизма : с простым массивом C можно работать с помощью инструкций SIMD. Некоторые части Numpy включают SIMD, в зависимости от вашего процессора и процесса установки. Преимущества параллелизма не будут такими значительными, как статическая типизация и улучшенное кеширование, но они все же солидная победа.
Мораль истории: используйте векторные операции в Numpy и pandas. Они быстрее скалярных операций в Python по той простой причине, что эти операции в любом случае написаны вручную программистом на C. (За исключением того, что понятие массива гораздо легче читать, чем явные циклы со встроенными инструкциями SIMD.)
источник
Вот способ решить вашу проблему. Все это векторизовано.
In [58]: df = table1.merge(table2,on='letter') In [59]: df['calc'] = df['number1']*df['number2'] In [60]: df Out[60]: letter number1 number2 calc 0 a 50 0.2 10 1 a 50 0.5 25 2 b -10 0.1 -1 3 b -10 0.4 -4 In [61]: df.groupby('letter')['calc'].max() Out[61]: letter a 25 b -1 Name: calc, dtype: float64 In [62]: df.groupby('letter')['calc'].idxmax() Out[62]: letter a 1 b 2 Name: calc, dtype: int64 In [63]: df.loc[df.groupby('letter')['calc'].idxmax()] Out[63]: letter number1 number2 calc 1 a 50 0.5 25 2 b -10 0.1 -1
источник
Другой вариант - использовать
to_records()
, который быстрее, чем обаitertuples
иiterrows
.Но в вашем случае есть много возможностей для других улучшений.
Вот моя последняя оптимизированная версия
def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) t2info = table2.to_records() for index, letter, n1 in table1.to_records(): t2 = t2info[grouped.groups[letter].values] # np.multiply is in general faster than "x * y" maxrow = np.multiply(t2.number2, n1).argmax() # `[1:]` removes the index column ret.append(t2[maxrow].tolist()[1:]) global table3 table3 = pd.DataFrame(ret, columns=('letter', 'number2'))
Контрольный тест:
-- iterrows() -- 100 loops, best of 3: 12.7 ms per loop letter number2 0 a 0.5 1 b 0.1 2 c 5.0 3 d 4.0 -- itertuple() -- 100 loops, best of 3: 12.3 ms per loop -- to_records() -- 100 loops, best of 3: 7.29 ms per loop -- Use group by -- 100 loops, best of 3: 4.07 ms per loop letter number2 1 a 0.5 2 b 0.1 4 c 5.0 5 d 4.0 -- Avoid multiplication -- 1000 loops, best of 3: 1.39 ms per loop letter number2 0 a 0.5 1 b 0.1 2 c 5.0 3 d 4.0
Полный код:
import pandas as pd import numpy as np #%% Create the original tables t1 = {'letter':['a','b','c','d'], 'number1':[50,-10,.5,3]} t2 = {'letter':['a','a','b','b','c','d','c'], 'number2':[0.2,0.5,0.1,0.4,5,4,1]} table1 = pd.DataFrame(t1) table2 = pd.DataFrame(t2) #%% Create the body of the new table table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=table1.index) print('\n-- iterrows() --') def optimize(t2info, t1info): calculation = [] for index, r in t2info.iterrows(): calculation.append(r['number2'] * t1info) maxrow_in_t2 = calculation.index(max(calculation)) return t2info.loc[maxrow_in_t2] #%% Iterate through filtering relevant data, optimizing, returning info def iterthrough(): for row_index, row in table1.iterrows(): t2info = table2[table2.letter == row['letter']].reset_index() table3.iloc[row_index,:] = optimize(t2info, row['number1']) %timeit iterthrough() print(table3) print('\n-- itertuple() --') def optimize(t2info, n1): calculation = [] for index, letter, n2 in t2info.itertuples(): calculation.append(n2 * n1) maxrow = calculation.index(max(calculation)) return t2info.iloc[maxrow] def iterthrough(): for row_index, letter, n1 in table1.itertuples(): t2info = table2[table2.letter == letter] table3.iloc[row_index,:] = optimize(t2info, n1) %timeit iterthrough() print('\n-- to_records() --') def optimize(t2info, n1): calculation = [] for index, letter, n2 in t2info.to_records(): calculation.append(n2 * n1) maxrow = calculation.index(max(calculation)) return t2info.iloc[maxrow] def iterthrough(): for row_index, letter, n1 in table1.to_records(): t2info = table2[table2.letter == letter] table3.iloc[row_index,:] = optimize(t2info, n1) %timeit iterthrough() print('\n-- Use group by --') def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) for index, letter, n1 in table1.to_records(): t2 = table2.iloc[grouped.groups[letter]] calculation = t2.number2 * n1 maxrow = calculation.argsort().iloc[-1] ret.append(t2.iloc[maxrow]) global table3 table3 = pd.DataFrame(ret) %timeit iterthrough() print(table3) print('\n-- Even Faster --') def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) t2info = table2.to_records() for index, letter, n1 in table1.to_records(): t2 = t2info[grouped.groups[letter].values] maxrow = np.multiply(t2.number2, n1).argmax() # `[1:]` removes the index column ret.append(t2[maxrow].tolist()[1:]) global table3 table3 = pd.DataFrame(ret, columns=('letter', 'number2')) %timeit iterthrough() print(table3)
Окончательная версия почти в 10 раз быстрее исходного кода. Стратегия такова:
groupby
чтобы избежать повторного сравнения значений.to_records
для доступа к необработанным объектам numpy.records.источник
Да, Pandas itertuples () быстрее, чем iterrows (). вы можете обратиться к документации: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html
«Чтобы сохранить типы dtypes при итерации по строкам, лучше использовать itertuples (), который возвращает именованные кортежи значений и который обычно выполняется быстрее, чем iterrows».
источник
Подробности в этом видео
Контрольный показатель
источник