Каков наиболее эффективный способ циклического преобразования данных с помощью 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 с векторизованными вычислениями, если подмножества общих характеристик строк позволяли вам это делать.