【问题标题】:Pandas vectorisation between two dataframes两个数据帧之间的 Pandas 矢量化
【发布时间】:2018-09-13 17:10:49
【问题描述】:
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 年...我已经对其进行了改进,但需要删除循环以真正改进它。

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

为了更清楚,预期的输出会是这样的(抱歉没有计算 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

【问题讨论】:

  • 你期望的输出是什么?
  • @ason​​gtoruin 我已将预期输出添加到问题中。请注意,它是一个简化版本,S1 和 G1 实际上是多个描述符列。

标签: python pandas vectorization


【解决方案1】:

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

首先,此解决方案假定df1df2 中的IDs 列是同构的(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(),这使向量化变得复杂。

【讨论】:

  • G1 != S1。对于长表格式,您需要类似 G-IDs S-ID value_y value_x 的标题。所以第 1 行是G1 S1 1 [ float, float] 。第二行是G1 S1 2 [float,float],第三行是G1 S2 0 [float] 这有意义吗?我已经用预期的输出更新了我的问题以帮助澄清它。你认为编写我自己的能够矢量化的 levene 函数值得吗?我计算出我要运行 4.7x10^11 的堤坝测试......
猜你喜欢
  • 2019-01-30
  • 2021-10-11
  • 2017-11-07
  • 2021-02-22
  • 1970-01-01
  • 1970-01-01
  • 2021-09-12
  • 1970-01-01
  • 2018-07-16
相关资源
最近更新 更多