Pandas:一旦列达到另一列的某个值,我如何返回行值?

Posted

技术标签:

【中文标题】Pandas:一旦列达到另一列的某个值,我如何返回行值?【英文标题】:Pandas: How do I return a row value once a column reaches a certain value of another column? 【发布时间】: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 列中的值移到 *** 之外。 现在好多了。我可以用一个命令复制和重现数据帧。 如果先发生相反的情况怎么办?您想要那个时间戳还是什么都不想要? 您能否阐明您想要的数据输出?您的第一个条件出现在索引 9 处,而您的第二个条件出现在索引 1 处,因此第一个 不会 出现在第二个之前。 【参考方案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 行的时间戳。

这是你要找的吗?

【讨论】:

以上是关于Pandas:一旦列达到另一列的某个值,我如何返回行值?的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 表示基于另一列的某些列(二进制值)

用 Pandas 将 DataFrame 中某些列和行的值替换为同一 DataFrame 中另一列的值

当percentile_approx基于groupby返回特定列的单个值时,如何选择另一列的对应值?

要检查 Pandas Dataframe 列的 TRUE/FALSE,如果 TRUE 检查另一列是不是满足条件并生成具有值 PASS/FAIL 的新列

Pandas - 基于 str 包含从另一列创建新列/值

在行值更改后,在另一列中返回一行