Панды: Как я могу вернуть значение строки, когда столбец достигает определенного значения другого столбца?

Вот образец данных:

enter image description here

Цель:
создайте новый столбец отметки времени, когда 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

enter image description here

Ответ 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.

Это то, что вы ищите?