我正在编写一个 Pandas 例程库,它需要能够处理可能具有不同类型的数据框中的日期。具体来说,我得到了很多 datetime.date
和 pandas._libs.tslib.Timestamp
类型的不同组合。据报告(并通过我的测试确认)这与具有多索引设置然后重置的帧有关。 (请参阅我之前的问题 What changes type of date in this pandas code?,它解决了从多索引来回时类型被更改的问题。)
这是一个简短的(但人为设计的)示例:
import pandas as pd
df = pd.DataFrame(
data={
'date' : ['2019-01-01', '2019-01-02', '2019-01-03'],
'value' : [1, 2, 3],
'other' : [11, 12, 13]
}
)
df.date = pd.to_datetime(df.date).dt.date
print df.head()
date other value
0 2019-01-01 11 1
1 2019-01-02 12 2
2 2019-01-03 13 3
df_reindex = df.set_index(['date','other']).reset_index()
date other value
0 2019-01-01 11 1
1 2019-01-02 12 2
2 2019-01-03 13 3
print pd.merge(df, df_reindex, on='date')
Empty DataFrame
Columns: [date, other_x, value_x, other_y, value_y]
Index: []
print pd.merge(df, df, on='date')
date other_x value_x other_y value_y
0 2019-01-01 11 1 11 1
1 2019-01-02 12 2 12 2
2 2019-01-03 13 3 13 3
print pd.merge(df_reindex, df_reindex, on='date')
date other_x value_x other_y value_y
0 2019-01-01 11 1 11 1
1 2019-01-02 12 2 12 2
2 2019-01-03 13 3 13 3
print type(df.date[0])
<type 'datetime.date'>
print type(df_reindex.date[0])
<class 'pandas._libs.tslib.Timestamp'>
此处 df
和 df_reindex
具有基本相同的数据内容,但是,由于在 set_index 点 Pandas 内部的类型发生了变化
,它们之间的合并是空的,而两者中的任何一个与自身之间的“自合并”给出了预期的(尽管,在这里的人为情况下,冗余和微不足道的)结果。
当然,真实案例不会像这样反复无常地设置和重置索引就达到了这一点——真实数据通过多位代码在不同阶段执行具有不同索引要求的不同操作,并且合并是在帧之间进行的有一些不重叠的列。
关于我的另一个关于使用 NumPy 日期时间的问题有评论,但这似乎是徒劳的,因为转换显然会导致两种有问题的数据类型之一,即底层 Pandas 类:
df_numpy = df.copy()
df_numpy.date = df.date.apply(np.datetime64)
print type(df.date[0])
<type 'datetime.date'>
print type(df_numpy.date[0])
<class 'pandas._libs.tslib.Timestamp'>
(更不用说我在现有框架内工作,因此目前可能无法强制所有框架都使用 NumPy 类型而不是 Pandas 类型。)
我需要做的是能够在库代码中合并表,无论它们是否被我无法控制的调用者如此操纵。我作为输入获得的数据框无法更改。我可以复制它们并在副本上实现直接转换(如此处 pandas merge on date column issue ),但框架有时很大,除非没有其他选择,否则我不想复制它们。
有没有办法让合并将它们识别为等效的?如果没有,是否有日期格式的最佳实践选择可以避免此处暴露的转换问题?
最佳答案
Is there a way to get the merge to recognize these as equivalent?
不,不是当前的pandas code :
# datetimelikes must match exactly
elif is_datetimelike(lk) and not is_datetimelike(rk):
raise ValueError(msg)
elif not is_datetimelike(lk) and is_datetimelike(rk):
raise ValueError(msg)
elif is_datetime64tz_dtype(lk) and not is_datetime64tz_dtype(rk):
raise ValueError(msg)
elif not is_datetime64tz_dtype(lk) and is_datetime64tz_dtype(rk):
raise ValueError(msg)
If not, is there a best-practice choice of date format that avoids the conversion issue exposed here?
我从您的问题中了解到,数据框及其数据类型不在您的控制范围内,并且无法更改,因此这个问题不会给我们带来任何帮助。
您需要的是像 SQL 中那样的条件连接。有个老开issue此功能自 2014 年以来没有任何事件。(邀请 PR...)
可能的解决方法是这样的:
def merge_on_date(left, right, on):
from pandas.core.dtypes.common import is_datetimelike
try:
return pd.merge(left, right, on=on)
except:
if is_datetimelike(right[on]):
return pd.merge(left, right.assign(**{on: eval('right[on].dt.date')}), on=on)
else:
return pd.merge(left.assign(**{on: eval('left[on].dt.date')}), right, on=on)
结果:
>>> merge_on_date(df, df_reindex, 'date')
date value_x other_x other_y value_y
0 2019-01-01 1 11 11 1
1 2019-01-02 2 12 12 2
2 2019-01-03 3 13 13 3
>>> merge_on_date(df_reindex, df, 'date')
date other_x value_x value_y other_y
0 2019-01-01 11 1 1 11
1 2019-01-02 12 2 2 12
2 2019-01-03 13 3 3 13
然而,这个大缺点是 assign
makes a copy在幕后。
PS:我刚刚看到 pd.merge(df, df_reindex, on='date')
在您的示例中生成了一个空数据框。从版本 0.22.0 开始,这应该引发一个 ValueError
。您使用的是什么版本?
关于python - 如何在支持多种数据格式的 Pandas 中合并日期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58276892/