Панды: Как я могу вернуть значение строки, когда столбец достигает определенного значения другого столбца?
Вот образец данных:
Цель:
создайте новый столбец отметки времени, когда running_bid_max
больше или равно значению в ask_price_target_good
. Затем создайте отдельный столбец меток времени, когда значение running_bid_min
меньше или равно ask_price_target_bad
.
Примечание. Это будет выполнено для большого объема данных, и потребности будут рассчитаны максимально быстро. Я надеюсь, что мне не придется перебирать все строки через iterrows()
running_bid_min
и running_bid_max
рассчитываются с использованием running.min()
и pd.running.max()
из определенного временного интервала в будущем (в этом примере используется 5-минутная временная шкала. Таким образом, это будет текущая минимальная, максимальная 5 минут от текущей время)
скопируйте данные ниже и затем используйте df = pd.read_clipboard(sep=',')
time,bid_price,ask_price,running_bid_max,running_bid_min,ask_price_target_good,ask_price_target_bad
2019-07-24 07:59:44.432034,291.06,291.26,291.4,291.09,291.46,291.06
2019-07-24 07:59:46.393418,291.1,291.33,291.4,291.09,291.53,291.13
2019-07-24 07:59:48.425615,291.1,291.33,291.4,291.09,291.53,291.13
2019-07-24 07:59:50.084206,291.12,291.33,291.4,291.09,291.53,291.13
2019-07-24 07:59:52.326455,291.12,291.33,291.4,291.09,291.53,291.13
2019-07-24 07:59:54.428181,291.12,291.33,291.4,291.09,291.53,291.13
2019-07-24 07:59:58.550378,291.14,291.35,291.4,291.2,291.55,291.15
2019-07-24 08:00:00.837238,291.2,291.35,291.4,291.2,291.55,291.15
2019-07-24 08:00:57.338769,291.4,291.46,291.51,291.4,291.66,291.26
2019-07-24 08:00:59.058198,291.4,291.46,291.96,291.4,291.66,291.26
2019-07-24 08:01:00.802679,291.4,291.46,291.96,291.4,291.66,291.26
2019-07-24 08:01:02.781289,291.4,291.46,291.96,291.45,291.66,291.26
2019-07-24 08:01:04.645144,291.45,291.46,291.96,291.45,291.66,291.26
2019-07-24 08:01:06.491997,291.45,291.46,292.07,291.45,291.66,291.26
2019-07-24 08:01:08.586688,291.45,291.46,292.1,291.45,291.66,291.26
Ответы
Ответ 1
Из вашего вопроса:
создание нового столбца меток времени, когда running_bid_max
больше или равно значению в ask_price_target_good
. Затем создайте отдельный столбец метки времени, когда значение running_bid_min
меньше или равно ask_price_target_bad
проблема кажется тривиальной:
df['g'] = np.where(df.running_bid_max.ge(df.ask_price_target_good), df['time'], pd.NaT)
df['l'] = np.where(df.running_bid_min.le(df.ask_price_target_bad), df['time'], pd.NaT)
Или я что-то упустил?
Обновление: вы можете захотеть ffill
и bfill
после выполнения приведенных выше команд:
df['g'] = df['g'].bfill()
df['l'] = df['l'].ffill()
Вывод, например df['g']
:
0 2019-07-24 08:00:59.058198
1 2019-07-24 08:00:59.058198
2 2019-07-24 08:00:59.058198
3 2019-07-24 08:00:59.058198
4 2019-07-24 08:00:59.058198
5 2019-07-24 08:00:59.058198
6 2019-07-24 08:00:59.058198
7 2019-07-24 08:00:59.058198
8 2019-07-24 08:00:59.058198
9 2019-07-24 08:00:59.058198
10 2019-07-24 08:01:00.802679
11 2019-07-24 08:01:02.781289
12 2019-07-24 08:01:04.645144
13 2019-07-24 08:01:06.491997
14 2019-07-24 08:01:08.586688
Ответ 2
Было бы очень хорошо, если бы вы могли распечатать желаемый результат. В противном случае я могу пропустить логику.
Если вы работаете с большим объемом данных, имеет смысл применить аналитику паром*. (Это достаточно эффективно использует память, и если вы используете cytoolz
даже в 2-4 раза быстрее)
Таким образом, в основном вы хотели бы разделить ваши данные на основе одного или другого условия:
partitions = toolz.partitionby(lambda x: (x['running_bid_max'] >= x['ask_price_target_good']) or
(x['running_bid_min'] <= x['ask_price_target_bad']), data_stream)
Все, что вы будете делать с отдельными разделами, зависит от вас (вы можете создавать дополнительные поля или столбцы и т.д.).
print([(part[0]['time'], part[-1]['time'],
part[0]['running_bid_max'] > part[0]['ask_price_target_good'],
part[0]['running_bid_min'] > part[0]['ask_price_target_bad'])
for part in partitions])
[('2019-07-24T07:59:46.393418', '2019-07-24T07:59:46.393418', False, False),
('2019-07-24T07:59:44.432034', '2019-07-24T07:59:44.432034', False, True),
('2019-07-24T07:59:48.425615', '2019-07-24T07:59:54.428181', False, False),
('2019-07-24T07:59:58.550378', '2019-07-24T08:00:57.338769', False, True),
('2019-07-24T08:00:59.058198', '2019-07-24T08:01:08.586688', True, True)]
Также обратите внимание, что легко создать отдельного человека DataFrame
info_cols = ['running_bid_max', 'ask_price_target_good', 'running_bid_min', 'ask_price_target_bad', 'time']
data_frames = [pandas.DataFrame(_)[info_cols] for _ in partitions]
data_frames
running_bid_max ask_price_target_good running_bid_min ask_price_target_bad time
0 291.4 291.53 291.09 291.13 2019-07-24T07:59:46.393418
running_bid_max ask_price_target_good running_bid_min ask_price_target_bad time
0 291.4 291.46 291.09 291.06 2019-07-24T07:59:44.432034
running_bid_max ask_price_target_good running_bid_min ask_price_target_bad time
0 291.4 291.53 291.09 291.13 2019-07-24T07:59:48.425615
1 291.4 291.53 291.09 291.13 2019-07-24T07:59:50.084206
2 291.4 291.53 291.09 291.13 2019-07-24T07:59:52.326455
3 291.4 291.53 291.09 291.13 2019-07-24T07:59:54.428181
running_bid_max ask_price_target_good running_bid_min ask_price_target_bad time
0 291.40 291.55 291.2 291.15 2019-07-24T07:59:58.550378
1 291.40 291.55 291.2 291.15 2019-07-24T08:00:00.837238
2 291.51 291.66 291.4 291.26 2019-07-24T08:00:57.338769
running_bid_max ask_price_target_good running_bid_min ask_price_target_bad time
0 291.96 291.66 291.40 291.26 2019-07-24T08:00:59.058198
1 291.96 291.66 291.40 291.26 2019-07-24T08:01:00.802679
2 291.96 291.66 291.45 291.26 2019-07-24T08:01:02.781289
3 291.96 291.66 291.45 291.26 2019-07-24T08:01:04.645144
4 292.07 291.66 291.45 291.26 2019-07-24T08:01:06.491997
5 292.10 291.66 291.45 291.26 2019-07-24T08:01:08.586688
К сожалению, я не смог найти один вкладыш pytition_by
для DataFrame
. Это наверняка где-то спрятано. (Но, опять же, pandas
обычно загружает все данные в память - если вы хотите агрегировать во время ввода/вывода, потоковая передача может быть способом.)
* Потоковый пример
Например, давайте создадим простой поток csv
:
def data_stream():
with open('blubb.csv') as tsfile:
reader = csv.DictReader(tsfile, delimiter='\t')
number_keys = [_ for _ in reader.fieldnames if _ != 'time']
def update_values(data_item):
for k in number_keys:
data_item[k] = float(data_item[k])
return data_item
for row in reader:
yield update_values(dict(row))
который возвращает одну обработанную строку за раз:
next(data_stream())
{'time': '2019-07-24T07:59:46.393418',
'bid_price': 291.1,
'ask_price': 291.33,
'running_bid_max': 291.4,
'running_bid_min': 291.09,
'ask_price_target_good': 291.53,
'ask_price_target_bad': 291.13}
Ответ 3
Я не уверен, что правильно понимаю вашу проблему. Ниже приведено решение следующей проблемы:
- Для данной строки (которую я назову текущей строкой) мы сохраняем все строки, время которых
находится между временем в этом ряду и временем в этом ряду плюс 5 минут
- В строках, которые мы сохранили, мы ищем, может ли
running_bid_max
превосходить
значение, которое мы имеем в столбце ask_price_target_good
текущей строки
- Если это так, мы сохраняем первое вхождение
running_bid_max
выше
ask_price_target_good
текущей строки
В вашем примере для строки 0
у нас есть 291.46
в ask_price_target_good
. В строке 8
(чье время находится в пределах 5 минут от времени строки 0
), мы находим 291.51
(который превосходит 291.46
) и, таким образом, мы хотели бы сохранить это значение для строка 0
.
Симметричная операция должна быть сделана для running_bid_min
, которая должна быть проверена, чтобы уступать ask_price_target_bad
.
Чтобы решить эту проблему, я написал следующий код. Я не использую iterrows
, но функцию apply
в DataFrame
. Тем не менее, мне нужно, для каждой строки, выбрать группу строк из целого кадра данных (временное окно 5 минут) перед поиском строк, которые могут превосходить ask_price_target_good
. Надеюсь, это будет достаточно быстро, если у вас большие фреймы данных.
import numpy as np
import pandas as pd
import datetime as dtm
data = pd.read_csv("data.csv", parse_dates=["time"])
TIME_WINDOW = 5*60
def over_target_good(row, dataframe):
time_window = dataframe.time <= (row.time
+ dtm.timedelta(seconds=TIME_WINDOW))
window_data = dataframe[time_window]
over_test = window_data.running_bid_max >= row.ask_price_target_good
over_data = window_data[over_test]
if len(over_data) > 0:
return over_data.running_bid_max[over_data.index[0]]
return np.NaN
def below_target_bad(row, dataframe):
time_window = dataframe.time <= (row.time
+ dtm.timedelta(seconds=TIME_WINDOW))
window_data = dataframe[time_window]
below_test = window_data.running_bid_min <= row.ask_price_target_bad
below_data = window_data[below_test]
if len(below_data) > 0:
return below_data.running_bid_min[below_data.index[0]]
return np.NaN
print("OVER\n", data.apply(over_target_good, axis=1, args=(data,)) )
print("BELOW\n", data.apply(below_target_bad, axis=1, args=(data,)) )
Ответ 4
Попробуйте это:
df['g']=np.NaN
df['l']=np.NaN
deep=len(df.index)
irange= np.arange(1,deep)
for i in irange:
G=df.time[df.running_bid_max.shift(i)-df.ask_price_target_good>=0]
G.index=G.index-i
df['g']=df['g'].combine_first(G)
L=df.time[df.running_bid_min.shift(i)-df.ask_price_target_bad<=0]
L.index=L.index-i
df['l']=df['l'].combine_first(L)
Вы можете изменить параметр глубины (временное окно)
Это может быть оптимизировано
Ответ 5
Цель
создание нового столбца меток времени, когда running_bid_max
больше или равно значению в ask_price_target_good
. Затем создайте отдельный столбец меток времени, когда running_bid_min
меньше или равно ask_price_target_bad
Попробуйте это:
import numpy as np
# Setup conditions
conditions = [
(df['running_bid_max'] >= df['ask_price_target_good']),
(df['running_bid_min'] >= df['ask_price_target_bad'])]
# Setup output (you could insert timestamp var here)
choices = ["Greater", "Lesser"]
# Apply conditions
df['bid_value'] = np.select(conditions, choices, default='N/A')
Надеюсь, это поможет обеспечить решение :)
Ответ 6
Вы можете просто использовать:
df['time_bid_max_greater'] = df.time[df['running_bid_max'] >= df['ask_price_target_good']]
df['time_bid_min_less'] = df.time[df['running_bid_min'] <= df['ask_price_target_bad']]
df
Ответ 7
Хорошо, я думаю, что понимаю. Вы хотите, чтобы значение для каждой строки было отметкой времени следующей даты, когда выполняются эти условия? Если это так, вы можете построить ответ Quang. В частности, давайте все еще делаем
df['g'] = np.where(df.running_bid_max.ge(df.ask_price_target_good), df['time'], pd.NaT)
df['l'] = np.where(df.running_bid_min.le(df.ask_price_target_bad), df['time'], pd.NaT)
Теперь мы можем сделать:
df['g'] = df['g'].fillna(method='bfill')
df['l'] = df['l'].fillna(method='bfill')
Теперь у вас есть метка времени из строки 9 в строке 0.
Это то, что вы ищите?