scipy 对未定义列数的PandasGroupBy执行ANOVA和平均值

2eafrhcq  于 2022-11-09  发布在  其他
关注(0)|答案(2)|浏览(132)

我需要创建一个多功能函数,并将其传输到pandas DataFrame中,以便对未指定数量的组和子组运行ANOVA。我有一些DataFrame,其中包含两个分类列(这里是cat1cat2)和一些连续列(这里只有contin_1contin2)。

cat1 cat2      contin_Z       contin_Y    ...
0         A   1             08             33
1         B   1             00             86
2         C   2             85             65
3         A   1             82             08
4         A   2             90             85
5         A   3             93             93
6         A   2             13             65
7         A   1             33             90
8         B   2             00             10
9         C   2             58             57
10        C   1             68             68
11        B   1             43             40
12        A   1             35            NaN
13        A   3             75             40
14        A   3             68             53
15        A   2             93             93
16        B   3             18             65
17        C   3             33             28
18        A   1             50             94
19        B   1             25             90

对于上面的示例数据,我希望按cat1对它进行分组,并对每个cat1组的所有cat2组进行ANOVA,同时输出每个cat1/cat2组合的平均值。我希望能够在不考虑连续列数的情况下进行此操作--有点像pandas.DataFrame.groupby.mean可以对任意数量的列进行操作。
输出应如下所示,具有某种形状:(这是一个示例输出,只是为了显示我正在寻找的输出的形状;平均值和p值是无意义值。

cat1   cat2  Z_mean   Z_anova_pval    Y_mean   Y_anova_pval    ...
0      A      1     54           .005         43           .076
1      A      2     73           .005         34           .076
2      A      3     34           .005         42           .076
3      B      1     76           .567         32           .002
4      B      2     98           .567         78           .002
5      B      3     73           .567        101           .002
6      C      1     84           .043         15           .041
7      C      2     23           .043         43           .041
8      C      3     82           .043         87           .041

我想这个函数应该是这样的:

data.groupby('cat1').pipe(f)

最接近我需要的函数是这行代码,它通过一个生成器表达式将所有cat2子组传递给f_oneway,但我不知道如何在cat1上的groupby中完成此操作,尤其是在获取平均值时,我想可能需要单独使用groupby.agg来完成此操作,但我不确定如何使其可伸缩到DataFrame中的任意多个连续列。

from scipy import stats
def run_anova(data):
   return stats.f_oneway(*(data[data.cat2==cat].dropna() for cat in data.cat2.unique()))

编辑:沿着这条线的东西似乎产生了一个准确的p值方面的结果。

from scipy.stats import f_oneway as anova

continuous_cols = [ ... list of all desired continuous cols ... ]

cats = data.cat1.unique()

pvals = (
    data
    .groupby(['cat2'])[continuous_cols]
    .agg(
        lambda x: 
        anova(*[x[data.cat1==cat].dropna() for cat in cats])[0]
    )
)

>         contin_Z   contin_Y
>  cat2
>     1       .045       .087
>     2       .654       .945
>     3       .943       .003

但是,我不知道如何将其与需要每个子组的平均值相结合(除了执行两个GroupBys,然后在重置平均值表上的一个索引级别后合并):

pvals = pvals.add_suffix('_pval')

means = data.groupby(['cat2', 'cat1'])[continuous_cols].mean()

means = means.add_suffix('_mean')

table = (
    pvals
    .merge(means.reset_index(level=1), left_index=True, right_index=True)
)
hivapdat

hivapdat1#

您已经对所涉及的步骤有了很好的了解。这里的要点是,平均值和p值是在两个不同的聚总层上计算的:

  • (cat2, cat1)水平计算平均值
  • cat2级别计算p值

data.groupby(...).pipe(f)的想法实际上并不可行,因为它将你限制在一个水平上。下面的解决方案将分别计算均值和p值,然后将它们结合在一起。
它不需要你事先列出连续变量,也不需要改变代码来容纳新变量,它只关心分类变量,这些变量在你的用例中是固定的。


# Move the category columns into the index so only we know that only continuos

# variables remain in the dataframe

tmp = data.set_index(["cat2", "cat1"])

# The means are easy to calculate. To distinguish them from the later pvalues,

# add a level to the column names

means = tmp.groupby(["cat2", "cat1"]).mean()
means.columns = pd.MultiIndex.from_product([["mean"], means.columns])

# Calculate the pvalues

from scipy.stats import f_oneway

cat1 = data["cat1"].unique()

def anova(group: pd.DataFrame):
    # We know that every column in `group` is a continuous variable. The
    # categorical variables have been moved to the index
    result = {
        ("pvalue", col): f_oneway(
            *[group.loc[(slice(None), cat), col].dropna() for cat in cat1]
        ).pvalue
        for col in group.columns
    }
    return pd.Series(result)

pvalues = tmp.groupby("cat2").apply(anova)

# Merge the results

result = pd.merge(means.reset_index("cat1"), pvalues, on="cat2").set_index("cat1", append=True)

结果:

mean             pvalue          
            contin_Z contin_Y  contin_Z  contin_Y
cat2 cat1                                        
1    A     41.600000    56.25  0.341883  0.856163
     B     22.666667    72.00  0.341883  0.856163
     C     68.000000    68.00  0.341883  0.856163
2    A     65.333333    81.00  0.392376  0.034400
     B      0.000000    10.00  0.392376  0.034400
     C     71.500000    61.00  0.392376  0.034400
3    A     78.666667    62.00  0.086695  0.610693
     B     18.000000    65.00  0.086695  0.610693
     C     33.000000    28.00  0.086695  0.610693
jdgnovmf

jdgnovmf2#

我不知道我是否理解了你的问题,但这能帮助你吗?

import numpy as np
import pandas as pd
from scipy.optimize import newton
from scipy.stats import f_oneway as anova

data = pd.DataFrame({'cat1':'A B C A A A B C A A B B C C C A A B B'.split(' '),
               'cat2':'1 1 2 1 2 3 3 2 1 2 2 1 1 1 3 3 3 2 1'.split(' '),
               'cont1':'8 0 85 82 90 93 13 33 0 58 68 43 35 75 68 93 17 43 89'.split(' '),
               'cont2':'12 13 14 15 16 87 86 85 84 67 65 66 63 26 2 0 0 9 34'.split(' ')})

data['cont1'] = data['cont1'].astype(float)
data['cont2'] = data['cont2'].astype(float)

d = dict(zip(data.columns[2:],[list]*len(data.columns[2:])))

data1 = data.groupby(['cat1','cat2']) [data.columns[2:]].agg(d).reset_index()

col_list = data1.columns[2:].tolist()

for i in col_list:
    for j in col_list[col_list.index(i)+1:]:
        if i != j:
            data1['anova_'+i+'_'+j] = data1.apply(lambda x: anova(x[i],x[j]).pvalue, axis=1)

现在,它应该考虑到你所拥有的尽可能多的连续变量,为每个2个变量的组合创建一个p_value列。不要介意data['cont1'].astype(float)的事情,is只是让变量浮动。如果你的df已经包含十进制值,你就不需要它了。

相关问题