两个数据帧之间的熊猫矢量化

时间:2018-09-13 06:33:28

标签: python pandas vectorization

import numpy as np
import pandas as pd
from scipy.stats import levene as lev 

raw_data = {'IDs': ['G1', 'G2', 'G3', 'G4', 'G5'], 
            'Sample1': [102.2, 2310.4, 123.4, 213.0, 1234.2], 
            'Sample2': [112.8, 1910.4, 36.3, 188.2, 1271.2], 
            'Sample3': [32.2, 1290.3, 121.4, 212.3, 1333.5], 
            'Sample4': [52.1, 2210.1, 155.2, 244.7, 1987.1]} 

raw_data2 = {'IDs': ['S1', 'S2', 'S3', 'S4', 'S5'], 
        'Sample1': [1, 2, 1, 0, 2], 
        'Sample2': [2, 1, 2, 1, 2], 
        'Sample3': [2, 0, 1, 0, 1], 
        'Sample4': [1, 2, 1, 2, 1]} 
df1 = pd.DataFrame(raw_data, columns = ['IDs', 'Sample1', 'Sample2', 'Sample3', 'Sample4'])
df2 = pd.DataFrame(raw_data2, columns = ['IDs', 'Sample1', 'Sample2', 'Sample3', 'Sample4'])

我一直在尝试找到一种方法来对df1的每一行执行一次levenes测试,每行df2定义要拆分的组。例如,每当进行一次levenes测试时,df1的第一行将按df2的每一行进行分组。显然,我可以使用诸如以下的嵌套循环来实现它(必须包含if语句,因为并非所有行都将包含所有组):

   for i in range(0, df1.shape[0]):
    for j in range(0, df2.shape[0]):
        tmp1=df1.ix[i,:]
        tmp2=df2.ix[i,:]
        group1 = tmp1[tmp2==0]
        group2 = tmp1[tmp2==1]
        group3 = tmp1[tmp2==2]
        if len(group1) <= 1:
            lev(group2,group3) # and some how return the output to a new df
        elif len(group2) <= 1:
            lev(group1,group3) # and some how return the output to a new df
        elif len(group3) <=1:
            lev(group1,group2) # and some how return the output to a new df
        else:
            lev(group1,group2,group3) # and some how return the output to a new df

样本在数据帧中的顺序相同,但是一个df有一些额外的描述符列(为输出保留重要)。

由于我将要进行数百万次比较,因此使用循环来进行比较是不切实际的,我的第一次尝试将花费120年的时间...我已经对其进行了改进,但是需要删除循环以真正改善它。< / p>

我一直在阅读有关尝试使用向量化的信息,我在R中使用apply函数对它有些熟悉。我想熊猫,麻木等会有一种优雅的方式,但是还没有破解。

为了更清楚地说明预期的输出结果是类似的(抱歉,尚未计算出lev测试,因此没有实际数字-到达计算机时会更新):

DF1-ID DF2-ID Lev.stat Lev.pvalue
G1 S1 float float
G1 S2 float float
G1 S3 float float
G1 S4 float float
G2 S1 float float
.
.
.
G4 S4 float float

1 个答案:

答案 0 :(得分:0)

此解决方案不是特别优雅,并且有点脆弱,这意味着它在某些情况下会失败。但是,如果您的数据没有任何异常情况,它将起作用。

首先,此解决方案假定IDsdf1中的df2列是同构的(G1 = S1等)。其次,该解决方案假定仅对2或3组数据进行Levene的测试。 (对于您的示例数据而言,这是正确的。)第三,您的示例数据没有很多数据,有时这会引发Levene测试-在这些情况下,Scipy会抱怨(尽管它只返回Inf完成执行)。

简而言之,过程是:

  1. 使用melt()从宽到长格式
  2. 合并df1df2
  3. 使用groupby列出分组值
  4. 在组上运行lev()

第一:

# merge data
df2.IDs = df2.IDs.str.replace("S", "G")
merged = df1.melt(id_vars="IDs").merge(df2.melt(id_vars="IDs"), on=["IDs", "variable"])

# group values into lists
lev_data = merged.groupby(["IDs", "value_y"]).value_x.agg(list).reset_index()

lev_data
   IDs  value_y                value_x
0   G1        1          [102.2, 52.1]
1   G1        2          [112.8, 32.2]
2   G2        0               [1290.3]
3   G2        1               [1910.4]
4   G2        2       [2310.4, 2210.1]
5   G3        1  [123.4, 121.4, 155.2]
6   G3        2                 [36.3]
7   G4        0         [213.0, 212.3]
8   G4        1                [188.2]
9   G4        2                [244.7]
10  G5        1       [1333.5, 1987.1]
11  G5        2       [1234.2, 1271.2]

下一步:

# run lev() on two- or three-group sets
def do_lev(vals):
    if len(vals) == 2:
        return lev(vals.iloc[0,2], vals.iloc[1,2])
    return lev(vals.iloc[0,2], vals.iloc[1,2], vals.iloc[2,2])

lev_data.groupby("IDs").apply(do_lev)

输出:

IDs
G1    (1.84254995447e+31, 5.42726126677e-32) # (test statistic, p-value)
G2                                (inf, 0.0)
G3          (0.300123996469, 0.638777548242)
G4                                (inf, 0.0)
G5                                (inf, 0.0)
dtype: object

注意:由于scipy.stats.levene() requires每个样本要单独输入,而不是接受向量列表,因此在某种程度上阻止了完全向量化。这意味着需要对样本进行分解并逐个输入lev(),这会使向量化变得复杂。