web-dev-qa-db-fra.com

Pandas: Comment puis-je retourner une valeur de ligne une fois qu'une colonne atteint une certaine valeur d'une autre colonne?

Voici un échantillon des données:

enter image description here

Objectif:
créez une nouvelle colonne d'horodatage lorsque running_bid_max supérieur ou égal à la valeur de ask_price_target_good . Créez ensuite une colonne d'horodatage distincte lorsque running_bid_min Est inférieur ou égal à ask_price_target_bad.

Remarque : Cela sera effectué sur une grande quantité de données et de besoins calculés aussi rapidement que possible. J'espère ne pas avoir à parcourir toutes les lignes via iterrows()

running_bid_min Et running_bid_max Sont calculés à l'aide d'une running.min() et pd.running.max() à partir d'un certain laps de temps à l'avenir (cet exemple utilise une chronologie de 5 minutes. Ce sera donc le min, max 5 minutes de l'heure actuelle)

copiez les données ci-dessous puis utilisez 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
14
bbennett36

De votre question:

création d'une nouvelle colonne d'horodatage pour quand running_bid_max supérieur ou égal à la valeur dans ask_price_target_good. Créez ensuite une colonne d'horodatage distincte pour quand running_bid_min est inférieur ou égal à ask_price_target_bad

le problème semble trivial:

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)

Ou est-ce que je manque quelque chose?


Mise à jour: vous voudrez peut-être ffill et bfill après les commandes ci-dessus:

df['g'] = df['g'].bfill()
df['l'] = df['l'].ffill()

Sortie, par exemple 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
11
Quang Hoang

Ce serait très bien si vous pouviez imprimer la sortie souhaitée. Sinon, je risque de manquer la logique.

Si vous travaillez sur une grande quantité de données, il est logique d'appliquer analyse à la vapeur *. (Cela sera assez efficace en mémoire et si vous utilisez cytoolz même 2 à 4 fois plus vite)

Donc, fondamentalement, vous souhaitez partitionner vos données en fonction de l'une ou l'autre condition:

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)

Tout ce que vous ferez avec des partitions individuelles vous appartient (vous pouvez créer des champs ou des colonnes supplémentaires, etc.).

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)]

Notez également qu'il est facile de créer des DataFrames individuels

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

Malheureusement, je n'ai pas trouvé de doublure pytition_by pour DataFrame. Il est sûrement caché quelque part. (Mais encore une fois, pandas charge généralement toutes les données en mémoire - si vous souhaitez agréger pendant les E/S, la diffusion en continu peut être une solution.)


* Exemple de streaming

Par exemple, laissez-nous créer un simple flux 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))

qui produit une ligne traitée à la fois:

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}
5
Drey

Essaye ça:

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)

Vous pouvez changer le paramètre profond (fenêtre de temps)

Il peut être optimisé

4
Andrea Mannari

Je ne suis pas sûr de bien comprendre votre problème. Je fournis ci-dessous une solution au problème suivant:

  • Pour une ligne donnée (que j'appellerai la ligne courante), on garde toutes les lignes dont le temps est compris entre le temps de cette ligne et le temps de cette ligne plus 5 minutes
  • Dans les lignes que nous avons conservées, nous recherchons si running_bid_max pourrait être supérieur à la valeur que nous avons dans le ask_price_target_good colonne de la ligne courante
  • Si c'est le cas, nous gardons la première occurrence de running_bid_max supérieur à ask_price_target_good de la ligne actuelle

Dans votre exemple, pour la ligne 0, on a 291.46 dans ask_price_target_good. À la ligne 8 (dont le temps dans le délai de 5 minutes à partir de l'heure de la ligne0), nous trouvons 291.51 (qui est supérieur à 291.46) et nous souhaitons donc conserver cette valeur pour la ligne 0.

Une opération symétrique doit être effectuée pour running_bid_min qui doit être testé pour être inférieur à ask_price_target_bad.

Pour résoudre ce problème, j'ai écrit le code suivant. Je n'utilise pas iterrows mais la fonction apply de DataFrame. Néanmoins, je dois, pour chaque ligne, sélectionner un groupe de lignes dans l'ensemble de la trame de données (la fenêtre de temps de 5 minutes) avant de rechercher les lignes qui pourraient être supérieures à ask_price_target_good. J'espère que ce sera assez rapide si vous avez de grandes trames de données.

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
Olivier CAYROL

vous pouvez simplement utiliser:

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

1
kederrac

Objectif

création d'une nouvelle colonne d'horodatage pour quand running_bid_max supérieur ou égal à la valeur dans ask_price_target_good. Créez ensuite une colonne d'horodatage distincte pour quand running_bid_min est inférieur ou égal à ask_price_target_bad

Essaye ça :

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')

J'espère que cela aide à fournir une solution :)

1
Anthony R

D'accord, donc je pense que je comprends. Vous souhaitez que la valeur de chaque ligne soit l'horodatage de la prochaine date à laquelle ces conditions sont remplies? Si c'est le cas, vous pouvez construire à partir de la réponse de Quang. Plus précisément, laisse encore faire

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)

Maintenant, nous pouvons faire:

 df['g'] = df['g'].fillna(method='bfill')
 df['l'] = df['l'].fillna(method='bfill')

Vous vous retrouvez maintenant avec l'horodatage de la ligne 9 à la ligne 0.

Est-ce ce que vous recherchez?

1
hchw