【问题标题】:find the minimum value of a column between two occurrences of a value in another column in a Python data frame在 Python 数据框中的另一列中找到两次出现的值之间的列的最小值
【发布时间】:2019-05-09 14:28:27
【问题描述】:

我有每天包含开盘价、最高价、最低价、收盘价的股票价格数据。我正在创建一个新列“信号”,它将根据某些条件采用值“信号”或“无”。

每次df['signal']=="signal",我们都必须将它与df['signal']=="signal"的前3次出现进行比较。让我们想象当前发生的事件是第四个信号。因此,之前出现的df['signal']=="signal" 将是第三个信号,偶数之前的将是第二个信号,之前的信号将是第一个信号。

我需要检查信号 4 和信号 3 之间的 df['low'] 的最小值是否大于信号 1 和信号 2 之间的 df['low'] 的最小值。

如果更大,我需要一个新列 df['trade']=="Buy"。

Sample data

No Open High Low Close signal Trade 

1   75   95   65  50    signal
2   78   94   74  77    none
3   83   91   81  84    none
4   91   101  88  93    signal
5   104  121  95  103   none
6   101  111  99  105   none
7   97   108  95  101   signal
8   103  113  102 106   none
9   108  128  105 114   signal  BUY
10  104  114  99  102   none
11  110  130  105 115   signal  BUY
12  112  122  110 115   none
13  118  145  112 123   none
14  123  143  71  133   signal  NONE
15  130  150  120 140   none

在上面的示例数据中,在第 9 行中,df['Trade']=="BUY" 发生,因为 df['Low']=95 的最小值介于此 df['signal']="signal " 和之前的 df['signal']="signal" 大于 df['signal']="signal" 的前两次出现之间的 df['Low']= 65 的最小值。

同样,在第 14 行,df['Trade']="None" 发生,因为此信号和前一个信号之间的 df['Low']=71 的最小值不大于 df[ 的最小值'Low']=99 在前两个信号之间。

我需要代码方面的帮助来实现这一点。


    import pandas as pd
    import numpy as np
    import bisect as bs

    df = pd.read_csv("Nifty.csv")
    cols = ['No', 'Low', 'signal']
    df['5EMA'] = df['Close'].ewm(span=5).mean()
    df['10EMA'] = df['Close'].ewm(span=10).mean()
    condition1 = df['5EMA'].shift(1) < df['10EMA'].shift(1)
    condition2 = df['5EMA'] > df['10EMA']
    df['signal'] = np.where(condition1 & condition2, 'signal', None)
    df1 = pd.concat([df[cols], df.loc[df.signal=='signal',cols].assign(signal='temp')]) \
            .sort_values(['No', 'signal'],ascending=[1,0])
    df1['g'] = (df1.signal == 'signal').cumsum()
    df1['Low_min'] = df1.groupby('g').Low.transform('min')
    s = df1.groupby('g').Low.min()
    buy = s[s.shift(1) > s.shift(3)].index.tolist()
    m1 = df1.signal.eq('signal') & df1.g.gt(3)
    m2 = df1.g.isin(buy) & m1
    df1['trade'] = np.select([m2, m1], ['Buy', 'None'], '')
    df['trade'] = ''
    df.trade.update(df1.loc[df1.signal=='signal',"trade"])
    print(df)

【问题讨论】:

  • 添加一些示例数据,主要是 5-10 行就足够了。比通读代码更容易直观地看到您尝试在数据中执行的操作。查找有关提出“好”熊猫问题的更多信息here
  • @Erfan 您好,感谢您的回复。我添加了示例数据来解释我打算做什么。

标签: python pandas trading algorithmic-trading


【解决方案1】:

添加一些额外的临时行后,您的问题可以得到简化。我设置了一个新数据框,其中仅包含原始 df 中的必填字段,并克隆了所有标记为“信号”但重命名为“临时”df.loc[df.signal=='signal',cols].assign(signal='temp') 的行。然后将使用“信号”和 cumsum() 对已排序的行进行分组标记。见以下代码:

str="""No Open High Low Close signal 
1   75   95   65  50    signal 
2   78   94   74  77    none 
3   83   91   81  84    none 
4   91   101  88  93    signal 
5   104  121  95  103   none 
6   101  111  99  105   none 
7   97   108  95  101   signal 
8   103  113  102 106   none 
9   108  128  105 114   signal 
10  104  114  99  102   none 
11  110  130  105 115   signal 
12  112  122  110 115   none 
13  118  145  112 123   none 
14  123  143  71  133   signal 
15  130  150  120 140   none"""

df = pd.read_csv(pd.io.common.StringIO(str), sep='\s+')

# cols which are used in this task 
cols = ['No', 'Low', 'signal']

# create a new dataframe, cloned all 'signal' rows but rename signal to 'temp', sort the rows
df1 = pd.concat([df[cols], df.loc[df.signal=='signal',cols].assign(signal='temp')]) \
        .sort_values(['No', 'signal'],ascending=[1,0])

# set up group-number with cumsum() and get min() value from each group
df1['g'] = (df1.signal == 'signal').cumsum()
# the following field just for reference, no need for calculation
df1['Low_min'] = df1.groupby('g').Low.transform('min')

新的数据框df1 如下所示。除了第一组和最后一组,现在每个组都以“信号”开始,以“温度”(也是“信号”)结束:

根据您的描述,对于第 9 行(黄色背景,df1.g==4 中的第一项),我们可以检查df1.loc[df1.g==3, "Low_min"](红色边框)对照 df1.loc[df1.g==1, "Low_min"](绿色边框)

如果我们有以下情况:

s = df1.groupby('g').Low.min()

buy group的列表应该满足s.shift(1) > s.shift(3)

buy = s[s.shift(1) > s.shift(3)].index.tolist()

那么,我们来设置条件:

# m1: row marked with signal
# skip the first 3 groups which do not have enough signals
m1 = df1.signal.eq('signal') & df1.g.gt(3)

# m2: m1 plus must in buy list
m2 = df1.g.isin(buy) & m1
df1['trade'] = np.select([m2, m1], ['Buy', 'None'], '')
#In [36]: df1
#Out[36]: 
#    No  Low  signal  g  Low_min trade
#0    1   65    temp  0       65      
#0    1   65  signal  1       65      
#1    2   74    none  1       65      
#2    3   81    none  1       65      
#3    4   88    temp  1       65      
#3    4   88  signal  2       88      
#4    5   95    none  2       88      
#5    6   99    none  2       88      
#6    7   95    temp  2       88      
#6    7   95  signal  3       95      
#7    8  102    none  3       95      
#8    9  105    temp  3       95      
#8    9  105  signal  4       99   Buy
#9   10   99    none  4       99      
#10  11  105    temp  4       99      
#10  11  105  signal  5       71   Buy
#11  12  110    none  5       71      
#12  13  112    none  5       71      
#13  14   71    temp  5       71      
#13  14   71  signal  6       71  None
#14  15  120    none  6       71      

有了df1.trade之后,我们就可以更新原来的dataframe了:

# set up column `trade` with EMPTY as default and update 
# the field based on df1.trade (using the index)
df['trade'] = ''
df.trade.update(df1.loc[df1.signal=='signal',"trade"])

【讨论】:

  • 嗨,Jxc,我已经更新了我的代码。我在 df = pd.read_csv(pd.io.common.StringIO(str), sep='\s+') 中遇到错误,说 initial_value 必须是 str 或 None,而不是类型。你能帮我完成这个吗?
  • 嗨,@Kartick,您需要从文件“Nifty.csv”创建您的 df,而不是我示例中的字符串。例如,将您的代码更改为:df = pd.read_csv("Nifty.csv")
  • 哦,明白了。实际上,我添加“否”列只是为了解释这个问题。它实际上不存在于我的数据框中。您能告诉我如何添加该列吗?我目前拥有的代码已在问题中更新。
  • @Kartick,您可以使用 Pandas 数据框的默认索引为No。检查您是否通过df = df.reset_index().rename(columns={'index':'No'}) 获得No 列。
  • 非常感谢......你帮了大忙。被这个卡住了几个月:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 2019-06-11
  • 2022-06-16
  • 1970-01-01
相关资源
最近更新 更多