【发布时间】: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.vectorizewithoutotypes从试算中猜测返回dtype。它也只适用于numpydtypes。即使在 numpy 中使用也可能很棘手。