将计算出的列添加到熊猫数据框中


78

我有一个OHLC价格数据集,该数据集已从CSV解析为Pandas数据框,并重新采样为15分钟的柱形:

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 500047 entries, 1998-05-04 04:45:00 to 2012-08-07 00:15:00
Freq: 15T
Data columns:
Close    363152  non-null values
High     363152  non-null values
Low      363152  non-null values
Open     363152  non-null values
dtypes: float64(4)

我想添加各种计算出的列,从简单的列开始,例如周期范围(HL),然后是布尔值以指示我将定义的价格模式的出现-例如锤形蜡烛模式,为其定义示例:

def closed_in_top_half_of_range(h,l,c):
    return c > l + (h-l)/2

def lower_wick(o,l,c):
    return min(o,c)-l

def real_body(o,c):
    return abs(c-o)

def lower_wick_at_least_twice_real_body(o,l,c):
    return lower_wick(o,l,c) >= 2 * real_body(o,c)

def is_hammer(row):
    return lower_wick_at_least_twice_real_body(row["Open"],row["Low"],row["Close"]) \
    and closed_in_top_half_of_range(row["High"],row["Low"],row["Close"])

基本问题:如何将函数映射到列,特别是在我想引用多个其他列或整行或其他内容的地方?

这篇文章处理的是从单个源列中添加两个计算列,这是很接近的,但还不完全是。

稍微先进一点:对于参照多个条形(T)确定的价格模式,我如何从函数定义中引用不同的行(例如T-1,T-2等)?

Answers:


75

确切的代码对于您要执行的每个列都会有所不同,但是您可能需要使用mapandapply函数。在某些情况下,您可以直接使用现有的列进行计算,因为这些列是Pandas系列对象,它们也可以作为Numpy数组使用,对于常规的数学运算,它们会自动以元素方式工作。

>>> d
    A   B  C
0  11  13  5
1   6   7  4
2   8   3  6
3   4   8  7
4   0   1  7
>>> (d.A + d.B) / d.C
0    4.800000
1    3.250000
2    1.833333
3    1.714286
4    0.142857
>>> d.A > d.C
0     True
1     True
2     True
3    False
4    False

如果需要在一行中使用诸如max和min之类的操作,则可以使用applywithaxis=1将您喜欢的任何函数应用于每一行。这是一个计算的示例min(A, B)-C,它看起来像您的“下芯”:

>>> d.apply(lambda row: min([row['A'], row['B']])-row['C'], axis=1)
0    6
1    2
2   -3
3   -3
4   -7

希望这使您对如何进行有了一些了解。

编辑:将行与相邻行进行比较,最简单的方法是对要比较的列进行切片,不使用开始/结尾,然后比较所得切片。例如,这将告诉您A列中的元素的哪些行小于C列中下一行的元素:

d['A'][:-1] < d['C'][1:]

这是另一种方式,告诉您哪些行的A小于前一行的C:

d['A'][1:] < d['C'][:-1]

这样做['A"][:-1]切片断列A的最后一个元素,并做['C'][1:]切片关闭C列的第一个元素,所以你排队的这两个并比较他们,你是在一个每个元素比较从以下行℃。


47

您可能会is_hammerrow["Open"]等方面如下

def is_hammer(rOpen,rLow,rClose,rHigh):
    return lower_wick_at_least_twice_real_body(rOpen,rLow,rClose) \
       and closed_in_top_half_of_range(rHigh,rLow,rClose)

然后,您可以使用map:

df["isHammer"] = map(is_hammer, df["Open"], df["Low"], df["Close"], df["High"])

2
也很有用,非常感谢。剥皮猫的方法很多。我想给你点赞,但这是我对StackOverflow的第一个问题,可惜我没有足够的代表。我不认为您在第二部分上有nny个想法,即从map / apply函数中引用数据框中的相邻行吗?再次欢呼。
ultra909 2012年

1
由于某种原因,使用map(f, col1, col2)的方法比的方法快df.apply(..., axis=1)。对于1M行数据帧,map需要0.35s vs df.apply需要26s。知道为什么吗?(python 2.7和pandas 0.18.1)
MohamedEzz

有时这很好用,但有时我会收到警告:“试图在DataFrame的切片副本上设置一个值。尝试改用.loc [row_indexer,col_indexer] = value” ...有什么见解?
elomage

1
@elomage尚不清楚,没有更多信息。也许在您开始时先对我们的一些数据框进行切片,然后在此视图中进行分配。您可以使用df = df.copy()上面的方法将视图制作为副本...但这可以作为一个新问题更好地回答。
安迪·海登

@AndyHayden df.copy()做到了,谢谢。我以前做过切片。对于较小的数据帧没有问题,对于较大的数据帧我必须执行copy()。
elomage '18


1

您列出的前四个函数也将适用于向量,但需要修改lower_wick。像这样

def lower_wick_vec(o, l, c):
    min_oc = numpy.where(o > c, c, o)
    return min_oc - l

其中o,l和c是向量。您可以用这种方式来代替,尽管它会更慢:

def lower_wick_df(df):
    min_oc = df[['Open', 'Close']].min(axis=1)
    return min_oc - l

其他三个将按原样在列或向量上工作。然后,您可以完成

def is_hammer(df):
    lw = lower_wick_at_least_twice_real_body(df["Open"], df["Low"], df["Close"]) 
    cl = closed_in_top_half_of_range(df["High"], df["Low"], df["Close"])
    return cl & lw

位运算符可以对布尔向量&for and|foror等执行设置逻辑。这足以完全向量化您给出的样本计算,并且应该相对较快。在执行这些计算时,通过临时处理数据基础的numpy数组,您可能会进一步提高速度。

对于第二部分,我建议引入一列,指示每一行的模式,并编写处理每个模式的函数系列。然后对模式进行分组,并将适当的功能应用于每个组。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.