代码之家  ›  专栏  ›  技术社区  ›  cs95 abhishek58g

在大熊猫的给定范围内生成随机日期

  •  33
  • cs95 abhishek58g  · 技术社区  · 8 年前

    这是一个自我回复的帖子。一个常见的问题是在给定的开始日期和结束日期之间随机生成日期。

    有两种情况需要考虑:

    1. 具有时间成分的随机日期,以及
    2. 没有时间的随机日期

    例如,给定一些开始日期 2015-01-01 还有结束日期 2018-01-01 ,如何使用pandas在该范围内随机抽取n个日期?

    8 回复  |  直到 7 年前
        1
  •  9
  •   Paul Panzer    8 年前

    我们可以利用以下事实加速@akilat90的方法,大约是两倍(在@coldspeed的基准中) datetime64 只是重新命名 int64 因此,我们可以查看演员表:

    def pp(start, end, n):
        start_u = start.value//10**9
        end_u = end.value//10**9
    
        return pd.DatetimeIndex((10**9*np.random.randint(start_u, end_u, n)).view('M8[ns]'))
    

    enter image description here

        2
  •  23
  •   akilat90    8 年前

    转换为Unix时间戳可以接受吗?

    def random_dates(start, end, n=10):
    
        start_u = start.value//10**9
        end_u = end.value//10**9
    
        return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')
    

    样本运行:

    start = pd.to_datetime('2015-01-01')
    end = pd.to_datetime('2018-01-01')
    random_dates(start, end)
    
    DatetimeIndex(['2016-10-08 07:34:13', '2015-11-15 06:12:48',
                   '2015-01-24 10:11:04', '2015-03-26 16:23:53',
                   '2017-04-01 00:38:21', '2015-05-15 03:47:54',
                   '2015-06-24 07:32:32', '2015-11-10 20:39:36',
                   '2016-07-25 05:48:09', '2015-03-19 16:05:19'],
                  dtype='datetime64[ns]', freq=None)
    

    编辑:

    根据@smci的评论,我编写了一个函数来容纳1和2,在函数本身中有一些解释。

    def random_datetimes_or_dates(start, end, out_format='datetime', n=10): 
    
        '''   
        unix timestamp is in ns by default. 
        I divide the unix time value by 10**9 to make it seconds (or 24*60*60*10**9 to make it days).
        The corresponding unit variable is passed to the pd.to_datetime function. 
        Values for the (divide_by, unit) pair to select is defined by the out_format parameter.
        for 1 -> out_format='datetime'
        for 2 -> out_format=anything else
        '''
        (divide_by, unit) = (10**9, 's') if out_format=='datetime' else (24*60*60*10**9, 'D')
    
        start_u = start.value//divide_by
        end_u = end.value//divide_by
    
        return pd.to_datetime(np.random.randint(start_u, end_u, n), unit=unit) 
    

    样本运行:

    random_datetimes_or_dates(start, end, out_format='datetime')
    
    DatetimeIndex(['2017-01-30 05:14:27', '2016-10-18 21:17:16',
                   '2016-10-20 08:38:02', '2015-09-02 00:03:08',
                   '2015-06-04 02:38:12', '2016-02-19 05:22:01',
    
    
                      '2015-11-06 10:37:10', '2017-12-17 03:26:02',
                       '2017-11-20 06:51:32', '2016-01-02 02:48:03'],
                      dtype='datetime64[ns]', freq=None)
    
    random_datetimes_or_dates(start, end, out_format='not datetime')
    
    DatetimeIndex(['2017-05-10', '2017-12-31', '2017-11-10', '2015-05-02',
                   '2016-04-11', '2015-11-27', '2015-03-29', '2017-05-21',
                   '2015-05-11', '2017-02-08'],
                  dtype='datetime64[ns]', freq=None)
    
        3
  •  12
  •   cs95 abhishek58g    7 年前

    np.random.randn + to_timedelta

    这解决了案例(1)。您可以通过生成一个随机数组 timedelta 对象并将它们添加到 start 日期。

    def random_dates(start, end, n, unit='D', seed=None):
        if not seed:  # from piR's answer
            np.random.seed(0)
    
        ndays = (end - start).days + 1
        return pd.to_timedelta(np.random.rand(n) * ndays, unit=unit) + start
    

    >>> np.random.seed(0)
    >>> start = pd.to_datetime('2015-01-01')
    >>> end = pd.to_datetime('2018-01-01')
    >>> random_dates(start, end, 10)
    DatetimeIndex([   '2016-08-25 01:09:42.969600',
                      '2017-02-23 13:30:20.304000',
                      '2016-10-23 05:33:15.033600',
                   '2016-08-20 17:41:04.012799999',
                   '2016-04-09 17:59:00.815999999',
                      '2016-12-09 13:06:00.748800',
                      '2016-04-25 00:47:45.974400',
                      '2017-09-05 06:35:58.444800',
                      '2017-11-23 03:18:47.347200',
                      '2016-02-25 15:14:53.894400'],
                  dtype='datetime64[ns]', freq=None)
    

    悲哀地, rand 不支持 replace=False ,因此,如果您想要唯一的日期,您将需要一个两步的过程:1)生成非唯一的日期组件,2)生成唯一的秒/毫秒组件,然后将两者相加。


    np.random.randint + 到时间三角洲

    这解决了案例(2)。您可以修改 random_dates 上面生成随机整数而不是随机浮点数:

    def random_dates2(start, end, n, unit='D', seed=None):
        if not seed:  # from piR's answer
            np.random.seed(0)
    
        ndays = (end - start).days + 1
        return start + pd.to_timedelta(
            np.random.randint(0, ndays, n), unit=unit
        )
    

    >>> random_dates2(start, end, 10)
    DatetimeIndex(['2016-11-15', '2016-07-13', '2017-04-15', '2017-02-02',
                   '2017-10-30', '2015-10-05', '2016-08-22', '2017-12-30',
                   '2016-08-23', '2015-11-11'],
                  dtype='datetime64[ns]', freq=None)
    

    要使用其他频率生成日期,可以使用不同的值调用上面的函数 unit . 此外,还可以添加参数 freq 并根据需要调整函数调用。

    如果你想的话 独特的 随机日期,可以使用 np.random.choice 具有 替换=假 :

    def random_dates2_unique(start, end, n, unit='D', seed=None):
        if not seed:  # from piR's answer
            np.random.seed(0)
    
        ndays = (end - start).days + 1
        return start + pd.to_timedelta(
            np.random.choice(ndays, n, replace=False), unit=unit
        )
    

    性能

    只对处理案例(1)的方法进行基准测试,因为案例(2)是任何方法都可以使用的特殊案例。 dt.floor .

    enter image description here 功能

    def cs(start, end, n):
        ndays = (end - start).days + 1
        return pd.to_timedelta(np.random.rand(n) * ndays, unit='D') + start
    
    def akilat90(start, end, n):
        start_u = start.value//10**9
        end_u = end.value//10**9
    
        return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')
    
    def piR(start, end, n):
        dr = pd.date_range(start, end, freq='H') # can't get better than this :-(
        return pd.to_datetime(np.sort(np.random.choice(dr, n, replace=False)))
    
    def piR2(start, end, n):
        dr = pd.date_range(start, end, freq='H')
        a = np.arange(len(dr))
        b = np.sort(np.random.permutation(a)[:n])
        return dr[b]
    

    基准代码

    from timeit import timeit
    
    import pandas as pd
    import matplotlib.pyplot as plt
    
    res = pd.DataFrame(
           index=['cs', 'akilat90', 'piR', 'piR2'],
           columns=[10, 20, 50, 100, 200, 500, 1000, 2000, 5000],
           dtype=float
    )
    
    for f in res.index: 
        for c in res.columns:
            np.random.seed(0)
    
            start = pd.to_datetime('2015-01-01')
            end = pd.to_datetime('2018-01-01')
    
            stmt = '{}(start, end, c)'.format(f)
            setp = 'from __main__ import start, end, c, {}'.format(f)
            res.at[f, c] = timeit(stmt, setp, number=30)
    
    ax = res.div(res.min()).T.plot(loglog=True) 
    ax.set_xlabel("N"); 
    ax.set_ylabel("time (relative)");
    
    plt.show()
    
        4
  •  6
  •   piRSquared    8 年前

    numpy.random.choice

    你可以利用numpy的随机选择。 choice 可能在很大程度上有问题 data_ranges . 例如,太大将导致内存错误。它需要存储整个内容以选择随机位。

    random_dates('2015-01-01', '2018-01-01', 10, 'ns', seed=[3, 1415])
    
    MemoryError
    

    而且,这需要一种分类。

    def random_dates(start, end, n, freq, seed=None):
        if seed is not None:
            np.random.seed(seed)
    
        dr = pd.date_range(start, end, freq=freq)
        return pd.to_datetime(np.sort(np.random.choice(dr, n, replace=False)))
    
    random_dates('2015-01-01', '2018-01-01', 10, 'H', seed=[3, 1415])
    
    DatetimeIndex(['2015-04-24 02:00:00', '2015-11-26 23:00:00',
                   '2016-01-18 00:00:00', '2016-06-27 22:00:00',
                   '2016-08-12 17:00:00', '2016-10-21 11:00:00',
                   '2016-11-07 11:00:00', '2016-12-09 23:00:00',
                   '2017-02-20 01:00:00', '2017-06-17 18:00:00'],
                  dtype='datetime64[ns]', freq=None)
    

    numpy.random.permutation

    类似于其他答案。不过,我喜欢这个答案,因为它切分了 datetimeindex 生产商 date_range 自动返回另一个 日期时间索引 .

    def random_dates_2(start, end, n, freq, seed=None):
        if seed is not None:
            np.random.seed(seed)
    
        dr = pd.date_range(start, end, freq=freq)
        a = np.arange(len(dr))
        b = np.sort(np.random.permutation(a)[:n])
        return dr[b]
    
        5
  •  2
  •   BENY    8 年前

    我发现一个新的基础库生成的日期范围,似乎在我这边比 pandas.data_range ,来自此的学分 answer

    from dateutil.rrule import rrule, DAILY
    import datetime, random
    def pick(start,end,n):
        return (random.sample(list(rrule(DAILY, dtstart=start,until=end)),n))
    
    
    pick(datetime.datetime(2010, 2, 1, 0, 0),datetime.datetime(2010, 2, 5, 0, 0),2)
    [datetime.datetime(2010, 2, 3, 0, 0), datetime.datetime(2010, 2, 2, 0, 0)]
    
        6
  •  2
  •   P.Tillmann Zhongxia Yan    8 年前

    就我的两分钱,使用日期范围和样本:

    def random_dates(start, end, n, seed=1, replace=False):
        dates = pd.date_range(start, end).to_series()
        return dates.sample(n, replace=replace, random_state=seed)
    
    random_dates("20170101","20171223", 10, seed=1)
    Out[29]: 
    2017-10-01   2017-10-01
    2017-08-23   2017-08-23
    2017-11-30   2017-11-30
    2017-06-15   2017-06-15
    2017-11-18   2017-11-18
    2017-10-31   2017-10-31
    2017-07-31   2017-07-31
    2017-03-07   2017-03-07
    2017-09-09   2017-09-09
    2017-10-15   2017-10-15
    dtype: datetime64[ns]
    
        7
  •  0
  •   Alex    8 年前

    这是另一种方法:也许有人会需要它。

    from datetime import datetime
    import random
    import numpy as np
    import pandas as pd
    
    N = 10 #N-samples
    dates = np.zeros([N,3])
    
    for i in range(0,N):
        year = random.randint(1970, 2010) 
        month = random.randint(1, 12)
        day = random.randint(1, 28)
        #if you need to change it use variables :3
        birth_date = datetime(year, month, day)
        dates[i] = [year,month,day]
    
    df = pd.DataFrame(dates.astype(int))
    df.columns = ['year', 'month', 'day']
    pd.to_datetime(df)
    

    结果:

    0   1999-08-22
    1   1989-04-27
    2   1978-10-01
    3   1998-12-09
    4   1979-04-19
    5   1988-03-22
    6   1992-03-02
    7   1993-04-28
    8   1978-10-04
    9   1972-01-13
    dtype: datetime64[ns]
    
        8
  •  0
  •   Dheeraj Inampudi    7 年前

    我认为这是一个简单的解决方案,只需在熊猫的日期框中创建一个日期字段。

    list1 = []
    for x in range(0,365):
        list1.append(x)
    date = pd.DataFrame(pd.to_datetime(list1, unit='D',origin=pd.Timestamp('2018-01-01')))