如何在支持多种数据格式的 Pandas 中合并日期?

Posted

技术标签:

【中文标题】如何在支持多种数据格式的 Pandas 中合并日期?【英文标题】:How to merge on date in Pandas supporting multiple data formats? 【发布时间】:2020-02-05 04:22:37 【问题描述】:

我正在编写一个 Pandas 例程库,该库需要能够处理可能属于不同类型的数据框中的日期。具体来说,我得到了 datetime.datepandas._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'>

这里dfdf_reindex 具有基本相同的数据内容,但是,由于在Pandas 内部set_index 处的类型已更改,它们之间的合并为空,而“self -merge” 两者中的任何一个与自身之间给出了预期的(尽管在这里人为的情况下是多余的和微不足道的)结果。

实际情况当然是在没有像这样反复设置和重置索引的情况下达到这一点 - 真实数据通过多位代码在不同阶段执行具有不同索引要求的不同操作,并且合并是在帧之间进行有一些不重叠的列。

关于我的另一个关于使用 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),但帧有时很大,除非没有其他选择,否则我不想复制它们。

有没有办法让合并将它们识别为等效?如果没有,是否有避免此处暴露的转换问题的日期格式的最佳实践选择?

【问题讨论】:

您是否尝试在合并之前使用 df['date'] = pd.to_datetime(df['date'],unit='D') (或任何其他必要单位)强制日期时间? pandas.pydata.org/pandas-docs/stable/reference/api/… 这对输入有副作用,@rhedak,这是不允许的,并在链接的第二个问题中介绍。我必须复制框架以避免副作用,这是我希望不需要的。我想也许我可以只复制该系列,对其进行适当的修改,然后在最后将原始复制回来,但这仍然超出了理想情况下的必要性,尤其是因为在许多情况下数据类型是匹配的。最终我认为这是 Pandas 中的一个错误——它有时会改变类型而不是其他类型(有关详细信息,请参阅第一个链接问题)——但我需要一个解决方法。 你不能再做一列只是为了加入然后把它扔到最后吗? 【参考方案1】:

有没有办法让合并将它们识别为等效?

不,不是现在的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)

如果没有,是否有最佳实践选择日期格式来避免 转换问题暴露在这里?

我从您的问题中了解到,数据框及其数据类型超出了您的控制范围,并且无法更改,因此这个问题将无处可去。


您需要的是像 SQL 中的条件连接。此功能有一个旧的开放 issue,自 2014 年以来没有任何活动。(邀请 PR ......) 一个可能的workaround是这样的:
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。你用的是什么版本?

【讨论】:

我在一个使用 pandas 0.20.1 版本的系统上运行了这个例子。真正的代码在运行各种版本的多台机器上运行,尽管我还没有看到ValueError 用于这种情况,所以根据您的评论,我猜它们都没有运行 0.22.0 或更高版本。 好的,在这种情况下,您必须先重构 merge_on_date 函数以首先进行测试,并在左右为相同类型时进行常规合并。跨度>

以上是关于如何在支持多种数据格式的 Pandas 中合并日期?的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据框中的多列合并为 Pandas 日期时间格式

pandas设置excel单元格日期类型分秒

如何防止 pandas 将原始数据库日期格式转换为 pandas 日期格式

如何在 pandas 的数据框中选择多个日期列,然后将它们全部格式化? (Python)

Pandas技巧:万能转格式轻松合并压缩数据,让数据分析更高效

pandas设置excel单元格 日期 类型 分 秒