복붙노트

[PYTHON] 판다에서 주어진 범위 내에서 임의의 날짜 생성하기

PYTHON

판다에서 주어진 범위 내에서 임의의 날짜 생성하기

이것은 자기 답신 게시물입니다. 일반적인 문제는 주어진 시작 날짜와 종료 날짜 사이에서 임의로 날짜를 생성하는 것입니다.

예를 들어 시작 날짜가 2015-01-01이고 종료 날짜가 2018-01-01 인 경우 팬더를 사용하여이 범위 사이에서 임의의 임의의 날짜를 어떻게 샘플링 할 수 있습니까?

고려해야 할 두 가지 경우가 있습니다.

몇 줄의 코드에서 두 가지 방법을 아래에서 모두 달성 할 수있는 방법을 설명합니다.

해결법

  1. ==============================

    1.우리는 datetime64가 rebranded int64라는 사실을 이용하여 @ akilat90의 접근 방식을 두 배 정도 (@ coldspeed의 벤치 마크에서) 빠르게 할 수 있습니다. 따라서 우리는 view-cast를 할 수 있습니다 :

    우리는 datetime64가 rebranded int64라는 사실을 이용하여 @ akilat90의 접근 방식을 두 배 정도 (@ coldspeed의 벤치 마크에서) 빠르게 할 수 있습니다. 따라서 우리는 view-cast를 할 수 있습니다 :

    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]'))
    

  2. ==============================

    2.유닉스 타임 스탬프로 변환 가능합니까?

    유닉스 타임 스탬프로 변환 가능합니까?

    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. ==============================

    3.이것은 사례 (1)을 다룬다. Timedelta 오브젝트의 무작위 배열을 생성하고이를 시작 날짜에 추가하여이를 수행 할 수 있습니다.

    이것은 사례 (1)을 다룬다. Timedelta 오브젝트의 무작위 배열을 생성하고이를 시작 날짜에 추가하여이를 수행 할 수 있습니다.

    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를 지원하지 않으므로 고유 한 날짜를 원한다면 다음 두 단계 프로세스가 필요합니다.

    그리고 두 개를 합치십시오.

    이것은 사례 (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)
    

    다른 빈도로 날짜를 생성하려면 위의 함수를 단위에 대해 다른 값으로 호출 할 수 있습니다. 또한 매개 변수 freq를 추가하고 필요에 따라 함수 호출을 조정할 수 있습니다.

    고유 한 무작위 날짜가 필요하면 replace = False와 함께 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
        )
    

    Case (2)는 어떤 메소드가 dt.floor를 사용할 수있는 특별한 경우이기 때문에 Case (1)을 처리하는 메소드 만 벤치마킹하려고합니다.

    기능들

    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. ==============================

    4.Numpy의 임의 선택을 활용할 수 있습니다. 큰 data_ranges보다 선택이 문제가 될 수 있습니다. 예를 들어, 너무 큰 경우 MemoryError가 발생합니다. 그것은 임의의 비트를 선택하기 위해 전체를 저장해야합니다.

    Numpy의 임의 선택을 활용할 수 있습니다. 큰 data_ranges보다 선택이 문제가 될 수 있습니다. 예를 들어, 너무 큰 경우 MemoryError가 발생합니다. 그것은 임의의 비트를 선택하기 위해 전체를 저장해야합니다.

    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)
    

    다른 답변과 유사합니다. 그러나 date_range에 의해 생성 된 datetimeindex를 조각화하고 다른 datetimeindex를 자동으로 반환하므로이 대답을 좋아합니다.

    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. ==============================

    5.나는 새로운베이스 라이브러리가 날짜의 범위를 생성했다는 것을 발견했다. pandas.data_range보다 약간 빠른 것 같다.

    나는 새로운베이스 라이브러리가 날짜의 범위를 생성했다는 것을 발견했다. pandas.data_range보다 약간 빠른 것 같다.

    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. ==============================

    6.date_range와 sample을 사용하여 나의 2 센트 만 :

    date_range와 sample을 사용하여 나의 2 센트 만 :

    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. ==============================

    7.어떤 다른 방법 : D 아마 누군가가 그것을 필요로 할 것입니다.

    어떤 다른 방법 : D 아마 누군가가 그것을 필요로 할 것입니다.

    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. ==============================

    8.나는 이것이 판다 DateFrame에서 날짜 필드를 만드는 더 쉬운 해결책이라고 생각한다.

    나는 이것이 판다 DateFrame에서 날짜 필드를 만드는 더 쉬운 해결책이라고 생각한다.

    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')))
    
  9. from https://stackoverflow.com/questions/50559078/generating-random-dates-within-a-given-range-in-pandas by cc-by-sa and MIT license