【问题标题】:Pandas: How do I return a row value once a column reaches a certain value of another column?Pandas:一旦列达到另一列的某个值,我如何返回行值?
【发布时间】:2019-12-02 14:58:07
【问题描述】:

以下是数据示例:

目标:
running_bid_max 大于或等于ask_price_target_good 中的值时,创建一个新的时间戳列。然后为running_bid_min 小于或等于 ask_price_target_bad 时创建一个单独的时间戳列。

注意:这将在大量数据上执行,需要尽快计算。我希望我不必通过iterrows() 遍历所有行

running_bid_minrunning_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

【问题讨论】:

  • 包含样本数据的图像不是一个好主意。您应该将数据头打印到终端并复制/粘贴它。这样人们就可以在数据上尝试他们的代码并更好地帮助您。
  • @QuangHoang 我用打印的数据进行了更新,它的格式是我能得到的最好的。我不能让任何其他行在不移位的情况下移位。如果您愿意,请随时尝试修复它。应该很容易将最后 2 列中的值移到 stackoverflow 之外。
  • 现在好多了。我可以用一个命令复制和重现数据帧。
  • 如果先发生相反的情况怎么办?您想要那个时间戳还是什么都不想要?
  • 您能否阐明您想要的数据输出?您的第一个条件出现在索引 9 处,而您的第二个条件出现在索引 1 处,因此第一个 不会 出现在第二个之前。

标签: python python-3.x pandas performance time


【解决方案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)

还是我错过了什么?


更新:您可能希望在上述命令之后 ffillbfill

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

【讨论】:

  • 我认为这只是检查同一行中的值。我需要它来检查未来的日期,而不仅仅是当前行。因此,例如,如果您在索引 0 处获取值。我希望从索引 1 返回时间戳,因为“running_bid_max”大于或等于“ask_price_target_good”中的值 在上面的数据中,索引 0 不会能够返回“running_bid_min”小于或等于“ask_price_target_bad”的时间戳,因为在那之后永远不会发生这种情况。
【解决方案2】:

我不确定我是否正确理解了您的问题。我在下面提供了以下问题的解决方案:

  • 对于给定的行(我将其称为当前行),我们保留其时间的所有行 介于本行时间和本行时间加 5 分钟之间
  • 在我们保留的行中,我们搜索running_bid_max 是否可能优于 我们在当前行的ask_price_target_good 列中的值
  • 如果是这样,我们保持running_bid_max 的第一次出现优于 当前行的ask_price_target_good

在您的示例中,对于行0,我们在ask_price_target_good 中有291.46。在行8(其时间在行0的5分钟内),我们找到291.51(优于291.46),因此我们希望将此值保留为行0

必须对 running_bid_min 执行对称操作,必须测试其劣于 ask_price_target_bad

为了解决这个问题,我写了下面的代码。我没有使用iterrows,而是DataFrameapply 函数。不过,对于每一行,在搜索可能优于ask_price_target_good 的行之前,我需要从整个数据帧(5 分钟时间窗口)中选择一堆行。如果您有大型数据框,我希望这会足够快。

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

【讨论】:

    【解决方案3】:

    试试这个:

    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)
    

    可以更改深度参数(时间窗口)

    可以优化

    【讨论】:

      【解决方案4】:

      如果您可以打印所需的输出,那就太好了。否则我可能会错过逻辑。

      如果您正在处理大量数据,则可以申请steaming analytics*。 (这将非常节省内存,如果您使用 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)]
      

      还要注意,创建个人DataFrames 很容易

      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
      

      不幸的是,我无法为 DataFrame 找到一个单一的班轮 pytition_by。它肯定隐藏在某个地方。 (但同样,pandas 通常会将所有数据加载到内存中 - 如果您想在 I/O 期间进行聚合,那么流式传输可能是一种方法。)


      *流媒体示例

      例如,让我们创建一个简单的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}
      

      【讨论】:

        【解决方案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
          

          【讨论】:

          • @bbennett36 我只是复制你给定的数据,直到我得到大约 100 万行,并且这个过程持续了不到 80 毫秒
          【解决方案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')
          

          现在您将得到第 0 行第 9 行的时间戳。

          这是你要找的吗?

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-12-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-01-17
            相关资源
            最近更新 更多