Продуктивність Pandas DataFrame


74

Pandas - це дійсно чудово, але я справді здивований тим, наскільки неефективно отримувати значення з Pandas.DataFrame. У наступному прикладі іграшок навіть метод DataFrame.iloc більш ніж у 100 разів повільніший за словник.

Питання: чи урок тут полягає лише у тому, що словники є кращим способом пошуку цінностей? Так, я розумію, що саме для цього вони були створені. Але мені просто цікаво, чи щось мені не вистачає щодо продуктивності пошуку DataFrame.

Я усвідомлюю, що це питання більше "роздумує", ніж "задає", але я прийму відповідь, яка дає розуміння чи перспективу на це. Дякую.

import timeit

setup = '''
import numpy, pandas
df = pandas.DataFrame(numpy.zeros(shape=[10, 10]))
dictionary = df.to_dict()
'''

f = ['value = dictionary[5][5]', 'value = df.loc[5, 5]', 'value = df.iloc[5, 5]']

for func in f:
    print func
    print min(timeit.Timer(func, setup).repeat(3, 100000))

значення = словник [5] [5]

0,130625009537

значення = df.loc [5, 5]

19.4681699276

значення = df.iloc [5, 5]

17,2575249672

Відповіді:


108

Дикт стосується DataFrame, як велосипед - до автомобіля. Ви можете крутити педалі на 10 футів на велосипеді швидше, ніж можете завести машину, перевести її в передачу тощо тощо. Але якщо вам потрібно пройти кілометр, машина виграє.

Для певних малих, цільових цілей винесення може бути швидшим. І якщо це все, що вам потрібно, то скористайтеся диктом, точно! Але якщо вам потрібна / потрібна потужність та розкіш DataFrame, тоді дикт не може замінити вас. Безглуздо порівняти швидкість, якщо структура даних спочатку не задовольняє ваші потреби.

Тепер, наприклад, - щоб бути більш конкретним - дикт хороший для доступу до стовпців, але він не такий зручний для доступу до рядків.

import timeit

setup = '''
import numpy, pandas
df = pandas.DataFrame(numpy.zeros(shape=[10, 1000]))
dictionary = df.to_dict()
'''

# f = ['value = dictionary[5][5]', 'value = df.loc[5, 5]', 'value = df.iloc[5, 5]']
f = ['value = [val[5] for col,val in dictionary.items()]', 'value = df.loc[5]', 'value = df.iloc[5]']

for func in f:
    print(func)
    print(min(timeit.Timer(func, setup).repeat(3, 100000)))

врожайність

value = [val[5] for col,val in dictionary.iteritems()]
25.5416321754
value = df.loc[5]
5.68071913719
value = df.iloc[5]
4.56006002426

Отже, визначення списків у 5 разів повільніше при отриманні рядків, ніж df.iloc. Дефіцит швидкості стає більшим із збільшенням кількості стовпців. (Кількість стовпців, як аналогічно кількості футів, за аналогією з велосипедом. Чим більше відстань, тим зручніше стає машина ...)

Це лише один із прикладів, коли складання списку було б менш зручним / повільним, ніж DataFrame.

Іншим прикладом може бути, коли у вас є DatetimeIndex для рядків і ви хочете вибрати всі рядки між певними датами. За допомогою DataFrame ви можете використовувати

df.loc['2000-1-1':'2000-3-31']

Немає простого аналога для цього, якщо ви хочете скористатися списком списків. І цикли Python, які вам потрібно буде використовувати для вибору правильних рядків, знову будуть надзвичайно повільними в порівнянні з DataFrame.


відповіді на зразок цього, можливо, додати до поширених запитань, див. тут: github.com/pydata/pandas/issues/3871
Джефф

4
Дякую за два справді яскраві приклади, а також за аналогію, яку, як велосипедист, я ціную.
Оуен

3
У вашому прикладі використовується df.to_dict()не dict стовпців, а dict dicts. використовуючи dic = {x:df[x].values.tolist() for x in df}ви отримуєте (на моїй машині) покращення доступу до стовпців у 25 разів і швидший доступ до рядків в 1,5 рази. отже, словник швидший
тал.

21

Здається, різниця в продуктивності набагато менша зараз (0.21.1 - я забув, якою була версія Pandas в оригінальному прикладі). Не лише розрив у продуктивності між доступом до словника та .locзменшенням (приблизно з 335 разів до 126 разів повільнішим), loc( iloc) менше ніж у два рази повільнішим, ніж at( iat) зараз.

In [1]: import numpy, pandas
   ...:    ...: df = pandas.DataFrame(numpy.zeros(shape=[10, 10]))
   ...:    ...: dictionary = df.to_dict()
   ...: 

In [2]: %timeit value = dictionary[5][5]
85.5 ns ± 0.336 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [3]: %timeit value = df.loc[5, 5]
10.8 µs ± 137 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [4]: %timeit value = df.at[5, 5]
6.87 µs ± 64.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [5]: %timeit value = df.iloc[5, 5]
14.9 µs ± 114 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [6]: %timeit value = df.iat[5, 5]
9.89 µs ± 54.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: print(pandas.__version__)
0.21.1

---- Оригінальна відповідь нижче ----

+1 для використання atабо iatдля скалярних операцій. Приклад еталону:

In [1]: import numpy, pandas
   ...: df = pandas.DataFrame(numpy.zeros(shape=[10, 10]))
   ...: dictionary = df.to_dict()

In [2]: %timeit value = dictionary[5][5]
The slowest run took 34.06 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 310 ns per loop

In [4]: %timeit value = df.loc[5, 5]
10000 loops, best of 3: 104 µs per loop

In [5]: %timeit value = df.at[5, 5]
The slowest run took 6.59 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 9.26 µs per loop

In [6]: %timeit value = df.iloc[5, 5]
10000 loops, best of 3: 98.8 µs per loop

In [7]: %timeit value = df.iat[5, 5]
The slowest run took 6.67 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 9.58 µs per loop

Здається, використання at( iat) приблизно в 10 разів швидше, ніж loc( iloc).


6

Я зіткнувся з тією ж проблемою. ви можете використовувати atдля вдосконалення.

"Оскільки індексація за допомогою [] повинна обробляти багато випадків (доступ з однією міткою, нарізка, булеве індексування тощо), вона має трохи накладних витрат, щоб зрозуміти, про що ви просите. Якщо ви хочете лише отримати доступ до скалярного значення, найшвидший спосіб - скористатися методами atand iat, які реалізовані у всіх структурах даних. "

див. офіційне посилання http://pandas.pydata.org/pandas-docs/stable/indexing.html розділ "Швидке отримання та налаштування скалярного значення"


це хороша довідка, але не така детальна, як наведена вище відповідь.
BCR

1

Я думаю, що найшвидший спосіб отримати доступ до комірки - це

df.get_value(row,column)
df.set_value(row,column,value) 

Обидва швидші, ніж (я думаю)

df.iat(...) 
df.at(...)

1
Здається, atце швидше - 4,68 мкс ( at) проти 5,98 мкс ( get_values). Також atнабагато гнучкіший, оскільки ви можете використовувати іменовані індекси.
юний

0

Я зазнав різного явища щодо доступу до рядка фреймів даних. протестуйте цей простий приклад на фреймі даних близько 10 000 000 рядків. словникові скелі.

def testRow(go):
    go_dict = go.to_dict()
    times = 100000
    ot= time.time()
    for i in range(times):
        go.iloc[100,:]
    nt = time.time()
    print('for iloc {}'.format(nt-ot))
    ot= time.time()
    for i in range(times):
        go.loc[100,2]
    nt = time.time()
    print('for loc {}'.format(nt-ot))
    ot= time.time()
    for i in range(times):
        [val[100] for col,val in go_dict.iteritems()]
    nt = time.time()
    print('for dict {}'.format(nt-ot))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.