Додавання обчислених стовпців до фрейму даних у пандах


78

У мене є набір даних про ціни OHLC, який я проаналізував із CSV у фрейм даних Pandas і зробив вибірку до 15-хвилинних барів:

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 500047 entries, 1998-05-04 04:45:00 to 2012-08-07 00:15:00
Freq: 15T
Data columns:
Close    363152  non-null values
High     363152  non-null values
Low      363152  non-null values
Open     363152  non-null values
dtypes: float64(4)

Я хотів би додати різні обчислювані стовпці, починаючи з простих, таких як діапазон періоду (HL), а потім логічні значення, щоб вказати на появу закономірностей ціни, які я визначу, - наприклад, модель свічки молотка, для якої є зразок визначення:

def closed_in_top_half_of_range(h,l,c):
    return c > l + (h-l)/2

def lower_wick(o,l,c):
    return min(o,c)-l

def real_body(o,c):
    return abs(c-o)

def lower_wick_at_least_twice_real_body(o,l,c):
    return lower_wick(o,l,c) >= 2 * real_body(o,c)

def is_hammer(row):
    return lower_wick_at_least_twice_real_body(row["Open"],row["Low"],row["Close"]) \
    and closed_in_top_half_of_range(row["High"],row["Low"],row["Close"])

Основна проблема: як я можу зіставити функцію зі стовпцем, зокрема там, де я хотів би посилатися на більше ніж один інший стовпець або цілий рядок чи що інше?

Ця публікація стосується додавання двох обчислюваних стовпців з одного вихідного стовпця, який є близьким, але не зовсім цим.

І трохи вдосконаленіший: для моделей цін, які визначаються з посиланням на більше, ніж один бар (T), як я можу посилатися на різні рядки (наприклад, T-1, T-2 тощо) у межах визначення функції?

Відповіді:


75

Точний код буде відрізнятися для кожного стовпця, який ви хочете зробити, але, швидше за все, ви захочете використовувати функції mapand apply. У деяких випадках можна просто обчислити, використовуючи наявні стовпці безпосередньо, оскільки стовпці - це об’єкти серії Pandas, які також працюють як масиви Numpy, які автоматично працюють по елементах для звичайних математичних операцій.

>>> d
    A   B  C
0  11  13  5
1   6   7  4
2   8   3  6
3   4   8  7
4   0   1  7
>>> (d.A + d.B) / d.C
0    4.800000
1    3.250000
2    1.833333
3    1.714286
4    0.142857
>>> d.A > d.C
0     True
1     True
2     True
3    False
4    False

Якщо вам потрібно використовувати такі операції, як max і min у рядку, ви можете використовувати за applyдопомогою axis=1будь-якої функції, яка вам подобається, до кожного рядка. Ось приклад, який обчислює min(A, B)-C, схоже на ваш "нижній гніт":

>>> d.apply(lambda row: min([row['A'], row['B']])-row['C'], axis=1)
0    6
1    2
2   -3
3   -3
4   -7

Сподіваємось, це дає вам деяке уявлення про те, як діяти далі.

Редагувати: для порівняння рядків із сусідніми рядками найпростішим підходом є нарізання стовпців, які потрібно порівняти, залишаючи початок / кінець, а потім порівняння отриманих фрагментів. Наприклад, це покаже вам, для яких рядків елемент у стовпці A менше, ніж елемент наступного рядка в стовпці C:

d['A'][:-1] < d['C'][1:]

і це робиться в інший спосіб, повідомляючи вам, які рядки мають A менше, ніж C попереднього рядка:

d['A'][1:] < d['C'][:-1]

Виконуючи ['A"][:-1]фрагменти з останнього елемента стовпця A та виконуючи ['C'][1:]фрагменти з першого елемента стовпця C, тому, коли ви виставляєте ці два в ряд і порівнюєте їх, ви порівнюєте кожен елемент в A з C із наступного рядка.


47

Ви можете отримати is_hammerз точки зору row["Open"]тощо наступним чином

def is_hammer(rOpen,rLow,rClose,rHigh):
    return lower_wick_at_least_twice_real_body(rOpen,rLow,rClose) \
       and closed_in_top_half_of_range(rHigh,rLow,rClose)

Тоді ви можете використовувати карту:

df["isHammer"] = map(is_hammer, df["Open"], df["Low"], df["Close"], df["High"])

2
Також корисно, велике спасибі. Багато способів зняти шкіру з кота і все. Я дав би вам голос за, але це моє перше запитання про StackOverflow, і у мене, на жаль, недостатньо представників. Я не думаю, що у вас є ідеї щодо другої частини, а саме посилання на сусідні рядки у фреймі даних із функції map / apply? Знову ура.
ultra909

1
З якоїсь причини використання методу map(f, col1, col2)набагато швидший, ніж df.apply(..., axis=1). map займає 0,35 с проти df. застосовувати 26 с для 1М рядка даних. Будь-яка ідея чому? (python 2.7 та pandas 0.18.1)
MohamedEzz

Іноді це чудово працює, але іноді я отримую попередження: "Значення намагається встановити на копії зрізу з DataFrame. Спробуйте замість цього використовувати .loc [row_indexer, col_indexer] = value" ... Будь-яка інформація?
elomage

1
@elomage незрозуміло без додаткової інформації. Можливо, на початку ви нарізаєте нам частину фрейму даних, а потім призначаєте в цьому поданні. Ви можете зробити вигляд копією, скориставшись df = df.copy()вище ... але на це, можливо, краще відповісти як на нове питання.
Енді Хейден,

@AndyHayden df.copy () зробив свою справу, дякую. Я зробив кілька нарізок раніше. Для менших кадрів даних проблем не було, для більших мені довелося зробити копію ().
elomage


1

Перші чотири функції, які ви перелічите, працюватимуть і на векторах, за винятком того, що lower_wick потрібно адаптувати. Щось на зразок цього,

def lower_wick_vec(o, l, c):
    min_oc = numpy.where(o > c, c, o)
    return min_oc - l

де o, l і c - вектори. Ви можете зробити це таким чином, який просто приймає df як вхід і уникає використання numpy, хоча це буде набагато повільніше:

def lower_wick_df(df):
    min_oc = df[['Open', 'Close']].min(axis=1)
    return min_oc - l

Інші три працюватимуть над стовпцями або векторами такими, як вони є. Тоді ви можете закінчити з

def is_hammer(df):
    lw = lower_wick_at_least_twice_real_body(df["Open"], df["Low"], df["Close"]) 
    cl = closed_in_top_half_of_range(df["High"], df["Low"], df["Close"])
    return cl & lw

Бітові оператори можуть виконувати задану логіку на логічні вектори, &for and, |for orі т. Д. Цього достатньо, щоб повністю векторизувати зразки обчислень, які ви дали, і повинно бути відносно швидко. Можливо, ви могли б пришвидшити ще більше, тимчасово працюючи з масивами numpy, що лежать в основі даних під час виконання цих обчислень.

Для другої частини я рекомендую ввести стовпець із зазначенням шаблону для кожного рядка та написати сімейство функцій, які мають справу з кожним шаблоном. Потім згрупуйте за зразком і застосуйте відповідну функцію до кожної групи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.