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_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
【问题讨论】:
包含样本数据的图像不是一个好主意。您应该将数据头打印到终端并复制/粘贴它。这样人们就可以在数据上尝试他们的代码并更好地帮助您。 @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)
还是我错过了什么?
更新:您可能希望在上述命令之后 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
【讨论】:
我认为这只是检查同一行中的值。我需要它来检查未来的日期,而不仅仅是当前行。因此,例如,如果您在索引 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
,而是DataFrame
的apply
函数。不过,对于每一行,在搜索可能优于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)]
还要注意,创建个人DataFrame
s 很容易
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 将 DataFrame 中某些列和行的值替换为同一 DataFrame 中另一列的值
当percentile_approx基于groupby返回特定列的单个值时,如何选择另一列的对应值?
要检查 Pandas Dataframe 列的 TRUE/FALSE,如果 TRUE 检查另一列是不是满足条件并生成具有值 PASS/FAIL 的新列