使用多个DataFrame的pandas中的时间偏移

时间:2015-11-19 15:56:04

标签: python pandas

我有两只pandas Dataframes:

一个名为sdtarray的浮点数(代表秒数):

    z1  z2  z3  ...
0   NaN NaN NaN
1   2.6 3.4 63.0
2   NaN NaN NaN
3   0.1 1.1 60.7
4   4.7 5.2 64.9
5   0.1 0.6 61.1
...
[33945 rows x 95 columns]

和另一个格式化日期(新时间):

0    2014-09-01 05:22:00
1    2014-09-01 05:38:00
2    2014-09-01 06:08:00
3    2014-09-01 06:27:00
4    2014-09-01 06:37:00
5    2014-09-01 06:57:00
...
Name: thenewtime, dtype: datetime64[ns]

通过相应的日期(相同的行索引但是新的时间DataFrame)来偏移float DataFrame(sdtarray)中的每一行的最佳方法是什么 - 以日期的DataFrame结束?

示例输出为:

    z4                  z5                  z6              …
0   NaN                 NaN                 NaN 
1   01/09/2014 05:38:02 01/09/2014 05:38:03 01/09/2014 05:39:03 
2   NaN                 NaN                 NaN 
3   01/09/2014 06:27:00 01/09/2014 06:27:01 01/09/2014 06:28:00 
4   01/09/2014 06:37:04 01/09/2014 06:37:05 01/09/2014 06:38:04 
5   01/09/2014 06:57:00 01/09/2014 06:57:00 01/09/2014 06:58:01
…               

我正在使用pandas 0.13.1,我知道它并没有帮助,但由于ArcGIS要求它必须与numpy 1.7.1兼容,因此我坚持使用它。

我设法在每一行使用itertuples获得正确的输出(并且使用timedelta但是对于大数据来说它非常慢(34k行乘100列)并且必须有一种更有效的方式来不依赖于检查循环中的每个rol /列。

任何帮助和指导将不胜感激:)

4 个答案:

答案 0 :(得分:1)

编辑:

对于完全矢量化的解决方案,将秒转换为timedelta对象

seconds_td = (1e9*seconds.fillna(0)).astype('timedelta64')

乘以1e9转换为纳秒。我们填充空值,否则类型转换将失败。

你可以做到

result = pd.DataFrame(
    thenewtime.values + seconds_td.values.T
).T.where(df1.notnull().values)

请注意,此处的计算位于基础数组上。这是因为由于某些bug或其他原因,在使用Pandas对象时广播失败。 列名称将丢失,但您可以轻松地将它们放回:

result.columns = seconds.columns

它不是最干净的,但它应该比使用.applymap快几个数量级。您可以在更高版本的Pandas中更干净地完成它。以下是一些100K行的基准测试:

seconds = pd.DataFrame(np.random.rand(100000, 10))
seconds.loc[np.arange(0, 100000, 3)] = np.nan

%%timeit 
seconds_td = (1e9*seconds.fillna(0)).astype('timedelta64')
thenewtime = pd.date_range('20151120', freq='H', periods=100000)

result = pd.DataFrame(
    thenewtime.values + seconds_td.values.T
).T.where(seconds.notnull().values)
1 loops, best of 3: 247 ms per loop

%timeit seconds_td = seconds.applymap(lambda x: dt.timedelta(seconds=x) if not np.isnan(x) else None)
1 loops, best of 3: 6.54 s per loop

答案 1 :(得分:1)

下面,我首先检查它们不是df1 s,然后将数据帧的秒数(datetime.timedelta)转换为NaN个对象。然后,我将这些值添加到df2中的日期。

在熊猫0.13.1下测试。

import datetime as dt
import pandas as pd

df1 = pd.DataFrame({'z1': [None, 2.6, None, 0.1, 4.7, 0.1], 
                    'z2': [None, 3.4, None, 1.1, 5.2, 0.6], 
                    'z3': [None, 63, None, 60.7, 64.9, 61.1]})
df2 = pd.DataFrame({'Datetime': ['2014-09-01 05:22', '2014-09-01 05:38', '2014-09-01 06:08', 
                                 '2014-09-01 06:27', '2014-09-01 06:37', '2014-09-01 06:57']})
df2['Datetime'] = pd.to_datetime(df2.Datetime)

result = df1.applymap(lambda x: dt.timedelta(seconds=x) if not np.isnan(x) else None) 
         + np.tile(df2.values, (1, df1.shape[1]))

>>> pd.DataFrame(result)
                          z1                         z2                         z3
0                        NaT                        NaT                        NaT
1 2014-09-01 05:38:02.600000 2014-09-01 05:38:03.400000        2014-09-01 05:39:03
2                        NaT                        NaT                        NaT
3 2014-09-01 06:27:00.100000 2014-09-01 06:27:01.100000 2014-09-01 06:28:00.700000
4 2014-09-01 06:37:04.700000 2014-09-01 06:37:05.200000 2014-09-01 06:38:04.900000
5 2014-09-01 06:57:00.100000 2014-09-01 06:57:00.600000 2014-09-01 06:58:01.100000

答案 2 :(得分:1)

一种方法不像我的评论中那样简洁,在0.17.0中更简单,更容易:

In [81]:
def func(x):
    z1 = pd.NaT
    z2 = pd.NaT
    z3 = pd.NaT
    if pd.notnull(x['z1']):
        z1 = dt.timedelta(seconds =x['z1'])
    if pd.notnull(x['z2']):
        z2 = dt.timedelta(seconds =x['z2'])
    if pd.notnull(x['z3']):
        z3 = dt.timedelta(seconds =x['z3'])
    return pd.Series([z1,z2,z3])
date.values + sdtarray.apply(lambda row: func(row), axis=1) 

Out[81]:
                        0                       1                       2
0                     NaT                     NaT                     NaT
1 2014-09-01 05:38:02.600 2014-09-01 05:38:03.400 2014-09-01 05:39:03.000
2                     NaT                     NaT                     NaT
3 2014-09-01 06:27:00.100 2014-09-01 06:27:01.100 2014-09-01 06:28:00.700
4 2014-09-01 06:37:04.700 2014-09-01 06:37:05.200 2014-09-01 06:38:04.900
5 2014-09-01 06:57:00.100 2014-09-01 06:57:00.600 2014-09-01 06:58:01.100

答案 3 :(得分:0)

您可以使用dateutils包逐列完成。 如果df是秒数据帧而d2是日期数据帧

from dateutil.relativedelta import *

df2.columns = ['Date']
combo = df2.combine_first(df)
combo.fillna(0).apply(lambda x: x['Date'] +  relativedelta(seconds=x['z1']), axis=1)