将矩阵与列向量进行比较

Compare a matrix against a column vector

下面的数组 '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 < B 比较的 True/False 值以获得以下结果:

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

我可以做列表理解语法,但有没有更好的方法来实现它。我的数组 A 和 B 非常大。

更有效的方法是降低 numpy 级别(这里的 A、B 是数据帧):

A.values<B.values

您可以使用 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,它将尝试对齐我们不需要的列标签。我们想沿列轴广播比较结果

考虑 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 是二维数组。为了比较 B 沿 A 的行,我们需要将 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 的解决方案速度更快,但不是那么快。它们的比例都相同。

另一个使用 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 可以在比较两个不同形状的数组时进行广播。