在Pandas中对一列进行缩减

6
我试图将一列或多列的返回数据转换为收盘价列。在Clojure中,我会使用reductions,它类似于reduce,但返回所有中间值的序列。
例如:
$ c

0.12
-.13
0.23
0.17
0.29
-0.11

# something like this
$ c.reductions(init=1, lambda accumulator, ret: accumulator * (1 + ret)) 

1.12
0.97
1.20
1.40
1.81
1.61

NB: 实际的收盘价并不重要,因此使用1作为初始值。我只需要一个“模拟”收盘价。

我的数据实际结构是一列名为TimeSeries的DataFrame。我猜我正在寻找类似于applymap的函数,但我不想使用该函数进行某些hacky操作,并从其中引用DF(这可能是解决此问题的一种方法?)

另外,如果我想保留returns数据,但同时也需要收盘“价格”,我应该怎么做?我是否应该返回一个元组,使TimeSeries的类型为(returns, closing_price)


啊,函数本身不是问题(应该在上面写清楚);那很容易。对我来说,问题在于将函数习惯地、正确地应用到DataFrame的列上。不过还是谢谢你! - Isaac
2
你对问题的描述有点抽象难懂。你能否提供实际格式的样本数据,并且给出你想要它实际执行的示例? - BrenBarn
3个回答

6

目前似乎还没有广泛宣传此功能,但您可以使用 expanding_apply 来计算收益:

In [1]: s
Out[1]:
0    0.12
1   -0.13
2    0.23
3    0.17
4    0.29
5   -0.11

In [2]: pd.expanding_apply(s ,lambda s: reduce(lambda x, y: x * (1+y), s, 1))

Out[2]:
0    1.120000
1    0.974400
2    1.198512
3    1.402259
4    1.808914
5    1.609934

我不是100%确定,但我认为expanding_apply适用于从第一个索引到当前索引开始的应用序列。我使用内置的reduce函数,它与您的Clojure函数完全相同。

expanding_apply的文档字符串:

Generic expanding function application

Parameters
----------
arg : Series, DataFrame
func : function
    Must produce a single value from an ndarray input
min_periods : int
    Minimum number of observations in window required to have a value
freq : None or string alias / date offset object, default=None
    Frequency to conform to before computing statistic
center : boolean, default False
    Whether the label should correspond with center of window

Returns
-------
y : type of input argument

这是一个非常有趣的函数,我期待着与它玩耍。谢谢。 - Andy Hayden
谢谢!这个方法很好用,但是我需要想办法处理NaN数据(现在只是使用一个简单的检查,例如 lambda x, y: x * (1+y) if pd.notnull(y) else x,但这不是一个很好的做法,显然有明显的缺陷... - Isaac

4
值得注意的是,在pandas中,写得更加冗长通常会更快(也更容易理解),而不是像使用reduce一样简洁。
在您的特定示例中,我只会add然后cumprod
In [2]: c.add(1).cumprod()
Out[2]: 
0    1.120000
1    0.974400
2    1.198512
3    1.402259
4    1.808914
5    1.609934

或者使用init*c.add(1).cumprod()

注意:在某些情况下,例如内存不足的情况下,您可能需要以更低级别/更聪明的方式重写这些代码,但通常首先尝试最简单的方法(并使用%timeit或分析内存进行测试)。


值得注意的是,即使有1000个条目,在这个特定的例子中,它比上面的“expanding_apply”要快得多。 - Andy Hayden
啊,现在正在更新pandas到0.10.1版本;这应该可以解决这个问题。 - Isaac
你可以使用(c+1).cumprod()。我对Andy的解决方案也遇到了同样的错误+1。它比我提出的方法快得多。 - Zelazny7
@Zelazny7 当我输入这个时,我认为你的方法可能会更快(我可以想象一些例子,在那里它可能会更快),但首先尝试最简单(最符合Python风格?)的方法绝对是一个好主意 :) - Andy Hayden

0

为了可读性,我更喜欢以下解决方案:

returns = pd.Series([0.12, -.13, 0.23, 0.17, 0.29, -0.11])

initial_value = 100
cum_growth = initial_value * (1 + returns).cumprod()

>>> cum_growth
0    112.000000
1     97.440000
2    119.851200
3    140.225904
4    180.891416
5    160.993360
dtype: float64

如果您想在序列中包含初始值:
>>> pd.concat([pd.Series(initial_value), cum_growth]).reset_index(drop=True)
0    100.000000
1    112.000000
2     97.440000
3    119.851200
4    140.225904
5    180.891416
6    160.993360
dtype: float64

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接