提问者:小点点

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


我正在编写一个Pandas例程库,该库需要能够处理可能具有不同类型的数据帧中的日期。具体来说,我得到了datetime.date熊猫类型的不同组合。_libs. tslb.Timestamp相当多。据报道(并通过我的测试证实),这与设置了多索引然后重置的帧有关。(请参阅我之前的问题什么改变了这个熊猫代码中的日期类型?,它解决了从多索引来回移动时类型被更改的问题。)

这里有一个简短(但做作)的例子:

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的位置已经改变,它们之间的合并是空的,而两者之间的“自合并”本身给出了预期的(尽管在这里的人为情况下,是多余和琐碎的)结果。

当然,真实的情况下,没有像这样随意地设置和重置索引就达到了这一点——真实的数据通过多个位代码,在不同的阶段执行不同的索引要求,合并是在具有一些不重叠列的帧之间进行的。

关于我关于使用NumPy datetime的另一个问题有一个评论,但这似乎是徒劳的,因为转换显然会导致两种有问题的数据类型之一,即底层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类型。)

我需要做的是能够合并库代码中的表,无论它们是否被我控制之外的调用者如此操纵。我作为输入获得的数据帧不能更改。我可以复制它们并在副本上实现直接转换(就像这里熊猫在日期列问题上合并一样),但是帧有时很大,除非没有其他选择,否则我不想复制它们。

有没有办法让合并将这些识别为等价的?如果没有,是否有最佳实践选择日期格式来避免这里暴露的转换问题?


共1个答案

匿名用户

有没有办法让合并将这些识别为等价的?

不,不是当前的熊猫代码:

    # 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)

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

我从您的问题中了解到,数据帧及其数据类型超出了您的控制范围,并且无法更改,因此这个问题不会给我们带来任何结果。

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

然而,这个很大的缺点是分配在幕后制作了一个副本。

PS:我刚刚看到pd. merge(df,df_reindex,on='date')在您的示例中产生了一个空数据帧。从版本0.22.0开始,这应该会引发ValueError。你使用的是什么版本?