Каков наиболее эффективный способ циклического преобразования данных с помощью pandas?
Я хочу выполнить свои собственные сложные операции с финансовыми данными в dataframes последовательным образом.
Например, я использую следующий файл 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
, к сожалению, только итерация столбца по столбцу.
Ответы
Ответ 1
В новейших версиях pandas теперь есть встроенная функция для итерации по строкам.
for index, row in df.iterrows():
# do some logic here
Или, если вы хотите быстрее использовать itertuples()
Но предложение unutbu использовать функции numpy, чтобы избежать итерации по строкам, приведет к созданию самого быстрого кода.
Ответ 2
Pandas основан на массивах NumPy.
Ключом к ускорению работы с массивами NumPy является одновременное выполнение ваших операций по всему массиву, никогда не по строкам или по отдельным элементам.
Например, если close
- это 1-мерный массив, и вы хотите изменить процент изменения дня за день,
pct_change = close[1:]/close[:-1]
Это вычисляет весь массив изменений процента как один оператор вместо
pct_change = []
for row in close:
pct_change.append(...)
Поэтому старайтесь полностью избегать цикла Python for i, row in enumerate(...)
и
подумайте о том, как выполнять вычисления с операциями по всему массиву (или файловому кадру) в целом, а не по строкам.
Ответ 3
Как и ранее, объект pandas наиболее эффективен при обработке всего массива сразу. Тем не менее для тех, кто действительно нуждается в цикле pandas DataFrame для выполнения чего-то, как и я, я нашел по крайней мере три способа сделать это. Я провел короткий тест, чтобы узнать, какой из трех занимает наименьшее время.
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]
Это, вероятно, не самый лучший способ измерить потребление времени, но это быстро для меня.
Вот некоторые плюсы и минусы IMHO:
- .iterrows(): возвращает индекс и элементы строки в отдельных переменных, но значительно медленнее
- .itertuples(): быстрее, чем .iterrows(), но возвращает индекс вместе с элементами строки, ir [0] - индекс
- zip: самый быстрый, но не доступ к индексу строки
Ответ 4
Вы можете прокручивать строки путем транспонирования, а затем вызывать 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/С++.
Ответ 5
Я проверил iterrows
после того, как заметил ответ Ника Кроуфорда, но обнаружил, что он дает (индекс, серию) кортежи. Не уверен, что будет работать лучше всего для вас, но в итоге я использовал метод itertuples
для своей проблемы, который дает (index, row_value1...) кортежи.
Здесь также iterkv
, который выполняет итерацию через (столбцы, ряды) кортежей.
Ответ 6
Так же, как небольшое дополнение, вы также можете применить заявку, если у вас есть сложная функция, которую вы применяете к одному столбцу:
http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html
df[b] = df[a].apply(lambda col: do stuff with col here)
Ответ 7
У вас есть три варианта:
По индексу (самое простое):
>>> 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
Ответ 8
Как указывал @joris, iterrows
намного медленнее, чем itertuples
и itertuples
примерно в 100 раз быстрее, чем iterrows
, и я проверил скорость обоих методов в DataFrame с 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
Ответ 9
Конечно, самый быстрый способ перебрать данные - это получить доступ к лежащему внизу массиву 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.
Ответ 10
Другое предложение состояло бы в том, чтобы объединить groupby с векторизованными вычислениями, если подмножества общих характеристик строк позволяли вам это делать.