【问题标题】:Min value in each column of a data frame excluding zeros数据框每列中的最小值,不包括零
【发布时间】:2018-07-24 20:28:15
【问题描述】:

原始数据框是这样的表格:

                        S1_r1_ctrl/     S1_r2_ctrl/     S1_r3_ctrl/
sp|P38646|GRP75_HUMAN   2.960000e-06    5.680000e-06    0.000000e+00
sp|O75694-2|NU155_HUMAN 2.710000e-07    0.000000e+00    2.180000e-07
sp|Q05397-2|FAK1_HUMAN  0.000000e+00    2.380000e-07    7.330000e-06
sp|O60671-2|RAD1_HUMAN  NaN             NaN             NaN

我正在寻找大于零的数据框每一列中的最小值。我试图用这个example 来回答我的问题。我的代码如下:

df.ne(0).idxmin().to_frame('pos').assign(value=lambda d: df.lookup(d.pos, d.index))

但我仍然只得到零,我的结果如下所示:

            pos                     value

S1_r1_ctrl/ sp|Q05397-2|FAK1_HUMAN  0.0
S1_r2_ctrl/ sp|O75694-2|NU155_HUMAN 0.0
S1_r3_ctrl/ sp|P38646|GRP75_HUMAN   0.0

而不是这个:

            pos                     value
S1_r1_ctrl/ sp|O75694-2|NU155_HUMAN 2.710000e-07
S1_r2_ctrl/ sp|Q05397-2|FAK1_HUMAN  2.380000e-07
S1_r3_ctrl/ sp|O75694-2|NU155_HUMAN 2.180000e-07

我猜数据类型可能有问题,但我不确定。我假设ne(0) 会忽略零,但事实并非如此,所以我很困惑为什么。也许还有一种更智能的方法可以找到我需要的东西。

【问题讨论】:

标签: python pandas dataframe types


【解决方案1】:

设置

df = pd.DataFrame([[0, 0, 0],
                   [0, 10, 0],
                   [4, 0, 0],
                   [1, 2, 3]],
                  columns=['first', 'second', 'third'])

使用带有 min(0) 的掩码:

df[df.gt(0)].min(0)

first     1.0
second    2.0
third     3.0
dtype: float64

正如@DSM 指出的,也可以这样写:

df.where(df.gt(0)).min(0)

性能

def chris():
    df1[df1.gt(0)].min(0)

def chris2():
    df1.where(df1.gt(0)).min(0)

def wen():
    a=df1.values.T
    a = np.ma.masked_equal(a, 0.0, copy=False)
    a.min(1)

def haleemur():
    df1.replace(0, np.nan).min()

设置

from timeit import timeit
import matplotlib.pyplot as plt

res = pd.DataFrame(
       index=['chris', 'chris2', 'wen', 'haleemur'],
       columns=[10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000],
       dtype=float
)

for f in res.index: 
    for c in res.columns:
        df1 = df.copy()
        df1 = pd.concat([df1]*c)
        stmt = '{}()'.format(f)
        setp = 'from __main__ import df1, {}'.format(f)
        res.at[f, c] = timeit(stmt, setp, number=50)

ax = res.div(res.min()).T.plot(loglog=True) 
ax.set_xlabel("N"); 
ax.set_ylabel("time (relative)");

plt.show()

结果

【讨论】:

  • 我会改用df.where,但这是正确的方法。
  • 感谢您的提示! where 这里有什么好处?
  • 这比我的方法好得多。 +1
  • 真的只是在语义上,因为我认为它在表达预期掩码方面做得更好。
  • 只代表“大于”。 df.gt(0)df > 0 相同
【解决方案2】:

也许numpy 是不错的选择

a=df.values.T
a = np.ma.masked_equal(a, 0.0, copy=False)
a.min(1)
Out[755]: 
masked_array(data=[1, 2, 3],
             mask=[False, False, False],
       fill_value=999999,
            dtype=int64)

【讨论】:

    【解决方案3】:

    您需要遍历所有列并找到不带 0 的系列的最小值。

    df = pd.DataFrame([[0, 0, 0],
                       [0, 10, 0],
                       [4, 0, 0],
                       [1, 2, 3]],
                      columns=['first', 'second', 'third'])
    
    [df[col][df[col].ne(0)].min() for col in df.columns]
    

    输出:

    [1, 2, 3]
    

    【讨论】:

    • 这个输出是什么?
    • @RafaelC,OP 有一个指向另一个 SO 问题的链接,输入最少。我已将其添加到答案中以避免混淆。
    【解决方案4】:

    另一种选择是将0 替换为np.nan,然后应用min 方法。

    注意:这并没有解决> 0 条件,但测试帧似乎只是非负值。

    使用与其他人相同的设置:

    df = pd.DataFrame([[0, 0, 0],
                       [0, 10, 0],
                       [4, 0, 0],
                       [1, 2, 3]],
                      columns=['first', 'second', 'third'])
    
    df.replace(0, np.nan).min()
    
    first     1.0
    second    2.0
    third     3.0
    dtype: float64
    

    发布这个替代方案,因为我发现它比user3483203excellent answer 稍快,这也是我对这个问题的第一直觉


    %timeit df.replace(0, np.nan).min()
    745 µs ± 2.72 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit df[df > 0].min()
    1.09 ms ± 14.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    还要注意:

    %timeit df[df != 0].min()
    1.1 ms ± 16.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    因此,如果在计算聚合时需要忽略一个特定值,而不是一个范围,replacenp.nan 似乎是一种高效的方式

    【讨论】:

    • 现在无法证明,但不确定这对于大 df 是否仍然更快。会猜df.gt(0) 变得更快
    • 是的,完全有可能,我会运行基准测试并很快发布图表,因为性能概况可能对一般人有用
    • 能否请您添加我的方法的时间,只是好奇..也谢谢您,为您的额外完美工作投票
    • @Wen,绝对会为您的解决方案添加时间:) 立即创建测试设置。
    • @Wen 添加到我的答案中,您的每一个数据框大小都会获胜。
    【解决方案5】:

    尝试每一列:

        df.value.min(skipna=True)
    

    【讨论】:

    • skipna=True 是默认值,这不解决 > 0 条件,因为零和负值不为空。
    猜你喜欢
    • 2014-07-09
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    • 2021-01-13
    • 2012-01-21
    • 1970-01-01
    • 2015-10-01
    • 2021-04-01
    相关资源
    最近更新 更多