【问题标题】:Compare a matrix against a column vector将矩阵与列向量进行比较
【发布时间】:2017-08-05 01:59:05
【问题描述】:

下面的数组“A”和向量“B”是 pandas 数据框的一部分。

我有一个大数组A 的形式:

28  39  52
77  80  66
7   18  24
9   97  68

我有一个向量B 的形式:

32
5
42
17

如何以 Python 方式比较 A 和 B 的每一列。我试图获取 A

TRUE    FALSE   FALSE
FALSE   FALSE   FALSE
TRUE    TRUE    TRUE
TRUE    FALSE   FALSE

我可以使用列表理解语法,但有没有更好的方法来解决这个问题。我的数组 A 和 B 非常大。

【问题讨论】:

    标签: python pandas numpy multidimensional-array numpy-ndarray


    【解决方案1】:

    考虑pd.DataFramepd.SeriesAB

    A = pd.DataFrame([
            [28, 39, 52],
            [77, 80, 66],
            [7, 18, 24],
            [9, 97, 68]
        ])
    
    B = pd.Series([32, 5, 42, 17])
    

    pandas

    默认情况下,当您比较 pd.DataFramepd.Series 时,pandas 会将序列中的每个索引值与数据框的列名对齐。这就是您使用A < B 时发生的情况。在这种情况下,您的数据框中有 4 行,系列中有 4 个元素,因此我将假设您希望将系列的索引值与数据框的索引值对齐。为了指定要对齐的轴,您需要使用比较方法而不是运算符。那是因为在使用方法时,可以使用axis参数,并指定你想要axis=0而不是默认的axis=1

    A.lt(B, axis=0)
    
           0      1      2
    0   True  False  False
    1  False  False  False
    2   True   True   True
    3   True  False  False
    

    我经常把这个写成A.lt(B, 0)


    numpy

    在 numpy 中,您还必须注意数组的维度,您假设位置已经排好。如果位置来自同一个数据框,则会得到处理。

    print(A.values)
    
    [[28 39 52]
     [77 80 66]
     [ 7 18 24]
     [ 9 97 68]]
    
    print(B.values)
    
    [32  5 42 17]
    

    请注意,B 是一维数组,而 A 是二维数组。为了沿着A 的行比较B,我们需要将B 重塑为一个二维数组。最明显的方法是使用reshape

    print(A.values < B.values.reshape(4, 1))
    
    [[ True False False]
     [False False False]
     [ True  True  True]
     [ True False False]]
    

    但是,这些是您通常会看到其他人进行相同重塑的方式

    A.values < B.values.reshape(-1, 1)
    

    或者

    A.values < B.values[:, None]
    

    定时回测

    为了掌握这些比较的速度,我构建了以下回溯测试。

    def pd_cmp(df, s):
        return df.lt(s, 0)
    
    def np_cmp_a2a(df, s):
        """To get an apples to apples comparison
        I return the same thing in both functions"""
        return pd.DataFrame(
            df.values < s.values[:, None],
            df.index, df.columns
        )
    
    def np_cmp_a2o(df, s):
        """To get an apples to oranges comparison
        I return a numpy array"""
        return df.values < s.values[:, None]
    
    
    results = pd.DataFrame(
        index=pd.Index([10, 1000, 100000], name='group size'),
        columns=pd.Index(['pd_cmp', 'np_cmp_a2a', 'np_cmp_a2o'], name='method'),
    )
    
    from timeit import timeit
    
    for i in results.index:
        df = pd.concat([A] * i, ignore_index=True)
        s = pd.concat([B] * i, ignore_index=True)
        for j in results.columns:
            results.set_value(
                i, j,
                timeit(
                    '{}(df, s)'.format(j),
                    'from __main__ import {}, df, s'.format(j),
                    number=100
                )
            )
    
    results.plot()
    

    我可以得出结论,基于numpy 的解决方案速度更快,但不是那么快。它们的比例都相同。

    【讨论】:

    • 由于 OP 表示数组非常大,因此对于未来的读者来说,黑白 Pandas 与 Numpy 的性能比较会提供很多信息
    • 我真的很喜欢你的熊猫“timeit”包装器。
    【解决方案2】:

    您可以使用lt 并在B 上调用squeeze 来执行此操作,以便将df 展平为一维系列:

    In [107]:
    A.lt(B.squeeze(),axis=0)
    
    Out[107]:
           0      1      2
    0   True  False  False
    1  False  False  False
    2   True   True   True
    3   True  False  False
    

    问题是如果没有squeeze,它会尝试对齐我们不想要的列标签。我们想沿列轴广播比较

    【讨论】:

      【解决方案3】:

      更高效的是向下numpy级别(A,B在这里是DataFrames):

      A.values<B.values
      

      【讨论】:

        【解决方案4】:

        另一个使用 numpy 的选项是 numpy.newaxis

        In [99]: B = B[:, np.newaxis]
        
        In [100]: B
        Out[100]: 
        array([[32],
               [ 5],
               [42],
               [17]])
        
        In [101]: A < B
        Out[101]: 
        array([[ True, False, False],
               [False, False, False],
               [ True,  True,  True],
               [ True, False, False]], dtype=bool)
        

        本质上,我们将向量 B 转换为二维数组,以便 numpy 可以在比较两个不同形状的数组时进行广播。

        【讨论】:

        • 这很有用 不知道 np.newaxis +1
        • @EdChum np.newaxisNone 的同义词。 np.newaxis is None 计算结果为 True
        • @piRSquared 啊..我不知道,我一直在这些情况下使用None
        • @EdChum 和大多数人一样,因为它少写了 6 个字符 :-)
        • @EdChum 我更喜欢这种方式,因为它更直观(意味着每次使用 newaxis 时我们将数组轴增加 1)并且可读性:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-24
        • 1970-01-01
        • 2019-05-23
        • 2017-01-28
        • 1970-01-01
        • 2010-10-28
        • 2013-05-04
        相关资源
        最近更新 更多