9

下面的数组“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   FALSE
FALSE   FALSE   FALSE
TRUE    TRUE    TRUE
TRUE    FALSE   FALSE

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

4

4 回答 4

21

考虑pd.DataFrameand pd.Series, AandB

A = pd.DataFrame([
        [28, 39, 52],
        [77, 80, 66],
        [7, 18, 24],
        [9, 97, 68]
    ])

B = pd.Series([32, 5, 42, 17])

pandas

默认情况下,当您将 apd.DataFrame与 a进行比较时pd.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基于解决方案的速度更快,但并不是那么快。它们的比例都相同。

于 2017-03-14T16:19:19.920 回答
3

更有效的是降低 numpy 级别(A,B 在这里是 DataFrames):

A.values<B.values
于 2017-03-14T16:12:53.657 回答
3

您可以使用lt和调用squeeze来执行此操作,B以便将 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它,它将尝试在我们不想要的列标签上对齐。我们想沿列轴广播比较

于 2017-03-14T16:14:02.220 回答
2

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

于 2017-03-14T17:04:08.910 回答