【问题标题】:Numpy vectorization messes up data typeNumpy矢量化弄乱了数据类型
【发布时间】:2020-01-03 11:03:36
【问题描述】:

使用pandas 数据框时,通常会使用A 列中的信息创建B 列。

背景

在某些情况下,可以一次性完成 (df['B'] = df['A'] + 4),但在其他情况下,操作更复杂,需要编写单独的函数。在这种情况下,可以通过以下两种方式之一应用此功能(据我所知):

def calc_b(a): 
    return a + 4

df = pd.DataFrame({'A': np.random.randint(0, 50, 5)})
df['B1'] = df['A'].apply(lambda x: calc_b(x))
df['B2'] = np.vectorize(calc_b)(df['A'])

生成的数据框:

    A  B1  B2
0  17  21  21
1  25  29  29
2   6  10  10
3  21  25  25
4  14  18  18

完美 - 两种方式都有正确的结果。在我的代码中,我一直使用np.vectorize 方式,因为.apply 很慢而considered bad practise

现在我的问题来了

在使用日期时间/时间戳时,此方法似乎失效了。一个最小的工作示例是这样的:

def is_past_midmonth(dt):
    return (dt.day > 15)

df = pd.DataFrame({'date':pd.date_range('2020-01-01', freq='6D', periods=7)})
df['past_midmonth1'] = df['date'].apply(lambda x: is_past_midmonth(x))
df['past_midmonth2'] = np.vectorize(is_past_midmonth)(df['date'])

.apply 方式有效;结果数据框是

        date  past_midmonth1
0 2020-01-01           False
1 2020-01-07           False
2 2020-01-13           False
3 2020-01-19            True
4 2020-01-25            True
5 2020-01-31            True
6 2020-02-06           False

np.vectorize 方式失败并出现AttributeError: 'numpy.datetime64' object has no attribute 'day'

type()挖一点,df['date']的元素属于<class 'pandas._libs.tslibs.timestamps.Timestamp'>,这也是函数接收它们的方式。但是,在矢量化函数中,它们作为<class 'numpy.datetime64'> 的实例被接收,这会导致错误。

我有两个问题:

  • 有没有办法“修复”np.vectorize 的这种行为?怎么样?
  • 我一般如何避免这些不兼容?

当然,我可以记下不要使用带有日期时间参数的np.vectorize 函数,但这很麻烦。我想要一个始终有效的解决方案,这样我就不必在遇到这种情况时考虑它。

如上所述,这是一个演示问题的最小工作示例。我知道在这种情况下我可以使用更简单的一次性操作,就像在第一个示例中使用 int 列一样。但这无关紧要。我对矢量化任何采用时间戳参数的函数的一般情况感兴趣。对于那些询问更具体/复杂示例的人,我创建了一个here

编辑:我想知道使用类型提示是否会有所作为——如果numpy 真的会考虑这些信息——但我对此表示怀疑,因为使用此签名def is_past_midmonth(dt: float) -> bool:,其中float 显然是错误的,给出同样的错误。不过,我对类型提示还很陌生,而且我没有支持它的 IDE,所以调试起来有点困难。

非常感谢!

【问题讨论】:

  • at this post 似乎“.apply”解决方案可能是目前的方法。 vectorize 函数将 Timedelta 对象转换为 numpy.datetime64 对象,所以,imo,几乎没有人可以做。
  • 谢谢@Andrea。我实际上看过那个帖子,但我想……已经 7 年了! :)
  • 我同意,但显然我们仍然处于相同的情况;)不过,@rpanai 的 int 解决方法似乎很有趣
  • "我对矢量化任何采用时间戳参数的函数的一般情况感兴趣。"没有任何解决方案是微不足道的,一些操作基本上是迭代的,并且可能需要某种形式的(美化的)循环。对于矢量化实现,我们可能需要更多细节
  • np.vectorize without otypes 从试算中猜测返回 dtype。它也只适用于numpy dtypes。即使在 numpy 中使用也可能很棘手。

标签: python pandas numpy


【解决方案1】:

您是否考虑过以int 而不是datetime64[ns] 来度过这一天?

import pandas as pd
import numpy as np

# I'd avoid use dt as it's used as alias for datetime
def is_past_midmonth1(d): 
    return (d.day > 15)

def is_past_midmonth2(day):
    return (day > 15)

N = int(1e4)
df = pd.DataFrame({'date':pd.date_range('2020-01-01', freq='6D',
                                        periods=N)})

应用(使用日期时间)

%%time
df['past_midmonth1'] = df['date'].apply(lambda x: is_past_midmonth1(x))

CPU times: user 55.4 ms, sys: 0 ns, total: 55.4 ms
Wall time: 53.8 ms

应用(使用 int)

%%time
df['past_midmonth2'] = (df['date'].dt.day).apply(lambda x: is_past_midmonth2(x))

CPU times: user 4.71 ms, sys: 0 ns, total: 4.71 ms
Wall time: 4.16 ms

np.vectorize

%%time
df['past_midmonth2_vec'] = np.vectorize(is_past_midmonth2)(df['date'].dt.day)

CPU times: user 4.2 ms, sys: 75 µs, total: 4.27 ms
Wall time: 3.49 ms

矢量化您的代码

%%time
df['past_midmonth3'] = df["date"].dt.day>15

CPU times: user 3.1 ms, sys: 11 µs, total: 3.11 ms
Wall time: 2.41 ms

时间

【讨论】:

  • 感谢@rpanai 的回答。然而,我提出的问题是一个最小的工作示例;我会更新我的帖子以进一步指出这一点。我的实际函数要复杂得多,并且需要将时间戳传递给它,因为对其进行了更多检查(不仅使用了day 属性)。我认为这使得提出的 3 个解决方案变得不可能。
【解决方案2】:

我会写这个作为答案,虽然我觉得这几乎不是一个解决方法;因此,如果您有更好的答案,请添加您的答案。 :)

通过将传入的dt 参数强制转换为带有dt = pd.to_datetime(dt)pandas 日期时间对象,它可以工作。

def is_past_midmonth(dt):
    dt = pd.to_datetime(dt) #the only addition
    return (dt.day > 15)

df = pd.DataFrame({'date':pd.date_range('2020-01-01', freq='6D', periods=7)})
df['past_midmonth1'] = df['date'].apply(lambda x: is_past_midmonth(x))
df['past_midmonth2'] = np.vectorize(is_past_midmonth)(df['date']) #this now works
In[45]: df
Out[45]: 
        date  past_midmonth1  past_midmonth2
0 2020-01-01           False           False
1 2020-01-07           False           False
2 2020-01-13           False           False
3 2020-01-19            True            True
4 2020-01-25            True            True
5 2020-01-31            True            True
6 2020-02-06           False           False

对于那些感兴趣的人 - 执行时间大约减半(对于更长的数据帧)。

【讨论】:

    猜你喜欢
    • 2020-04-22
    • 2010-12-24
    • 2020-04-06
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-17
    相关资源
    最近更新 更多