【问题标题】:Efficiently finding the low median over multiple columns有效地找到多列的低中位数
【发布时间】:2018-03-28 05:10:43
【问题描述】:

我对 Python 很陌生,所以我认为这可能是一个基本问题。我在网上找到了一些解决方案,但找不到我正在寻找的确切内容。目前我正在寻找一种方法来找到超过 3 列数据的“低中位数”。如果仅填充 3 列中的 2 个值,那么我想取较低的值。

这是我目前发现的

df['median']=np.nanmedian(df[['val1','val2','val3']], axis=1)

以上不是一个可行的解决方案,因为当存在偶数个值时,我没有看到任何关于取低中位数的参数的信息。此外,我发现有一个功能可以满足我的需求

statistics.median_low()

但是,我不确定如何在不使用某种函数(即循环或应用函数)的情况下将其应用于多列而不使用某种函数来逐行计算每个中值。理想情况下,我想要一个使用这个函数的矢量化解决方案,它可以同时计算中位数。谢谢您的帮助。

【问题讨论】:

  • 如果它的三列(奇数),你为什么需要担心低?
  • 有时会有NULL值
  • NULL 是指 NaN,对吧?
  • 是的,对不起,我的意思是 NaN

标签: python pandas numpy statistics vectorization


【解决方案1】:

利用对每一行进行排序然后简单地根据NaNs 选择第一列或第二列来对三列数据进行很少的优化,由于已排序,该列将被推到每一行的末尾。这让我们之后可以使用slicing 进行选择并为每一行获取所需的median_low 值。

这里将它们组装成一个矢量化解决方案 -

a = df.values
a_sorted = np.sort(a,1)
df['median'] = np.where(np.isnan(a_sorted[:,2]), a_sorted[:,0], a_sorted[:,1])

运行时测试

方法-

# Proposed in this post
def vectorized_app(df):
    a = df.values
    a_sorted = np.sort(a,1)
    df['median'] = np.where(np.isnan(a_sorted[:,2]), a_sorted[:,0], a_sorted[:,1])
    return df

# @piRSquared's new soln
def vectorized_app2(df):
    v = np.sort(df.values, axis=1)
    n = np.count_nonzero(~np.isnan(v), axis=1)
    j = (n - 1) // 2
    i = np.arange(len(v))
    return df.assign(median_low=v[i, j])

# @piRSquared's old soln
from statistics import median_low
def apply_app(df):
    med = lambda x: median_low(x.dropna())
    return df.apply(med, 1)

时间安排 -

In [433]: # Setup input dataframe and set one per row as NaN
     ...: np.random.seed(0)
     ...: a = np.random.randint(0,9,(10000,3)).astype(float)
     ...: idx = np.random.randint(0,3,a.shape[0])
     ...: a[np.arange(a.shape[0]), idx] = np.nan
     ...: df = pd.DataFrame(a)
     ...: df.columns = [['val1','val2','val3']]
     ...: 

In [435]: %timeit vectorized_app(df)
1000 loops, best of 3: 481 µs per loop

In [436]: %timeit vectorized_app2(df)
1000 loops, best of 3: 892 µs per loop

In [434]: %timeit apply_app(df)
1 loop, best of 3: 1.15 s per loop

【讨论】:

  • 谢谢!将在今天晚些时候进行测试并报告结果。
  • 我发布了一个新的解决方案。
  • @piRSquared 更新时间。
【解决方案2】:

回答
这是适用于任何大小数组的通用解决方案。

我对每一行进行排序,计算有多少非空值,然后确定median_low 必须在哪里。

v = np.sort(df.values, axis=1)
n = np.count_nonzero(~np.isnan(v), axis=1)
j = (n - 1) // 2
i = np.arange(len(v))

df.assign(median_low=v[i, j])

   A    B    C  median_low
0  4  5.0  8.0         5.0
1  3  6.0  4.0         4.0
2  4  9.0  NaN         4.0
3  1  NaN  NaN         1.0

旧答案

首先,您需要使用带有axis=1 选项的pd.DataFrame.apply 将函数应用于每一行。

其次,median_low 将考虑空值。你不想这样,所以制作一个 lambda 来删除空值,然后使用 median_low


import pandas as pd
from statistics import median_low

df = pd.DataFrame([
    [4, 5, 8],
    [3, 6, 4],
    [4, 9],
    [1]
], columns=list('ABC'))

med = lambda x: median_low(x.dropna())

df.apply(med, 1)

0    5.0
1    4.0
2    4.0
3    1.0
dtype: float64

【讨论】:

  • 非常感谢您的帮助,它非常清楚且有意义。唯一的事情是我需要一个矢量化的解决方案。我的理解是,在对行使用应用时,它的功能类似于循环,一次计算一个值。
  • 当我回答这些问题时。我通常会发布我想到的第一件事。然后我探索看看我是否能想出一个更好的答案。到目前为止,你有一些有用的东西。我们会看看我还能想出什么,或者其他人能想出什么。
猜你喜欢
  • 2021-08-08
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多