Каков наиболее эффективный способ перебора кадров данных с пандами?
Я хочу выполнять свои собственные сложные операции с финансовыми данными в кадрах данных в последовательном порядке.
например, я использую следующий файл MSFT CSV, взятый из Yahoo Finance:
Date,Open,High,Low,Close,Volume,Adj Close
2011-10-19,27.37,27.47,27.01,27.13,42880000,27.13
2011-10-18,26.94,27.40,26.80,27.31,52487900,27.31
2011-10-17,27.11,27.42,26.85,26.98,39433400,26.98
2011-10-14,27.31,27.50,27.02,27.27,50947700,27.27
....
затем я делаю следующее:
#!/usr/bin/env python
from pandas import *
df = read_csv('table.csv')
for i, row in enumerate(df.values):
date = df.index[i]
open, high, low, close, adjclose = row
#now perform analysis on open/close based on date, etc..
заключается в том, что наиболее эффективным способом? Учитывая акцент на скорости в pandas, я бы предположил, что должна быть какая-то специальная функция для итерации значений таким образом, чтобы также получить индекс (возможно, через генератор, чтобы быть эффективной памяти)? df.iteritems к сожалению, только повторяет столбец за столбцом.
10 ответов:
самые новые версии панд теперь включают встроенную функцию для перебора строк.
for index, row in df.iterrows(): # do some logic hereили, если вы хотите его быстрее использовать
itertuples()но предложение unutbu использовать функции numpy, чтобы избежать итерации по строкам, создаст самый быстрый код.
панды основаны на массивах NumPy. Ключ к скорости работы с массивами NumPy-это выполнение операций над всем массивом сразу, а не по строкам или элементам.
например, если
closeявляется 1-d массив, и вы хотите, чтобы день за днем процентное изменение,pct_change = close[1:]/close[:-1]это вычисляет весь массив процентных изменений как один оператор, а не
pct_change = [] for row in close: pct_change.append(...)поэтому старайтесь избегать цикла Python
for i, row in enumerate(...)полностью, и подумайте о том, как выполнить ваши вычисления с операциями над всем массивом (или фреймом данных) в целом, а не по строкам.
вы можете перебирать строки, транспонируя и затем вызывая iteritems:
for date, row in df.T.iteritems(): # do some logic hereЯ не уверен в эффективности в этом случае. Чтобы получить максимальную производительность в итерационном алгоритме, вы можете изучить его запись в на Cython, так что вы могли бы сделать что-то вроде:
def my_algo(ndarray[object] dates, ndarray[float64_t] open, ndarray[float64_t] low, ndarray[float64_t] high, ndarray[float64_t] close, ndarray[float64_t] volume): cdef: Py_ssize_t i, n float64_t foo n = len(dates) for i from 0 <= i < n: foo = close[i] - open[i] # will be extremely fastЯ бы рекомендовал сначала написать алгоритм на чистом Python, убедиться, что он работает и посмотреть, насколько он быстр-если он недостаточно быстр, преобразуйте вещи в Cython, как это с минимальной работой, чтобы получить что-то такое же быстрое, как ручной код C/C++.
Как и то, что было упомянуто ранее, объект pandas наиболее эффективен при обработке всего массива сразу. Однако для тех, кому действительно нужно пройти через фрейм данных pandas, чтобы выполнить что-то, как я, я нашел по крайней мере три способа сделать это. Я сделал короткий тест, чтобы увидеть, какой из трех является наименее трудоемким.
t = pd.DataFrame({'a': range(0, 10000), 'b': range(10000, 20000)}) B = [] C = [] A = time.time() for i,r in t.iterrows(): C.append((r['a'], r['b'])) B.append(time.time()-A) C = [] A = time.time() for ir in t.itertuples(): C.append((ir[1], ir[2])) B.append(time.time()-A) C = [] A = time.time() for r in zip(t['a'], t['b']): C.append((r[0], r[1])) B.append(time.time()-A) print Bрезультат:
[0.5639059543609619, 0.017839908599853516, 0.005645036697387695]это, вероятно, не лучший способ измерить потребление времени, но это быстро для мне.
вот некоторые плюсы и минусы ИМХО:
- .iterrows (): возвращает элементы индекса и строки в отдельных переменных, но значительно медленнее
- .itertuples(): быстрее, чем .iterrows (), но возвращает индекс вместе с элементами строки, ir[0] - это индекс
- zip: самый быстрый, но нет доступа к индексу строки
Я проверил
iterrowsувидев Ник Кроуфорд!--5--> ответ, но обнаружил, что он дает (индекс, ряд) кортежей. Не уверен, что будет работать лучше для вас, но я в конечном итоге с помощьюitertuplesметод для моей задачи, которая дает (index, row_value1...) кортежи.там же
iterkv, который перебирает (столбец, ряд) кортежей.
как небольшое дополнение, вы также можете применить, если у вас есть сложная функция, которую вы применяете к одному столбцу:
http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html
df[b] = df[a].apply(lambda col: do stuff with col here)
у вас есть три варианта:
By индекс (простой):
>>> for index in df.index: ... print ("df[" + str(index) + "]['B']=" + str(df['B'][index]))С iterrows (наиболее часто используемые):
>>> for index, row in df.iterrows(): ... print ("df[" + str(index) + "]['B']=" + str(row['B']))С itertuples (быстрый):
>>> for row in df.itertuples(): ... print ("df[" + str(row.Index) + "]['B']=" + str(row.B))три варианта отображения что-то вроде:
df[0]['B']=125 df[1]['B']=415 df[2]['B']=23 df[3]['B']=456 df[4]['B']=189 df[5]['B']=456 df[6]['B']=12источник:neural-networks.io
как @joris указано,
iterrowsгораздо медленнее, чемitertuplesиitertuplesпримерно в 100 раз больше, чемiterrows, и я проверил скорость обоих методов в фрейме данных с 5027505 записями результат дляiterrows, это 1200it/s, иitertuplesэто 120000it/С.если вы используете
itertuples, обратите внимание, что каждый элемент в цикле for является namedtuple, поэтому, чтобы получить значение в каждом столбце, вы можете обратиться к следующему примеру кода>>> df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b']) >>> df col1 col2 a 1 0.1 b 2 0.2 >>> for row in df.itertuples(): ... print(row.col1, row.col2) ... 1, 0.1 2, 0.2
другим предложением было бы объединить groupby с векторизованными вычислениями, если подмножества строк разделяют характеристики, которые позволили вам это сделать.
конечно, самый быстрый способ итерации по фрейму данных-это доступ к базовому numpy ndarray либо через
df.values(Как вы делаете) или путем доступа к каждому столбцу отдельноdf.column_name.values. Так как вы хотите иметь доступ к индексу тоже, вы можете использоватьdf.index.valuesдля этого.index = df.index.values column_of_interest1 = df.column_name1.values ... column_of_interestk = df.column_namek.values for i in range(df.shape[0]): index_value = index[i] ... column_value_k = column_of_interest_k[i]Не обновления? Конечно. Но быстро.
если вы хотите выжать больше сока из петли вы захотите взглянуть на на Cython. Cython позволит вам получить огромные ускорения (думаю, 10x-100x). Для максимальной проверки производительности просмотры памяти для cython.
Comments