将函数应用于多索引多列数据帧的 Pythonic 方法是啥?

Posted

技术标签:

【中文标题】将函数应用于多索引多列数据帧的 Pythonic 方法是啥?【英文标题】:What is the Pythonic way to apply a function to multi-index multi-columns dataFrame?将函数应用于多索引多列数据帧的 Pythonic 方法是什么? 【发布时间】:2015-12-20 05:18:08 【问题描述】:

给定下面的多索引多列数据框,我想将 LinearRegression 应用于此数据框的每个块,例如“索引(X,1),A 列”。并将预测的数据帧计算为 df_result。

                           A    B
X   1   1997-01-31  -0.061332   0.630682
        1997-02-28  -2.671818   0.377036
        1997-03-31  0.861159    0.303689
        ...
        1998-01-31  0.535192    -0.076420
        ...
        1998-12-31  1.430995    -0.763758
Y   1   1997-01-31  -0.061332   0.630682
        1997-02-28  -2.671818   0.377036
        1997-03-31  0.861159    0.303689
        ...
        1998-01-31  0.535192    -0.076420
        ...
        1998-12-31  1.430995    -0.763758

这是我尝试过的:

import pandas as pd
import numpy as np 
from sklearn.linear_model import LinearRegression

N = 24
dates = pd.date_range('19970101', periods=N, freq='M')
df=pd.DataFrame(np.random.randn(len(dates),2),index=dates,columns=list('AB')) 
df2=pd.concat([df,df],keys=[('X','1'),('Y','1')])

regr = LinearRegression()

# df_result will be reassined, copy the index and metadata from df2
df_result=df2.copy()

# I know the double loop below is not a clever idea. What is the right way?
for row in df2.index.to_series().unique():
    for col in df2.columns:
        #df2 can contain missing values
        lenX=np.count_nonzero(df2.ix[row[:1],col].notnull().values.ravel())
        X=np.array(range(lenX)).reshape(lenX,1)
        y=df2.ix[row[:1],col]
        y=y[y.notnull()]

        # train the model
        regr.fit(X,y)

        df_result.ix[row[:1],col][:lenX] = regr.predict(X)

问题是上面的双循环使得计算速度很慢,100kb的数据集要十多分钟。这样做的pythonic方法是什么?

编辑:

上面代码最后一行的第二个问题是我正在使用数据帧切片的副本。 “df_result”的某些列未通过此操作更新。

EDIT2:

原始数据的某些列可能包含缺失值,我们不能直接对它们应用回归。例如,

df2.ix[('X','1','1997-12-31')]['A']=np.nan
df2.ix[('Y','1','1998-12-31')]['A']=np.nan

【问题讨论】:

所以你想在 0 级执行线性回归对吗?所以 X 的所有行或第 1 级的所有行,所以该级别的每个组? @EdChum 我想在每一列的级别 (0,1) 上执行 LR,日期范围始终是从 1997-01-31 到 1998-12-31。 【参考方案1】:

我不太了解行循环。

无论如何,为了保持数字的一致性,我将np.random.seed(1) 放在顶部

简而言之,我认为您可以使用函数、groupby 和调用 .transform() 来实现您想要的。

def do_regression(y):
    X=np.array(range(len(y))).reshape(len(y),1)
    regr.fit(X,y)
    return regr.predict(X)

df_regressed = df2.groupby(level=[0,1]).transform(do_regression)

print df_regressed.head()

                       A         B
X 1 1997-01-31  0.779476 -1.222119
    1997-02-28  0.727184 -1.138630
    1997-03-31  0.674892 -1.055142
    1997-04-30  0.622601 -0.971653
    1997-05-31  0.570309 -0.888164

与您的 df_result 输出相匹配。

print df_result.head()

                       A         B
X 1 1997-01-31  0.779476 -1.222119
    1997-02-28  0.727184 -1.138630
    1997-03-31  0.674892 -1.055142
    1997-04-30  0.622601 -0.971653
    1997-05-31  0.570309 -0.888164

哦,还有几个替代方案:

X=np.array(range(len(y))).reshape(len(y),1)

1.) X = np.expand_dims(range(len(y)), axis=1)
2.) X = np.arange(len(y))[:,np.newaxis]

编辑空数据

好的 2 条建议:

使用 interpolate 方法填充空值是否合法?

df2 = df2.interpolate()

对非空值进行回归,然后在适当的索引位置弹出空值

   def do_regression(y):

        x_s =np.arange(len(y))
        x_s_non_nulls =  x_s[y.notnull().values]
        x_s_non_nulls = np.expand_dims(x_s_non_nulls, axis=1)

        y_non_nulls = y[y.notnull()]  # get the non nulls

        regr.fit(x_s_non_nulls,y_non_nulls)  # regression
        results = regr.predict(x_s_non_nulls)

        #pop back in then nulls.
        for idx in np.where(y.isnull().values ==True):
            results = np.insert(results,idx,np.NaN)

        return results

【讨论】:

还有一个问题是某些列包含缺失值,即 do_regression(y) 中的 y,而 regr.fit(X,y) 引发了“NAN”错误。这就是我在原始问题中使用 notnull() 和 lenX 的原因。转换函数似乎以所有列相同的索引方式工作,但不是逐列。还有什么提示吗? 嗨,Rex,我有点不清楚,你能发布更多信息/示例数据吗?

以上是关于将函数应用于多索引多列数据帧的 Pythonic 方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将熊猫多索引数据框重塑为多列

将多索引数据帧的索引值提取为python中的简单列表

Pandas,将多索引之一移动到多列索引之上

将熊猫多索引切片彼此分开

多索引 df 中的线性回归

Pandas:组合多索引数据帧的标题行