이번 포스팅에서는 


(1) Python에서 시간대 (time zone) 확인하기

(2) pandas 에서 date_range()로 날짜-시간 생성 시 시간대(time zone)를 설정하기 

     (time zone setting)

(3) 시간대 정보가 없는 naive 상태에서 지역 시간대로 변경하기 

     (convert from naive timezone to localized timezone)

(4) 날짜-시간 DatetimeIndex의 특정 시간대를 다른 시간대로 변경하기 

     (converst from a timezone to another timezone)


하는 방법을 소개하겠습니다. 



  (1) Python에서 시간대 (time zone) 확인하기


국가 간을 넘나들면서 여러 시간대에 걸쳐서 업무를 봐야 한다거나, 일광 절약 시간(미국식 Daylight Savings Time, DST, 영국식 Summer Time) 을 적용하고 있는 나라 (예: 미국, 캐나다, 대부분의 유럽 국가, 호주 일부 지역) 에서는 시간대를 고려해서 프로그래밍을 해야 한다는게 머리가 아픈 일입니다. 


그래서 국가/지역별 시간대의 국제 표준으로 UTC (Coordinated Univeral Time, 이전의 Greenwich Mean Time, GMT) 시간대를 많이 사용합니다. 아래 지도는 국가별 시간대를 나타낸 것인데요, 영국의 Greenwich 천문대를 지나는 지도의 가운데 부분이 바로 UTC 시간대입니다.  


참고로, 한국, 일본, 호주 가운데 지역은 UTC + 9hour 시간대에 속합니다. 


[ Standard Time Zones of the World ]

* 출처: https://en.wikipedia.org/wiki/Coordinated_Universal_Time#/media/File:World_Time_Zones_Map.png



Python에서는 pytz 라이브러리를 사용해서 시간대 정보를 확인할 수 있으며, pandas는 pytz 라이브러리를 wrap 해서 시간대 정보를 다루고 있습니다. 


아시아 지역의 시간대 이름 (time zone names in Asia)을 살펴보겠습니다. 



# time zone information

import pytz


# regular expression in Python

import re


# regular expression for pattern containing 'Asia' texts

pattern = re.compile(r'^Asia')


# list comprehension for selecting 'Asia****' time zones

tz_asia = [x for x in pytz.common_timezones if pattern.match(x)]

tz_asia

[Out]:
['Asia/Aden',
 'Asia/Almaty',
 'Asia/Amman',
 'Asia/Anadyr',
 'Asia/Aqtau',
 'Asia/Aqtobe',
 'Asia/Ashgabat',
 'Asia/Atyrau',
 'Asia/Baghdad',
 'Asia/Bahrain',
 'Asia/Baku',
 'Asia/Bangkok',
 'Asia/Barnaul',
 'Asia/Beirut',
 'Asia/Bishkek',
 'Asia/Brunei',
 'Asia/Chita',
 'Asia/Choibalsan',
 'Asia/Colombo',
 'Asia/Damascus',
 'Asia/Dhaka',
 'Asia/Dili',
 'Asia/Dubai',
 'Asia/Dushanbe',
 'Asia/Famagusta',
 'Asia/Gaza',
 'Asia/Hebron',
 'Asia/Ho_Chi_Minh',
 'Asia/Hong_Kong',
 'Asia/Hovd',
 'Asia/Irkutsk',
 'Asia/Jakarta',
 'Asia/Jayapura',
 'Asia/Jerusalem',
 'Asia/Kabul',
 'Asia/Kamchatka',
 'Asia/Karachi',
 'Asia/Kathmandu',
 'Asia/Khandyga',
 'Asia/Kolkata',
 'Asia/Krasnoyarsk',
 'Asia/Kuala_Lumpur',
 'Asia/Kuching',
 'Asia/Kuwait',
 'Asia/Macau',
 'Asia/Magadan',
 'Asia/Makassar',
 'Asia/Manila',
 'Asia/Muscat',
 'Asia/Nicosia',
 'Asia/Novokuznetsk',
 'Asia/Novosibirsk',
 'Asia/Omsk',
 'Asia/Oral',
 'Asia/Phnom_Penh',
 'Asia/Pontianak',
 'Asia/Pyongyang',
 'Asia/Qatar',
 'Asia/Qostanay',
 'Asia/Qyzylorda',
 'Asia/Riyadh',
 'Asia/Sakhalin',
 'Asia/Samarkand',
 'Asia/Seoul',
 'Asia/Shanghai',
 'Asia/Singapore',
 'Asia/Srednekolymsk',
 'Asia/Taipei',
 'Asia/Tashkent',
 'Asia/Tbilisi',
 'Asia/Tehran',
 'Asia/Thimphu',
 'Asia/Tokyo',
 'Asia/Tomsk',
 'Asia/Ulaanbaatar',
 'Asia/Urumqi',
 'Asia/Ust-Nera',
 'Asia/Vientiane',
 'Asia/Vladivostok',
 'Asia/Yakutsk',
 'Asia/Yangon',
 'Asia/Yekaterinburg',
 'Asia/Yerevan']

 



아래는 한국의 서울, 싱가폴, 중국의 상해, 일본의 도쿄의 시간대 정보를 조회해 본 결과입니다. 



# UTC: coordinated universal time

pytz.timezone('UTC')

[Out]: <UTC>


pytz.timezone('Asia/Seoul')

[Out]: <DstTzInfo 'Asia/Seoul' LMT+8:28:00 STD>


pytz.timezone('Asia/Singapore')

[Out]: <DstTzInfo 'Asia/Singapore' LMT+6:55:00 STD>


pytz.timezone('Asia/Shanghai')

[Out]: <DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>

pytz.timezone('Asia/Tokyo')

[Out]: <DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD>






  (2) 시간대를 포함해서 날짜-시간 범위 만들기 (generate date ranges with time zone)


pandas 의 date_range() 함수로 날짜-시간 DatetimeIndex를 생성할 때 tz = 'time_zone_name' 옵션을 사용하면 시간대(time zone)를 설정해줄 수 있습니다. 아래 예는 'Asia/Seoul' 시간대를 설정해서 2019-12-28 부터 4일 치 날짜를 생성한 것입니다. 



import pandas as pd


ts_seoul = pd.date_range('2019-12-28', periods=4, freq='D', tz='Asia/Seoul')

ts_seoul

[Out]:
DatetimeIndex(['2019-12-28 00:00:00+09:00', '2019-12-29 00:00:00+09:00',
               '2019-12-30 00:00:00+09:00', '2019-12-31 00:00:00+09:00'],
              dtype='datetime64[ns, Asia/Seoul]', freq='D')



ts_seoul_series = pd.Series(range(len(ts_seoul_idx)), index = ts_seoul)

ts_seoul_series.index.tz

[Out]:
<DstTzInfo 'Asia/Seoul' LMT+8:28:00 STD>





  (3) 시간대가 없는 naive 상태에서 지역 시간대 설정하기 

       (convert from naive to localized time zone)




pandas 의 date_range() 함수로 날짜-시간 DatetimeIndex를 생성하면 디폴트로는 시간대가 없는 naive 상태로 만들어집니다. 이런 naive time-zone에서 특정 국가/지역의 시간대를 설정하고 싶을 때 tz_localize('timezone_name') 메소드를 사용합니다. 



# timezone-naive timestamps

ts_naive = pd.date_range('2019-12-28', periods=6, freq='D')

ts_naive

[Out]:
DatetimeIndex(['2019-12-28', '2019-12-29', '2019-12-30', '2019-12-31',
               '2020-01-01', '2020-01-02'],
              dtype='datetime64[ns]', freq='D')


# localize timezone to 'Asia/Seoul' using tz_localize() methods

ts_local_seoul = ts_naive.tz_localize('Asia/Seoul')

ts_local_seoul

[Out]:
DatetimeIndex(['2019-12-28 00:00:00+09:00', '2019-12-29 00:00:00+09:00',
               '2019-12-30 00:00:00+09:00', '2019-12-31 00:00:00+09:00',
               '2020-01-01 00:00:00+09:00', '2020-01-02 00:00:00+09:00'],
              dtype='datetime64[ns, Asia/Seoul]', freq='D')




만약 naive time-zone 상태에서 시간대를 설정해주기 위해 tz_convert('timezone_name') 메소드를 사용하면 'TypeError: Connot convert tz-naive timestmaps, use tz_localize to localize' 라는 타입 에러가 발생합니다. 



# TypeError: Cannot convert tz-naive timestamps, use tz_localize to localize

ts_naive.tz_convert('Asia/Seoul')

--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-69-270d3596ed05> in <module> ----> 1 ts_naive.tz_convert('Asia/Seoul') --- 중간 생략 --- ~/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages/pandas/core/arrays/datetimes.py in tz_convert(self, tz) 958 # tz naive, use tz_localize 959 raise TypeError( --> 960 "Cannot convert tz-naive timestamps, use " "tz_localize to localize" 961 ) 962 TypeError: Cannot convert tz-naive timestamps, use tz_localize to localize

 





  (4) 특정 시간대를 다른 시간대로 바꾸기 (convert from a time-zone to another one)


아래의 예는 tz_convert('Asia/Singapore') 메소드를 이용해서 'Asia/Seoul' 시간대를 'Asia/Singapore' 시간대로 변경해보았습니다. 



# timezone 'Asia/Seoul'

ts_seoul = pd.date_range('2019-12-28', periods=4, freq='D', tz='Asia/Seoul')

ts_seoul

DatetimeIndex(['2019-12-28 00:00:00+09:00', '2019-12-29 00:00:00+09:00',
               '2019-12-30 00:00:00+09:00', '2019-12-31 00:00:00+09:00'],
              dtype='datetime64[ns, Asia/Seoul]', freq='D')


# convert from 'Asia/Seoul' to 'Asia/Singapore' using tz_convert()

ts_singapore = ts_seoul.tz_convert('Asia/Singapore')

ts_singapore

[Out]:
DatetimeIndex(['2019-12-27 23:00:00+08:00', '2019-12-28 23:00:00+08:00',
               '2019-12-29 23:00:00+08:00', '2019-12-30 23:00:00+08:00'],
              dtype='datetime64[ns, Asia/Singapore]', freq='D')




많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



[ 파이썬 시간대 이름 (Python Timezone Names) ]


import pytz

pytz.common_timezones

 Africa

 America

['Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 'Africa/Dakar',
 'Africa/Dar_es_Salaam',
 'Africa/Djibouti',
 'Africa/Douala',
 'Africa/El_Aaiun',
 'Africa/Freetown',
 'Africa/Gaborone',
 'Africa/Harare',
 'Africa/Johannesburg',
 'Africa/Juba',
 'Africa/Kampala',
 'Africa/Khartoum',
 'Africa/Kigali',
 'Africa/Kinshasa',
 'Africa/Lagos',
 'Africa/Libreville',
 'Africa/Lome',
 'Africa/Luanda',
 'Africa/Lubumbashi',
 'Africa/Lusaka',
 'Africa/Malabo',
 'Africa/Maputo',
 'Africa/Maseru',
 'Africa/Mbabane',
 'Africa/Mogadishu',
 'Africa/Monrovia',
 'Africa/Nairobi',
 'Africa/Ndjamena',
 'Africa/Niamey',
 'Africa/Nouakchott',
 'Africa/Ouagadougou',
 'Africa/Porto-Novo',
 'Africa/Sao_Tome',
 'Africa/Tripoli',
 'Africa/Tunis',
 'Africa/Windhoek']

['America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Bahia', 'America/Bahia_Banderas', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Costa_Rica', 'America/Creston', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Fort_Nelson', 'America/Fortaleza', 'America/Glace_Bay', 'America/Godthab', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Kralendijk', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Lower_Princes', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Menominee', 'America/Merida', 'America/Metlakatla', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nipigon', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Beulah', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Ojinaga', 'America/Panama', 'America/Pangnirtung', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Punta_Arenas', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Sitka', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Thunder_Bay', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 

'America/Yellowknife' ]


 Antarctica
 [ 'Antarctica/Casey',

'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/Syowa', 'Antarctica/Troll', 'Antarctica/Vostok', 'Arctic/Longyearbyen']

 Australia
[  'Australia/Adelaide',

'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/Perth', 'Australia/Sydney']

 Canada

[  'Canada/Atlantic',

'Canada/Central', 'Canada/Eastern', 'Canada/Mountain', 'Canada/Newfoundland', 'Canada/Pacific']

 Europe
 [ 'Europe/Amsterdam',
 'Europe/Andorra',
 'Europe/Astrakhan',
 'Europe/Athens',
 'Europe/Belgrade',
 'Europe/Berlin',
 'Europe/Bratislava',
 'Europe/Brussels',
 'Europe/Bucharest',
 'Europe/Budapest',
 'Europe/Busingen',
 'Europe/Chisinau',
 'Europe/Copenhagen',
 'Europe/Dublin',
 'Europe/Gibraltar',
 'Europe/Guernsey',
 'Europe/Helsinki',
 'Europe/Isle_of_Man',
 'Europe/Istanbul',
 'Europe/Jersey',
 'Europe/Kaliningrad',
 'Europe/Kiev',
 'Europe/Kirov',
 'Europe/Lisbon',
 'Europe/Ljubljana',
 'Europe/London',
 'Europe/Luxembourg',
 'Europe/Madrid',
 'Europe/Malta',
 'Europe/Mariehamn',
 'Europe/Minsk',
 'Europe/Monaco',
 'Europe/Moscow',
 'Europe/Oslo',
 'Europe/Paris',
 'Europe/Podgorica',
 'Europe/Prague',
 'Europe/Riga',
 'Europe/Rome',
 'Europe/Samara',
 'Europe/San_Marino',
 'Europe/Sarajevo',
 'Europe/Saratov',
 'Europe/Simferopol',
 'Europe/Skopje',
 'Europe/Sofia',
 'Europe/Stockholm',
 'Europe/Tallinn',
 'Europe/Tirane',
 'Europe/Ulyanovsk',
 'Europe/Uzhgorod',
 'Europe/Vaduz',
 'Europe/Vatican',
 'Europe/Vienna',
 'Europe/Vilnius',
 'Europe/Volgograd',
 'Europe/Warsaw',
 'Europe/Zagreb',
 'Europe/Zaporozhye', 
'Europe/Zurich']

 Pacific

 US

['Pacific/Apia', 'Pacific/Auckland', 'Pacific/Bougainville', 'Pacific/Chatham', 'Pacific/Chuuk', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Pohnpei', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Wake', 

'Pacific/Wallis']

[  'US/Alaska',
 'US/Arizona',
 'US/Central',
 'US/Eastern',
 'US/Hawaii',
 'US/Mountain',
 'US/Pacific']
 Indian
['Indian/Antananarivo',
 'Indian/Chagos',
 'Indian/Christmas',
 'Indian/Cocos',
 'Indian/Comoro',
 'Indian/Kerguelen',
 'Indian/Mahe',
 'Indian/Maldives',
 'Indian/Mauritius',
 'Indian/Mayotte', 
'Indian/Reunion'] 












728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 


(1) 텍스트 파일을 열어 각 Line 별로 읽어 들인 후에 문자열 메소드를 이용해 파싱(Parsing)

    --> pandas DataFrame으로 만들고, 


(2) ID를 기준으로 그룹별로 값을 한칸식 내려서(Lag) 새로운 칼럼을 만들기


를 해보겠습니다. 



아래와 같이 생긴 텍스트 파일이 있다고 하겠습니다. 

'color_range.txt' 파일

color_range.txt



첫번째 행 AAA는 0에서 100까지는 a 영역, 100부터 200까지는 b 영역이라는 의미입니다. 여기서 a(빨간색), b(파란색)은 색상을 나타내며, AAA 는 0(포함)부터 100(미포함)까지는 빨간색, 100(포함)부터 200(미포함)까지는 파란색, 200(포함)부터 300(미포함)까지는 빨간색, ... 을 의미합니다. 


이렇게 데이터가 행으로 옆으로 길게 늘여져서 쓰여진 파일을 'AAA', 'BBB' 의 ID별로 색깔(a: 빨간색, b: 파란색)별 시작 숫자와 끝 숫자를 알기 쉽게 각 칼럼으로 구분하여 pandas DataFrame으로 만들어보고자 합니다. 



  (1) 텍스트 파일을 열어 각 Line별로 읽어들인 후 문자열 메소드를 이용해 파싱(Parsing)

       --> pandas DataFrame 만들기



import pandas as pd

import os


# set file path

cwd = os.getcwd()

file_path = os.path.join(cwd, 'color_range.txt')



# read 'color_range.txt' file and parsing it by id and value

df = pd.DataFrame() # blank DataFrame to store results


# open file

f = open(file_path)


# parsing text line by line using for loop statement

for line in f.readlines():

    id_list = []

    color_list = []

    bin_list = []

    

    # remove white space

    line = line.strip()


    # delete '"'

    line = line.replace('"', '')

    

    # get ID and VALUE from a line

    id = line[:3]

    val = line[4:]

    

    # make a separator with comma(',')

    val = val.replace(' a', ',a')

    val = val.replace(' b', ',b')

    

    # split a line using separator ','

    val_split = val.split(sep=',')

    

    # get a 'ID', 'COLOR', 'BIN_END' values and append it to list

    for j in range(len(val_split)):

        id_list.append(id)

        color_list.append(val_split[j][:1])

        bin_list.append(val_split[j][2:])

    

    # make a temp DataFrame, having ID, COLOR, BIN_END values per each line

    # note: if a line has only one value(ie. Scalar), then it will erase 'index error'  :-(

    df_tmp = pd.DataFrame({'id': id_list, 

                           'color_cd': color_list, 

                           'bin_end': bin_list}

                         )

    

    # combine df and df_tmp one by one

    df = pd.concat([df, df_tmp], axis=0, ignore_index=True)



# let's check df DataFrame

df

[Out]:

idcolor_cdbin_end
0AAAa100
1AAAb200
2AAAa300
3AAAb400
4BBBa250
5BBBb350
6BBBa450
7BBBb550
8BBBa650
9BBBb750
10BBBa800
11BBBb910





  (2) ID를 기준으로 그룹별로 값을 한칸식 내려서(Lag) 새로운 칼럼을 만들기


'ID'를 기준으로 'bin_end' 칼럼을 한칸씩 내리고 (shift(1)), 첫번째 행의 결측값은 '0'으로 채워(fillna(0))보겠습니다. 



# lag 1 group by 'id' and fill missing value with '0'

df['bin_start'] = df.groupby('id')['bin_end'].shift(1).fillna(0)

[Out]:

idcolor_cdbin_endbin_start
0AAAa1000
1AAAb200100
2AAAa300200
3AAAb400300
4BBBa2500
5BBBb350250
6BBBa450350
7BBBb550450
8BBBa650550
9BBBb750650
10BBBa800750
11BBBb910800




color code ('color_cd')에서 'a' 는 빨간색(red), 'b'는 파란색(blue) 이라는 색깔 이름을 매핑해보겠습니다. 



# mapping color using color_cd

color_map = {'a': 'red', 

             'b': 'blue'}


df['color'] = df['color_cd'].map(lambda x: color_map.get(x, x))

df

[Out]:

idcolor_cdbin_endbin_startcolor
0AAAa1000red
1AAAb200100blue
2AAAa300200red
3AAAb400300blue
4BBBa2500red
5BBBb350250blue
6BBBa450350red
7BBBb550450blue
8BBBa650550red
9BBBb750650blue
10BBBa800750red
11BBBb910800blue




보기에 편리하도록 칼럼 순서를 'id', 'color_cd', 'color', 'bin_start', 'bin_end' 의 순서대로 재배열 해보겠습니다. 



# change the sequence of columns

df = df[['id', 'color_cd', 'color', 'bin_start', 'bin_end']]

df

[Out]:

idcolor_cdcolorbin_startbin_end
0AAAared0100
1AAAbblue100200
2AAAared200300
3AAAbblue300400
4BBBared0250
5BBBbblue250350
6BBBared350450
7BBBbblue450550
8BBBared550650
9BBBbblue650750
10BBBared750800
11BBBbblue800910

 



bin_start 는 포함하고 (include), bin_end 는 포함하지 않는(not include) 것을 알기 쉽도록 

 ==> 포함('[') 기호 + 'bin_start', 'bin_end' + 미포함(')') 기호를 덧붙여서 

'bin_range'라는 새로운 칼럼을 만들어보겠습니다. 



# make a 'Bin Range' column with include '[' and exclude ')' sign

df['bin_range'] = df['bin_start'].apply(lambda x: '[' + str(x) + ',') + \

    df['bin_end'].apply(lambda x: str(x + ')'))


df

[Out]:

idcolor_cdcolorbin_startbin_endbin_range
0AAAared0100[0,100)
1AAAbblue100200[100,200)
2AAAared200300[200,300)
3AAAbblue300400[300,400)
4BBBared0250[0,250)
5BBBbblue250350[250,350)
6BBBared350450[350,450)
7BBBbblue450550[450,550)
8BBBared550650[550,650)
9BBBbblue650750[650,750)
10BBBared750800[750,800)
11BBBbblue800910[800,910)

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 numpy와 pandas를 이용해서 차수 m인 단순 이동평균 구하는 방법 (https://rfriend.tistory.com/502) 를 소개하였습니다. 


이번 포스팅에서는 Python pandas에서 시계열 데이터를 생성할 때 유용하게 사용할 수 있는 빈도와 날짜 Offsets (pandas Frequencies and Date Offsets)에 대해서 알아보겠습니다.  2020년 달력을 가지고 Offset Type 별로 Alias 사용해가면서 결과값 확인해보도록 하겠습니다. 



[ Python pandas Base Time Series Frequencies and Date Offsets ]




(* 'Frequency'를 '빈도'라고 번역하는게 좋을지, 아니면 '주기'라고 번역하는게 좋을지 고민스럽습니다. 저는 의미상으로는 '주기'라고 번역하는게 더 적합할 것 같다고 생각하는데요, 이미 '빈도'라고 번역이 되어서 사용되고 있네요. Offset은 뭐라고 번역하는게 좋을지 잘 모르겠네요.)



  (1) 3일 주기의 날짜 데이터 생성하기 (generate dates with 3 days frequency)


pandas 의 Frequencies는 'base frequency'와 'multiplier'로 구성되어 있으며, base frequency는 Alias 문자열(alias string)를 사용하여 호출해서 이용합니다. 아래의 예는 'Day'의 Alias인 'd'(or 'D')에 '3'을 곱하여(multiplier) '3 Days' Frequency (빈도, 주기)의 날짜 범위를 8개 (periods = 8) 생성한 것입니다. 



import pandas as pd


pd.date_range('2019-12-01', periods = 8, freq = '3d') # or freq = '3D'

[Out]:

DatetimeIndex(['2019-12-01', '2019-12-04', '2019-12-07', '2019-12-10', '2019-12-13', '2019-12-16', '2019-12-19', '2019-12-22'], dtype='datetime64[ns]', freq='3D')

 



freq = 3 * '1D' 과 같이 명시적으로 곱하기 3을 밖으로 빼어서 표기해도 freq = '3D'와 결과 값은 동일합니다.  



pd.date_range('2019-12-01', periods = 8, freq = 3 * '1d') # or freq = 3 * '1D'

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-04', '2019-12-07', '2019-12-10',
               '2019-12-13', '2019-12-16', '2019-12-19', '2019-12-22'],
              dtype='datetime64[ns]', freq='3D')

 



그리고 base frequency는 'date offset' 이라는 클래스 객체(class object)를 가지고 있습니다. 아래에 pandas.tseries.offsets 으로부터 일(Day), 시간(Hour), 분(Minute), 초(Minute) date offsets을 불어와서, freq = Day(3)과 같이 Day(3)의 date offset으로 위의 freq = '3d'와 동일한 결과를 얻었습니다.  



from pandas.tseries.offsets import Day, Hour, Minute, Second


pd.date_range('2019-12-01', periods = 8, freq = Day(3))

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-04', '2019-12-07', '2019-12-10',
               '2019-12-13', '2019-12-16', '2019-12-19', '2019-12-22'],
              dtype='datetime64[ns]', freq='3D')




2일(2 Days) + 23시간 (23 Hours) + 59분 (59 Minutes) + 60초 (60 Seconds) = 3 일 (Days)  이므로 아래의 myfreq = Day(2) + Hour(23) + Minute(59) + Second(60) 으로 freq = myfreq 를 사용하여 날짜를 생성하면 위와 동일한 결과를 반환합니다. (3일 주기의 8개 날짜 생성)



from pandas.tseries.offsets import Day, Hour, Minute, Second


myfreq = Day(2) + Hour(23) + Minute(59) + Second(60) # 3 days

myfreq

[Out]: <3 * Days>


pd.date_range('2019-12-01', periods = 8, freq = myfreq)

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-04', '2019-12-07', '2019-12-10',
               '2019-12-13', '2019-12-16', '2019-12-19', '2019-12-22'],
              dtype='datetime64[ns]', freq='3D')

 



물론, freq = '2D23H59min60S' (혹은 freq = '2d23h59T60s') 로 Frequency 의 Alias를 사용해도 결과는 동일합니다. 



pd.date_range('2019-12-01', periods = 8, freq = '2D23H59min60S') # or freq = '2d23h59T60s'

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-04', '2019-12-07', '2019-12-10',
               '2019-12-13', '2019-12-16', '2019-12-19', '2019-12-22'],
              dtype='datetime64[ns]', freq='3D')





각 주/월/분기별 (a) 시작 날짜와 마지막 날짜, (b) 공휴일이 아닌(business day) 시작 날짜와 마지막 날짜를 가져올 수 있는 Data Offset, Alias를 살펴보기 위해 아래의 2020년 달력을 봐가면서 예를 들어보겠습니다.  






  (2) Month End vs. Business Month End, Month Begin vs. Business Month Begin


(2-1) (a) 월의 마지막 날짜(Month End) vs. (b) 월의 공휴일이 아닌 마지막 날짜 (Business Month End)


아래는 (a) 2020년 1월 ~ 8월의 각 월별 마지막 날짜 (offset type: MonthEnd, alias: 'M')와, (b) 각 월별 공휴일이 아닌 마지막 날짜(offset type: Business Month End, alias: 'BM')로 DatetimeIndex 를 생성해보았습니다. 2020년 2월달과 5월달의 'Month End'와 'Business Month End'가 서로 다르게 정확하게 생성되었다는 것을 위의 2020년 달력과 아래의 날짜 생성결과로 확인할 수 있습니다. 



# (a) Month End: 'M'

pd.date_range('2020-01-01', periods = 8, freq = 'M')

[Out]:
DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31', '2020-04-30',
               '2020-05-31', '2020-06-30', '2020-07-31', '2020-08-31'],
              dtype='datetime64[ns]', freq='M')

 



# (b) Business Month End: 'BM'

pd.date_range('2020-01-01', periods = 8, freq = 'BM')

[Out]:
DatetimeIndex(['2020-01-31', '2020-02-28', '2020-03-31', '2020-04-30',
               '2020-05-29', '2020-06-30', '2020-07-31', '2020-08-31'],
              dtype='datetime64[ns]', freq='BM')





(2-2) (a) 월별 시작 날짜(Month Begin) vs. (b) 월별 공휴일이 아닌 시작 날짜(Business Month Begin)


아래는 (a) 2020년 1월 ~ 8월까지 각 월별 시작 날짜(offset type: MonthStart, alias: 'MS') 와, (b) 공휴일이 아닌 시작 날짜(offset type: BusinessMonthBegin, alias:'BMS') 로 DatetimeIndex 를 생성하였습니다. 



# (a) Month Start: 'MS'

pd.date_range('2020-01-01', periods = 8, freq = 'MS')

[Out]:
DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01',
               '2020-05-01', '2020-06-01', '2020-07-01', '2020-08-01'],
              dtype='datetime64[ns]', freq='MS')




# (b) Business Month Start: 'BMS'

pd.date_range('2020-01-01', periods = 8, freq = 'BMS')

[Out]:
DatetimeIndex(['2020-01-01', '2020-02-03', '2020-03-02', '2020-04-01',
               '2020-05-01', '2020-06-01', '2020-07-01', '2020-08-03'],
              dtype='datetime64[ns]', freq='BMS')

 




  (3) 주별 특정 요일 날짜 (Week)


아래의 예는 2020년 1월 1일 이후의 날 중에서 매주 월요일(freq = 'W-MON'에 해당하는 날짜 8개로 DatetimeIndex를 생성한 것입니다. 각 요일별 alias는 영문 요일의 앞에서부터 3번째 자리까지의 알파벳입니다. 



# -- Week 'Alias': Offset Type

# 'W-MON': Monday

# 'W-TUE': Tuesday

# 'W-WED': Wednesday

# 'W-THU': Thursday

# 'W-FRI': Friday

# 'W-SAT': Saturday

# 'W-SUN': Sunday

pd.date_range('2020-01-01', periods = 8, freq = 'W-MON')

[Out]:

DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27', '2020-02-03', '2020-02-10', '2020-02-17', '2020-02-24'], dtype='datetime64[ns]', freq='W-MON')

 




  (4) 월별 특정 순번째의 요일 날짜 (Week of Month)


아래의 예는 2020년 1월 1일 이후의 날 중에서 매월 첫번째 금요일(freq = 'WOM-1FRI')에 해당하는 날째 8개로 DatetimeIndex를 생성한 것입니다.  만약 이러한 요건에 해당하는 offset type이 없어서 매뉴얼하게 코딩을 해야 한다고 생각하면 골치가 좀 아플것 같은데요, 매우 편리한 기능입니다. 

(가령, 매월 2번째 화요일로 날짜를 생성하고 싶다면 freq = "WOM-2TUE" 로 해주면 됩니다)



pd.date_range('2020-01-01', periods = 8, freq = 'WOM-1FRI')

[Out]:

DatetimeIndex(['2020-01-03', '2020-02-07', '2020-03-06', '2020-04-03', '2020-05-01', '2020-06-05', '2020-07-03', '2020-08-07'], dtype='datetime64[ns]', freq='WOM-1FRI')





  (5-1) 분기별 마지막 날짜(Quarter End), 분기별 공휴일 아닌 마지막 날짜(Business Quarter End) 


아래의 예는 2020년 1월 1일 이후 날짜 중에서 2월 달을 분기 마지막인 달 기준으로 해서 매 분기별 마지막 날짜(freq = 'Q-FEB') 8개(periods=8)를 가져와서 DatetimeIndex를 생성한 예입니다. 


만약 3월 달(March) 혹은 12월 달(December)을 분기 말의 기준으로 해서 매 분기별 마지막 날짜(freq = 'Q-MAR')를 8개 성성하고 싶다면 아래의 두번째 예제를 참고하면 됩니다. 



# quarterly dates anchored on 'February' last calendar day of each month

pd.date_range('2020-01-01', periods = 8, freq = 'Q-FEB')

[Out]:
DatetimeIndex(['2020-02-29', '2020-05-31', '2020-08-31', '2020-11-30',
               '2021-02-28', '2021-05-31', '2021-08-31', '2021-11-30'],
              dtype='datetime64[ns]', freq='Q-FEB')

 

# quarterly dates anchored on 'March' last calendar day of each month

pd.date_range('2020-01-01', periods = 8, freq = 'Q-MAR')

[Out]:
DatetimeIndex(['2020-03-31', '2020-06-30', '2020-09-30', '2020-12-31',
               '2021-03-31', '2021-06-30', '2021-09-30', '2021-12-31'],
              dtype='datetime64[ns]', freq='Q-MAR')




공휴일이 아닌 Business day 기준의 분기별 마지막 날짜(Business Quarter End)를 2월 달을 분기 마지막인 달 기준으로 8개 생성하려면 아래의 예처럼 freq = 'BQ-FEB' 의 base time series frequency를 사용하면 됩니다. 



# quarterly dates anchored on last busines day of each month

pd.date_range('2020-01-01', periods = 8, freq = 'BQ-FEB')

[Out]:
DatetimeIndex(['2020-02-28', '2020-05-29', '2020-08-31', '2020-11-30',
               '2021-02-26', '2021-05-31', '2021-08-31', '2021-11-30'],
              dtype='datetime64[ns]', freq='BQ-FEB')

 




  (5-2) 분기별 시작 날짜(Quarter Begin), 분기별 공휴일 아닌 시작 날짜(Business Quarter Begin)


분기별 시작 날짜는 위의 (5-1) 번의 offset type alias 'Q'에 'S'를 붙여주어서 'QS', 'BQS' 를 사용합니다. 


아래의 예는 2020년 1월 1일 이후의 날짜 중에서 2월(February) 달을 분기 마지막 달 기준으로 해서 각 분기별 시작 날짜(freq = 'QS-FEB')를 8개 (periods=8) 생성한 것입니다. 



# quarterly dates anchored on first calendar day of each month

pd.date_range('2020-01-01', periods = 8, freq = 'QS-FEB')

[Out]:

DatetimeIndex(['2020-02-01', '2020-05-01', '2020-08-01', '2020-11-01', '2021-02-01', '2021-05-01', '2021-08-01', '2021-11-01'], dtype='datetime64[ns]', freq='QS-FEB')




아래의 예는 분기별 공휴일이 아닌, 즉 business day 기준으로 2월(February) 달을 분기 마지막 달 기준으로 해서 각 분기별 시작 날짜(freq = 'BQS-FEB')를 8개 DatetimeIndex로 생성한 것입니다. 



# quarterly dates anchored on first busines day of each month

pd.date_range('2020-01-01', periods = 8, freq = 'BQS-FEB')

[Out]:
DatetimeIndex(['2020-02-03', '2020-05-01', '2020-08-03', '2020-11-02',
               '2021-02-01', '2021-05-03', '2021-08-02', '2021-11-01'],
              dtype='datetime64[ns]', freq='BQS-FEB')

 




  (6) 년별 특정월 마지막 날짜(Year End), 년별 공휴일이 아닌 특정월 마지막 날짜(Business Year End)


아래의 예는 년도별로 2월(February) 마지막 날짜 (freq = 'A-FEB')를 8개 가져와서 DatetimeIndex를 만든 것입니다. (만약 매년 1월 마지막 날짜를 생성하고 싶으면 freq = 'A-JAN' 처럼 JANUARY의 앞 3개 알파벳을 입력해주면 됩니다)



# Year End

# Annual dates anchored on last calendar day of given month

pd.date_range('2020-01-01', periods = 8, freq = 'A-FEB')

[Out]:

DatetimeIndex(['2020-02-29', '2021-02-28', '2022-02-28', '2023-02-28', '2024-02-29', '2025-02-28', '2026-02-28', '2027-02-28'], dtype='datetime64[ns]', freq='A-FEB')




아래의 예는 매 년도별로 공휴일이 아닌(Business day) 2월(February) 마지막 날짜(freq = 'BA-FEB') 를 8개 (periods=8) 가져와서 DatetimeIndex를 만는 것입니다. 



# Business Year End

# Annual dates anchored on last business day of given month

pd.date_range('2020-01-01', periods = 8, freq = 'BA-FEB')

[Out]:
DatetimeIndex(['2020-02-28', '2021-02-26', '2022-02-28', '2023-02-28',
               '2024-02-29', '2025-02-28', '2026-02-27', '2027-02-26'],
              dtype='datetime64[ns]', freq='BA-FEB')





  (7) 년별 시작 날짜(Year Begin), 년별 공휴일이 아닌 시작 날짜(Business Year Begin)


아래의 예는 2020-01-01일 이후 날짜 중에서 매년 2월(February)의 시작 날짜 (freq = 'AS-FEb') 를 8개(periods=8) 가져와서 DatetimeIndex를 만든 것입니다. 위의 (6)번에 'A'에 'S'를 추가하였습니다. 



# Year Begin

pd.date_range('2020-01-01', periods = 8, freq = 'AS-FEB')

[Out]:
DatetimeIndex(['2020-02-01', '2021-02-01', '2022-02-01', '2023-02-01',
               '2024-02-01', '2025-02-01', '2026-02-01', '2027-02-01'],
              dtype='datetime64[ns]', freq='AS-FEB')




아래의 예는 2020-01-01일 이후이고 공휴일이 아닌(business day) 날짜 중에서 매년 2월(February)의 시작 날짜 (freq = 'BAS-FEB') 를 8개 (periods=8) 가져와서 DatetimeIndex를 만든 것입니다. 바로 위의 freq = 'AS-FEB'에서 'B'를 추가하여 freq = 'BAS-FEB'를 사용해서 만들었습니다. 



# Business Year Begin

pd.date_range('2020-01-01', periods = 8, freq = 'BAS-FEB')

[Out]:
DatetimeIndex(['2020-02-03', '2021-02-01', '2022-02-01', '2023-02-01',
               '2024-02-01', '2025-02-03', '2026-02-02', '2027-02-01'],
              dtype='datetime64[ns]', freq='BAS-FEB')




  (8) Offset 만큼 날짜 이동하기 (shifting dates with offsets)


위에서 Base time series frequencies와 offset types 에 대해서 알아보았습니다. 이 offset 객체를 가지고 다른 datetime 객체에 더하거나 뺄 수 있습니다. 



from datetime import datetime

from pandas.tseries.offsets import MonthEnd, MonthBegin


now = datetime.now()

now

[Out]: datetime.datetime(2019, 12, 21, 15, 30, 39, 654904)



now + MonthEnd()

[Out]: Timestamp('2019-12-31 15:30:39.654904')


now - MonthEnd()

[Out]: Timestamp('2019-11-30 15:30:39.654904')


now + MonthBegin()

[Out]: Timestamp('2020-01-01 15:30:39.654904')



혹은 offset 객체에  rollforward() 메소드를 사용해서 앞으로(미래로) 날짜를 굴리거나(이동시키거나), 아니면 rollback() 메소드를 사용해서 뒤로(과거로) 날짜를 굴릴(이동시킬) 수 있습니다. 재미있는 기능입니다. ^^



offset_me = MonthEnd()


offset_me.rollforward(now)

[Out]: Timestamp('2019-12-31 15:30:39.654904')



offset_me.rollback(now)

[Out]: Timestamp('2019-11-30 15:30:39.654904')

 




  (9) pandas.period_range() 로 날짜 기간(period of time) 만들기


(a) pd.data_range('2020-01-01', periods=10, freq='d')로 만든 DatetimeIndex를 index로 해서 pandas Series를 만들 수도 있으며, (b) pd.period_range('2020-01-01', '2020-01-10', freq='d')러 PeriodIndex 를 만들어서 이를 index로 해서 pandas Series를 만들어도 동일한 결과를 얻을 수 있습니다. 



import pandas as pd

import numpy as np


# (a) pandas.date_range('start_date', periods, freq)

dr = pd.date_range('2020-01-01', periods=10, freq='d')

dr

[Out]:
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
               '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
               '2020-01-09', '2020-01-10'],
              dtype='datetime64[ns]', freq='D')



pd.Series(range(10), index=dr)

[Out]:
2020-01-01    0
2020-01-02    1
2020-01-03    2
2020-01-04    3
2020-01-05    4
2020-01-06    5
2020-01-07    6
2020-01-08    7
2020-01-09    8
2020-01-10    9
Freq: D, dtype: int64





# (b) pandas.period_range('start_date', 'end_date', freq)

pr = pd.period_range('2020-01-01', '2020-01-10', freq='d')

pr

[Out]:
PeriodIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
             '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
             '2020-01-09', '2020-01-10'],
            dtype='period[D]', freq='D')



pd.Series(range(10), index=pr)

[Out]:
2020-01-01    0
2020-01-02    1
2020-01-03    2
2020-01-04    3
2020-01-05    4
2020-01-06    5
2020-01-07    6
2020-01-08    7
2020-01-09    8
2020-01-10    9
Freq: D, dtype: int64





많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 눌러주세요. :-)




728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 


(1) Yahoo Finace에서 'Apple' 회사의 2019년도 주가 데이터를 가져오기

(2) 주식 종가로 5일, 10일, 20일 단순이동평균(Simple Moving Average) 구하기

(3) 종가, 5일/10일/20일 이동평균을 seaborn을 이용해서 시각화하기


를 차례대로 해보겠습니다. 



  (1) Yahoo Finace에서 'Apple' 회사의 2019년도 주가 데이터를 가져오기


Yahoo Finance 사이트에서 쉽게 주가 데이터를 다운로드 받는 방법 중의 하나는 yfinance library를 설치해서 download() 함수를 이용하는 것입니다. Jupyter Notebook의 Cell에서 바로 !pip install yfinance 명령어로 라이브러리를 설치하고 import 해서 download() 함수로 Apple('AAPL')의 2019-01-01 ~ 2019-12-24' 일까지의 주가 데이터를 다운로드하였습니다. 



# Install yfinance package.

!pip install yfinance

 

# Import yfinance

import yfinance as yf  

 

# Get the data for the stock Apple by specifying the stock ticker, start date, and end date

aapl = yf.download('AAPL','2019-01-01','2019-12-25')


Requirement already satisfied: yfinance in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (0.1.52)
Requirement already satisfied: multitasking>=0.0.7 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from yfinance) (0.0.9)
Requirement already satisfied: numpy>=1.15 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from yfinance) (1.17.3)
Requirement already satisfied: pandas>=0.24 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from yfinance) (0.25.3)
Requirement already satisfied: requests>=2.20 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from yfinance) (2.22.0)
Requirement already satisfied: pytz>=2017.2 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from pandas>=0.24->yfinance) (2019.3)
Requirement already satisfied: python-dateutil>=2.6.1 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from pandas>=0.24->yfinance) (2.8.1)
Requirement already satisfied: certifi>=2017.4.17 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from requests>=2.20->yfinance) (2019.11.28)
Requirement already satisfied: idna<2.9,>=2.5 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from requests>=2.20->yfinance) (2.8)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from requests>=2.20->yfinance) (1.25.7)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from requests>=2.20->yfinance) (3.0.4)
Requirement already satisfied: six>=1.5 in /Users/ihongdon/anaconda3/envs/py3.6_tf2.0/lib/python3.6/site-packages (from python-dateutil>=2.6.1->pandas>=0.24->yfinance) (1.13.0)
[*********************100%***********************]  1 of 1 completed


aapl.head()

[Out]:

OpenHighLowCloseAdj CloseVolume
Date
2018-12-31158.529999159.360001156.479996157.740005155.40504535003500
2019-01-02154.889999158.850006154.229996157.919998155.58236737039700
2019-01-03143.979996145.720001142.000000142.190002140.08522091312200
2019-01-04144.529999148.550003143.800003148.259995146.06535358607100
2019-01-07148.699997148.830002145.899994147.929993145.74026554777800





  (2) 주식 종가(Close)로 5일, 10일, 20일 이동평균 구하기


Apple 회사의 주식 데이터 중에서 '종가(Close)'를 대상으로 이동평균을 구해보겠습니다. 



aapl.Close[:10]

[Out]:
Date
2018-12-31    157.740005
2019-01-02    157.919998
2019-01-03    142.190002
2019-01-04    148.259995
2019-01-07    147.929993
2019-01-08    150.750000
2019-01-09    153.309998
2019-01-10    153.800003
2019-01-11    152.289993
2019-01-14    150.000000
Name: Close, dtype: float64

 




이동평균은 시계열 데이터 내의 잡음(noise)을 제거하는 데이터 전처리, 혹은 계절성이 존재하는 시계열 데이터에서 계절성 부분을 빼고 장기 추세 요인(trend factor)나 중기 순환/주기 요인(cycle factor)를 보려고 할 때 많이 사용합니다. 시계열 데이터 예측(forecasting)에도 사용하구요. 


이동평균은 가중치를 고려 안하는 (즉, 모든 값의 가중치가 같다고 가정하는) 단순이동평균(Simple Moving Average, SMA)과, 가중치를 부여하는 가중이동평균(Weighted Moving Average, WMA)가 있는데요, 이번 포스팅에서는 단순이동평균(SMA)에 대해서 다룹니다. 


차수(order) m 인 단순이동평균(Simple Moving Average with Order m) 은 다시 중심이동평균(Centered Moving Average)추적이동평균(Trailing Moving Average)로 구분할 수 있습니다 (아래의 개념 비교 이미지를 참고하세요). 이번 포스팅에서는 python pandas에서 사용하고 있는 추적이동평균 개념으로 window 5일, 10일, 15일의 단순이동평균을 계산해 보았습니다. 






이동평균을 구하는 두 가지 방법으로, for loop 반복문과 numpy.mean() 을 이용하는 수작업 방법과, pandas 라이브러리의 rolling(window=m).mean() 함수를 이용하는 좀더 편리한 방법을 소개하겠습니다. 



(2-1) for loop 반복문과 numpy.mean() 을 이용한 5일 이동평균 구하기



import numpy as np


for i in range(0, 6):

    stock_close_5days = aapl.Close[i:(i+5)]

    sma_5d = np.mean(stock_close_5days)

    print('SMA(5 Days Window) of', aapl.Close.index[i+4].date(), ':', sma_5d)


[Out]:

SMA(5 Days Window) of 2019-01-07 : 150.80799865722656 SMA(5 Days Window) of 2019-01-08 : 149.40999755859374 SMA(5 Days Window) of 2019-01-09 : 148.48799743652344 SMA(5 Days Window) of 2019-01-10 : 150.80999755859375 SMA(5 Days Window) of 2019-01-11 : 151.61599731445312 SMA(5 Days Window) of 2019-01-14 : 152.02999877929688

 




(2-2) pandas 의 rolling(window=5).mean() 함수를 이용한 5일 이동평균 구하기


차수 m인 이동평균(trailing moving average)을 구하면 처음 시작부분에 m-1 개의 결측값이 발생합니다.



import pandas as pd


sma_5d = aapl.Close.rolling(window=5).mean()

sma_5d[:10]


[Out]:

Date 2018-12-31 NaN 2019-01-02 NaN 2019-01-03 NaN 2019-01-04 NaN 2019-01-07 150.807999 2019-01-08 149.409998 2019-01-09 148.487997 2019-01-10 150.809998 2019-01-11 151.615997 2019-01-14 152.029999 Name: Close, dtype: float64

 



이제 pandas에서 이동평균 구하는 rolling() 함수를 알았으니, 차수(order, window)가 5일, 10일, 20일인 단순 추적 이동평균(simple trailing moving average)을 구해보겠습니다. 



# simple trailing moving average with window 5 days/ 10 days/ 20 days

df_sma = pd.DataFrame({

    'close': aapl.Close

    , 'sma_5d': aapl.Close.rolling(window=5).mean()

    , 'sma_10d': aapl.Close.rolling(window=10).mean()

    , 'sma_20d': aapl.Close.rolling(window=20).mean()

})

 

# top first 25 rows

df_sma[:25]

[Out]:

closesma_5dsma_10dsma_20d
Date
2018-12-31157.740005NaNNaNNaN
2019-01-02157.919998NaNNaNNaN
2019-01-03142.190002NaNNaNNaN
2019-01-04148.259995NaNNaNNaN
2019-01-07147.929993150.807999NaNNaN
2019-01-08150.750000149.409998NaNNaN
2019-01-09153.309998148.487997NaNNaN
2019-01-10153.800003150.809998NaNNaN
2019-01-11152.289993151.615997NaNNaN
2019-01-14150.000000152.029999151.418999NaN
2019-01-15153.070007152.494000150.951999NaN
2019-01-16154.940002152.820001150.653999NaN
2019-01-17155.860001153.232001152.020999NaN
2019-01-18156.820007154.138004152.877000NaN
2019-01-22153.300003154.798004153.414001NaN
2019-01-23153.919998154.968002153.731001NaN
2019-01-24152.699997154.520001153.670001NaN
2019-01-25157.759995154.900000154.066000NaN
2019-01-28156.300003154.795999154.467001NaN
2019-01-29154.679993155.071997154.935001153.177000
2019-01-30165.250000157.337997156.153000153.552499
2019-01-31166.440002160.085999157.303000153.978500
2019-02-01166.520004161.838000158.369000155.195000
2019-02-04171.250000164.828000159.812000156.344500
2019-02-05174.179993168.728000161.899998157.657000





  (3) 종가, 5일/10일/15일 이동평균을 seaborn을 이용해서 시각화하기


trailing moving average 이동평균을 구하면 차수 m-1 만큼의 결측값(NaN) 이 생깁니다. 시각화를 위해서 결측값이 있는 행은 삭제하도록 하겠습니다. 



df_sma.dropna(axis=0, inplace=True)

df_sma.head(10)

[Out]:

closesma_5dsma_10dsma_20d
Date
2019-01-29154.679993155.071997154.935001153.177000
2019-01-30165.250000157.337997156.153000153.552499
2019-01-31166.440002160.085999157.303000153.978500
2019-02-01166.520004161.838000158.369000155.195000
2019-02-04171.250000164.828000159.812000156.344500
2019-02-05174.179993168.728000161.899998157.657000
2019-02-06174.240005170.526001163.931999158.831500
2019-02-07170.940002171.426001165.756000159.713000
2019-02-08170.410004172.204001167.021001160.543501
2019-02-11169.429993171.839999168.334000161.400500

 



Matplotlib 을 이용해서 '종가(Close)', '5일 이동평균', '10일 이동평균', '20일 이동평균' 선 그래프를 그려보겠습니다. 



# line plot with moving average of 5 window, 10 window, 20 window

import matplotlib.pyplot as plt

plt.figure(figsize=(15, 10))

plt.plot(df_sma.index, df_sma.close, 'y-', label='close_price')

plt.plot(df_sma.index, df_sma.sma_5d, 'b-', label='sma_5d')

plt.plot(df_sma.index, df_sma.sma_10d, 'r-', label='sma_10d')

plt.plot(df_sma.index, df_sma.sma_20d, 'g-', label='sma_20d')

plt.legend()

plt.show()





위의 1년치 시계열 그래프가 서로 겹쳐보여서 잘 구분이 안되네요. 그래서 2월달의 20개 관측치만 선택해서 다시 시계열 선 그래프를 그려보겠습니다. 


아래의 그래프에서 확인할 수 있는 바와 같이, 이동평균 값은 원래의 주식 종가(Close) 값보다 후행적으로 쫓아가고(trailing) 있습니다. 그리고 차수(order, rolling window)가 클 수록 후행적으로 쫒아가는 정도가 더 느림을 알 수 있습니다. 



plt.figure(figsize=(15, 10))

plt.plot(df_sma.index[:20], df_sma.close[:20], 'yo-', label='close_price')

plt.plot(df_sma.index[:20], df_sma.sma_5d[:20], 'bo-', label='sma_5d')

plt.plot(df_sma.index[:20], df_sma.sma_10d[:20], 'ro-', label='sma_10d')

plt.plot(df_sma.index[:20], df_sma.sma_20d[:20], 'go-', label='sma_20d')

plt.legend()

plt.show()



 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas의 Series, DataFrame에서 시계열 데이터 index 의 중복 확인 및 처리하는 방법(https://rfriend.tistory.com/500) 에 대해서 소개하였습니다. 


이번 포스팅에서는 Python pandas에서 일정한 주기의 시계열 데이터(Fixed frequency time series)를 가진 Series, DataFrame 만드는 방법을 소개하겠습니다. 



[ 시계열 데이터의 특징 ]

  • 동일한/ 고정된 간격의 날짜-시간 index (equally spaced time interval, fixed frequency)
  • 중복 없고, 빠진 것도 없는 날짜-시간 index (no redundant values or gaps)
  • 시간 순서대로 정렬 (sequential order)

(* 시계열 데이터가 반드시 동일한/고정된 간격의 날짜-시간을 가져야만 하는 것은 아님. 가령, 주가(stock price) 데이는 장이 열리는 business day에만 존재하며 공휴일은 데이터 없음)





  (1) 동일 간격의 시계열 데이터 Series 만들기 (fixed frequency time series pandas Series)



(1-1) 중간에 날짜가 비어있는 시계열 데이터 Series 만들기 (non-equally spaced time series)


먼저, 예제로 사용할 간단한 시계열 데이터 pandas Series 를 만들어보겠습니다. 의도적으로 '2019-12-04', '2019-12-08' 일의 날짜-시간 index 를 제거(drop)하여 이빨 빠진 날짜-시간 index 를 만들었습니다. 



import pandas as pd


# generate dates from 2019-12-01 to 2019-12-10

date_idx = pd.date_range('2019-12-01', periods=10)

date_idx

[Out]:

DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04', '2019-12-05', '2019-12-06', '2019-12-07', '2019-12-08', '2019-12-09', '2019-12-10'], dtype='datetime64[ns]', freq='D')


# drop 2 dates from DatetimeIndex

date_idx = date_idx.drop(pd.DatetimeIndex(['2019-12-04', '2019-12-08']))

date_idx

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-05',
               '2019-12-06', '2019-12-07', '2019-12-09', '2019-12-10'],
              dtype='datetime64[ns]', freq=None)

# Time Series with missing dates index

series_ts_missing = pd.Series(range(len(date_idx))

                              , index=date_idx)


series_ts_missing

[Out]:
2019-12-01    0
2019-12-02    1
2019-12-03    2
2019-12-05    3
2019-12-06    4
2019-12-07    5
2019-12-09    6
2019-12-10    7
dtype: int64





(1-2) 이빨 빠진 Time Series를 동일한 간격의 시계열 데이터 pandas Series로 변환하기 

       (fixed frequency, equally spaced time interval time series)


위의 (1-1)에서 만든 Series는 '2019-12-04', '2019-12-08'일의 날짜-시간 index가 빠져있는데요, 이럴 경우 resample('D')를 이용하여 날짜-시간 index는 등간격의 날짜-시간을 채워넣고, 대신 값은 결측값 처리(missing value, NaN, Not a Number)를 해보겠습니다. 



# Create a 1 day Fixed Frequency Time Series using resample('D')

series_ts_fixed_freq = series_ts_missing.resample('D')

series_ts_fixed_freq.first()

[Out]:
2019-12-01    0.0
2019-12-02    1.0
2019-12-03    2.0
2019-12-04    NaN <---
2019-12-05    3.0
2019-12-06    4.0
2019-12-07    5.0
2019-12-08    NaN <---
2019-12-09    6.0
2019-12-10    7.0
Freq: D, dtype: float64




비어있던 '날짜-시간' index 를 등간격 '날짜-시간' index로 채우면서 값(value)에 'NaN'이 생긴 부분을 fillna(0)을 이용하여 '0'으로 채워보겠습니다. 



# fill missing value with '0'

series_ts_fixed_freq.first().fillna(0)

[Out]:
2019-12-01    0.0
2019-12-02    1.0
2019-12-03    2.0
2019-12-04    0.0 <---
2019-12-05    3.0
2019-12-06    4.0
2019-12-07    5.0
2019-12-08    0.0 <---
2019-12-09    6.0
2019-12-10    7.0
Freq: D, dtype: float64

 




이번에는 resample('10T')를 이용하여 '10분 단위의 동일 간격 날짜-시간' index의 시계열 데이터를 만들어보겠습니다. 이때도 원래의 데이터셋에 없던 '날짜-시간' index의 경우 값(value)은 결측값으로 처리되어 'NaN'으로 채워집니다. 



# resampling with 10 minutes frequency (interval)

series_ts_missing.resample('10T').first()

[Out]:

2019-12-01 00:00:00 0.0 2019-12-01 00:10:00 NaN 2019-12-01 00:20:00 NaN 2019-12-01 00:30:00 NaN 2019-12-01 00:40:00 NaN ... 2019-12-09 23:20:00 NaN 2019-12-09 23:30:00 NaN 2019-12-09 23:40:00 NaN 2019-12-09 23:50:00 NaN 2019-12-10 00:00:00 7.0 Freq: 10T, Length: 1297, dtype: float64

 





  (2) 동일 간격의 시계열 데이터 DataFrame 만들기 

       (fixed frequency time series pandas DataFrame)



(2-1) 중간에 날짜가 비어있는 시계열 데이터 DataFrame 만들기 (non-equally spaced time series DataFrame)


pd.date_range() 함수로 등간격의 10일치 날짜-시간 index를 만든 후에, drop(pd.DatetimeIndex()) 로 '2019-12-04', '2019-12-08'일을 제거하여 '이빨 빠진 날짜-시간' index를 만들었습니다. 



import pandas as pd


# generate dates from 2019-12-01 to 2019-12-10

date_idx = pd.date_range('2019-12-01', periods=10)


# drop 2 dates from DatetimeIndex

date_idx = date_idx.drop(pd.DatetimeIndex(['2019-12-04', '2019-12-08']))

date_idx

[Out]:

DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-05',

'2019-12-06', '2019-12-07', '2019-12-09', '2019-12-10'], dtype='datetime64[ns]', freq=None)


df_ts_missing = pd.DataFrame(range(len(date_idx))

                             , columns=['col']

                             , index=date_idx)


df_ts_missing

[Out]:

col
2019-12-010
2019-12-021
2019-12-032
2019-12-053
2019-12-064
2019-12-075
2019-12-096
2019-12-107

 




(2-2) 이빨 빠진 Time Series를 동일한 간격의 시계열 데이터 pandas DataFrame으로 변환하기 

       (fixed frequency, equally spaced time interval time series pandas DataFrame)


resample('D') 를 메소드를 사용하여 '일(Day)' 동일 간격의 '날짜-시간' index를 가지는 시계열 데이터 DataFrame을 만들었습니다. 이때 원래의 데이터에 없던 '날짜-시간' index의 경우 결측값 처리되어 값(value)은 'NaN'으로 처리됩니다. 



df_ts_fixed_freq = df_ts_missing.resample('D').first()

df_ts_fixed_freq

[Out]:

col
2019-12-010.0
2019-12-021.0
2019-12-032.0
2019-12-04NaN <---
2019-12-053.0
2019-12-064.0
2019-12-075.0
2019-12-08NaN <---
2019-12-096.0
2019-12-107.0




동일 간견 시계열 데이터로 변환하는 과정에서 생긴 'NaN' 결측값 부분을 fillina(0) 메소드를 이용하여 '0'으로 대체하여 채워보겠습니다. 



# fill missing value with '0'

df_ts_fixed_freq = df_ts_fixed_freq.fillna(0)

df_ts_fixed_freq

col
2019-12-010.0
2019-12-021.0
2019-12-032.0
2019-12-040.0 <---
2019-12-053.0
2019-12-064.0
2019-12-075.0
2019-12-080.0 <---
2019-12-096.0
2019-12-107.0




많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

지난 포스팅에서는 '날짜-시간 index'를 가지는 pandas Series, DataFrame에서 '날짜-시간 index'를 기준으로 시계열 데이터를 indexing, slicing, selection 하는 방법(https://rfriend.tistory.com/499)을 소개하였습니다. 



이번 포스팅에서는 Python pandas 의 Series, DataFrame에서 


(1) 시계열 데이터 index 중복 여부를 확인 (check duplicated time series indices)

(2) 시계열 데이터 중복 index 시 첫번째 행만 가져오기 (keep the first row from duplicated time series indices)

(3) 시계열 데이터 index 별 group by 집계 (group by aggregation using time series indices)


하는 방법을 소개하겠습니다. 





예제로 사용할 간단할 시계열 데이터 pandas Series를 만들어보겠습니다. 


이번 포스팅의 주제가 '중복된 시계열 데이터 인덱스 (Duplicated DatatimeIndex)' 이므로 append() 메소드를 사용하여 '2019-12-01', '2019-12-02' 일의 인덱스를 추가함으로써 중복 인덱스를 만들어보았습니다. 


그리고, 시계열 데이터는 시간 순서대로 정렬된 상태로 저장된 데이터이므로 sort_values() 메소드로 내림차순 정렬(sort in ascending order)을 해주었습니다. 



import pandas as pd


# generate dates from 2019-12-01 to 2019-12-10

date_idx = pd.date_range('2019-12-01', periods=10)

date_idx

[Out]:

DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04', '2019-12-05', '2019-12-06', '2019-12-07', '2019-12-08', '2019-12-09', '2019-12-10'], dtype='datetime64[ns]', freq='D')


# append duplicated dates index

date_idx = date_idx.append(pd.DatetimeIndex(['2019-12-01', '2019-12-02']))

date_idx

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04',
               '2019-12-05', '2019-12-06', '2019-12-07', '2019-12-08',
               '2019-12-09', '2019-12-10', '2019-12-01', '2019-12-02'],
              dtype='datetime64[ns]', freq=None)



# Time Series with duplicated dates index

series_ts = pd.Series(range(len(date_idx))

                      , index=date_idx.sort_values())


series_ts

[Out]:
2019-12-01     0
2019-12-01     1
2019-12-02     2
2019-12-02     3
2019-12-03     4
2019-12-04     5
2019-12-05     6
2019-12-06     7
2019-12-07     8
2019-12-08     9
2019-12-09    10
2019-12-10    11
dtype: int64


 



  (1) 시계열 데이터 index 중복 여부를 확인 (check duplicated DatetimeIndex indices)


날짜-시간 index 의 중복 여부를 확인하는 방법에는 여러가지가 있는데요, 그중에서 가장 간단한 방법은 유일한 값 여부(is unique?)를 확인할 수 있는 is_unique 메소드를 사용하는 것입니다. '유일한 값'으로만 되어있으면 True, '중복 값'이 포함되어 있으면 False를 반환합니다. 



# duplication check

series_ts.index.is_unique

[Out]: False

 



index 중복 여부를 확인하는 다른 방법으로는 index에 대해 nunique() 메소드를 사용해서 유일한 값의 index 개수를 세어보고, len(series_ts)로 Series의 전체 행의 개수를 세어보아서 이 둘의 값이 같은지를 확인해보는 것입니다. 만약 중복 index가 있다면 이 둘의 개수가 다르겠지요. 



# duplication check

series_ts.index.nunique() == len(series_ts)

[Out]: False

 




행 단위까지 내려가서 확인을 해보고 싶으면 groupby(level=0) 으로 첫번째인 날짜-시간 index 기준으로 Group By 개수 집계를 해봐서 날짜-시간 index별 행 개수가 '1' 초과인 행을 살펴보면 됩니다. 


< 날짜-시간 index별 행의 개수 집계 >


# count group by time series index

series_ts.groupby(level=0).count() # or size()

[Out]:
2019-12-01    2
2019-12-02    2
2019-12-03    1
2019-12-04    1
2019-12-05    1
2019-12-06    1
2019-12-07    1
2019-12-08    1
2019-12-09    1
2019-12-10    1
dtype: int64


< 날짜-시간 index별 행의 개수가 1 보다 큰 경우, 즉 중복 index 인 모든 행 선택 >


# selecting duplicated index rows

series_ts[series_ts.groupby(level=0).count() > 1]

[Out]:

2019-12-01 0 2019-12-01 1 2019-12-02 2 2019-12-02 3 dtype: int64

 





  (2) 시계열 데이터 중복 index 시 첫번째 행만 가져오기 

       (keep the first row from duplicated time series indices)


날짜-시간 index 중복인 경우 첫번째 행을 가져오려면 groupby(level=0).first(), 마지막 행을 가져오려면 groupby(level=0).last() 메소드를 사용합니다. 



# selecting FIRST row in case duplicated index

series_ts.groupby(level=0).first()

[Out]:
2019-12-01     0
2019-12-02     2
2019-12-03     4
2019-12-04     5
2019-12-05     6
2019-12-06     7
2019-12-07     8
2019-12-08     9
2019-12-09    10
2019-12-10    11
dtype: int64

 


# selecting LAST row in case duplicated index

series_ts.groupby(level=0).last()

[Out]:
2019-12-01     1
2019-12-02     3
2019-12-03     4
2019-12-04     5
2019-12-05     6
2019-12-06     7
2019-12-07     8
2019-12-08     9
2019-12-09    10
2019-12-10    11
dtype: int64






  (3) 시계열 데이터 index 별 group by 집계 

      (group by aggregation using time series indices)


groupby(level=0) 으로 날짜-시간 index 기준으로 GroupBy operation을 수행할 수 있으므로, groupby().agg() 로 집계하고자 하는 함수를 agg() 괄호 안에 넣어서 집계할 수 있습니다. 아래 예제에서는 날짜-시간 index별로 행의 개수(size), 합계(sum), 평균(mean), 최소값(min), 최대값(max) 를 구해보았습니다. (중복 index 시 groupby.agg() 함수 적용하여 집계/요약함)


참고로, groupby().agg() 로 여러개의 집계함수를 적용한 경우 DataFrame을 반환합니다. (vs. 한개의 집계함수만 groupby(level=0).size() 처럼 사용한 경우 Series 반환)



# aggregating size, sum, mean, min, max by group by time series index

series_ts.groupby(level=0).agg(['size', 'sum', 'mean', 'min', 'max'])

[Out]:

sizesummeanminmax
2019-12-01210.501
2019-12-02252.523
2019-12-03144.044
2019-12-04155.055
2019-12-05166.066
2019-12-06177.077
2019-12-07188.088
2019-12-08199.099
2019-12-0911010.01010
2019-12-1011111.01111

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 날짜-시간 시계열 객체(date-time, Timeseries objects)를 문자열(Strings)로 변환하기, 거꾸로 문자열을 날짜-시간 시계열 객체로 변환하는 방법(https://rfriend.tistory.com/498)을 소개하였습니다. 


이번 포스팅에서는 날짜-시간 시계열 데이터(date-time time series) index로 가지는  Python pandas의 Series, DataFrame 에서 특정 날짜-시간을 indexing, slicing, selection, truncation 하는 방법을 소개하겠습니다. 


(1) pandas Series에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법

(2) pandas DataFrame에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법





  (1) pandas Series에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법


먼저, 간단한 예제로 사용하도록 2019년 11월 25일 부터 ~ 2019년 12월 4일까지 10일 기간의 년-월-일 날짜를 index로 가지는 pands Series를 만들어보겠습니다. 


pandas.date_range(시작날짜, periods=생성할 날짜-시간 개수)  함수를 사용하여 날짜-시간 데이터를 생성하였으며, 이를 index로 하여 pandas Series를 만들었습니다. 



import pandas as pd

from datetime import datetime


# DatetimeIndex

ts_days_idx = pd.date_range('2019-11-25', periods=10)

ts_days_idx

[Out]:

DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28', '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04'], dtype='datetime64[ns]', freq='D')



# Series with time series index

series_ts = pd.Series(range(len(ts_days_idx))

                      , index=ts_days_idx)


series_ts

[Out]:
2019-11-25    0
2019-11-26    1
2019-11-27    2
2019-11-28    3
2019-11-29    4
2019-11-30    5
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64



series_ts.index

[Out]: 
DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28',
               '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02',
               '2019-12-03', '2019-12-04'],
              dtype='datetime64[ns]', freq='D')


series_ts.index[6]

[Out]: Timestamp('2019-12-01 00:00:00', freq='D')




참고로, 아례의 예처럼 pd.date_range(start='시작 날짜-시간', end='끝 날짜-시간') 처럼 명시적으로 시작과 끝의 날짜-시간을 지정해주어도 위의 perieds를 사용한 예와 동일한 결과를 얻을 수 있습니다. 



import pandas as pd


pd.date_range(start='2019-11-25', end='2019-12-04')

[Out]:
DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28',
               '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02',
               '2019-12-03', '2019-12-04'],
              dtype='datetime64[ns]', freq='D')




참고로 하나더 소개하자면요, pandas.date_range('시작날짜-시간', period=생성할 날짜-시간 개수, freq='주기 단위') 에서 freq 옵션을 통해서 'S' 1초 단위, '10S' 10초 단위, 'H' 1시간 단위, 'D' 1일 단위, 'M' 1달 단위(월 말일 기준), 'Y' 1년 단위 (년 말일 기준) 등으로 날짜-시간 시계열 데이터 생성 주기를 설정할 수 있습니다. 매우 편하지요?!


< 1초 단위로 날짜-시간 데이터 10개를 생성한 예 >


# 10 timeseries data points by Second(freq='S')

pd.date_range('2019-11-25 00:00:00', periods=10, freq='S')

[Out]: 
DatetimeIndex(['2019-11-25 00:00:00', '2019-11-25 00:00:01',
               '2019-11-25 00:00:02', '2019-11-25 00:00:03',
               '2019-11-25 00:00:04', '2019-11-25 00:00:05',
               '2019-11-25 00:00:06', '2019-11-25 00:00:07',
               '2019-11-25 00:00:08', '2019-11-25 00:00:09'],
              dtype='datetime64[ns]', freq='S')



< 10초 단위로 날짜-시간 데이터 10개를 생성한 예 >


# 10 timeseries data points by 10 Seconds (freq='10S')

pd.date_range('2019-11-25 00:00:00', periods=10, freq='10S')

[Out]:

DatetimeIndex(['2019-11-25 00:00:00', '2019-11-25 00:00:10', '2019-11-25 00:00:20', '2019-11-25 00:00:30', '2019-11-25 00:00:40', '2019-11-25 00:00:50', '2019-11-25 00:01:00', '2019-11-25 00:01:10', '2019-11-25 00:01:20', '2019-11-25 00:01:30'], dtype='datetime64[ns]', freq='10S')

 



(1-1) 시계열데이터를 index로 가지는 pandas Series에서 특정 날짜-시간 데이터 indexing 하기


먼저 위에서 생성한 series_ts 라는 이름의 시간 순서대로 정렬되어 있는 Series 에서 7번째에 위치한 '2019-12-01' 의 값 '6'을 indexing 해보겠습니다. 


(a), (b)와 같이 위치(position)를 가지고 인덱싱할 수 있습니다. 

또한, (c), (d)와 같이 날짜-시간 문자열(String)을 가지고도 인덱싱(indexing)을 할 수 있습니다. 

(e) 처럼 datetime.datetime(year, month, day) 객체를 사용해서도 인덱싱할 수 있습니다. 



import pandas as pd

from datetime import datetime


# (a) indexing with index number

series_ts[6]

[Out]: 6


# (b) indexing with index number using iloc

series_ts.iloc[6]

[Out]: 6


# (c) indexing with string ['year-month-day']

series_ts['2019-12-01']

[Out]: 6


# (d) indexing with string ['month/day/year']

series_ts['12/01/2019']

[Out]: 6


# (f) indexing with datetime.datetime(year, month, day)

series_ts[datetime(2019, 12, 1)]

[Out]: 6





(1-2) 시계열데이터를 index로 가지는 pandas Series에서 날짜-시간 데이터 Slicing 하기


아래는 '2019-12-01' 일 이후의 값을 모두 slicing 해오는 5가지 방법입니다. 

(a), (b)는 위치(position):위치(position)을 이용하여 날짜를 index로 가지는 Series를 slicing을 하였습니다. 

(c), (d)는 '년-월-일':'년-월-일' 혹은 '월/일/년':'월/일/년' 문자열(string)을 이용하여 slicing을 하였습니다. 

(e)는 datetime.datetime(년, 월, 일):datetime.datetime(년, 월, 일) 을 이용하여 slicing을 하였습니다. 



import pandas as pd

from datetime import datetime


# (a) slicing with position

series_ts[6:]

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64


# (b) slicing with position using iloc

series_ts.iloc[6:]

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

# (c) slicing with string

series_ts['2019-12-01':'2019-12-10']

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

# (d) slicing with string

series_ts['12/01/2019':'12/10/2019']

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

# (e) slicing with datetime

series_ts[datetime(2019, 12, 1):datetime(2019, 12, 10)]

[Out]:

2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64




(1-3) 시계열데이터를 index로 가지는 pandas Series 에서 날짜-시간 데이터 Selection 하기


'날짜-시간' 문자열(String)을 이용하여 특정 '년', '월'의 모든 데이터를 선택할 수도 있습니다. 꽤 편리하고 재미있는 기능입니다. 


< '2019'년 모든 데이터 선택하기 예 >


# selection with year string

series_ts['2019']

[Out]:

2019-11-25 0 2019-11-26 1 2019-11-27 2 2019-11-28 3 2019-11-29 4 2019-11-30 5 2019-12-01 6 2019-12-02 7 2019-12-03 8 2019-12-04 9 Freq: D, dtype: int64

 



< '2019년 12월' 모든 데이터 선택하기 예 >


# selection with year-month string

series_ts['2019-12']

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

 




(1-4) 시계열 데이터를 index로 가지는 pandas Series에서 날짜-시간 데이터 잘라내기 (Truncate)


truncate() methods를 사용하면 잘라내기(truncation)를 할 수 있습니다. before, after 옵션으로 잘라내기하는 범위 기간을 설정할 수 있는데요, 해당 날짜 포함 여부를 유심히 살펴보기 바랍니다. 


< '2019년 12월 1일' 이전(before) 모든 데이터 잘라내기 예 >

('2019년 11월 30일'까지의 모든 데이터 삭제하며, '2019-12-01'일 데이터는 남아 있음)


# truncate before

series_ts.truncate(before='2019-12-01')

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

 



< '2019년 11월 30일' 이후(after) 모든 데이터 잘라내기 예 >

(''2019년 12월 1일' 부터의 모든 데이터 삭제하며, '2019-11-30'일 데이터는 남아 있음)


# truncate after

series_ts.truncate(after='2019-11-30')

[Out]:
2019-11-25    0
2019-11-26    1
2019-11-27    2
2019-11-28    3
2019-11-29    4
2019-11-30    5
Freq: D, dtype: int64

 





  (2) pandas DataFrame에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법


위의 (1)번에서 소개했던 pandas Series의 시계열 데이터 indexing, slicing, selection, truncation 방법을 동일하게 pandas DataFrame에도 사용할 수 있습니다.  


년-월-일 날짜를 index로 가지는 간단한 pandas DataFrame 예제를 만들어보겠습니다. 



import pandas as pd

from datetime import datetime


# DatetimeIndex

ts_days_idx = pd.date_range('2019-11-25', periods=10)

ts_days_idx

[Out]:
DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28',
               '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02',
               '2019-12-03', '2019-12-04'],
              dtype='datetime64[ns]', freq='D')


# DataFrame with DatetimeIndex

df_ts = pd.DataFrame(range(len(ts_days_idx))

                     , columns=['col']

                     , index=ts_days_idx)


df_ts

col
2019-11-250
2019-11-261
2019-11-272
2019-11-283
2019-11-294
2019-11-305
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 




(2-1) 시계열데이터를 index로 가지는 pandas DataFrame에서 특정 날짜-시간 데이터 indexing 하기


위의 (1-1) Series indexing과 거의 유사한데요, DataFrame에서는 df_ts[6], df_ts[datetime(2019, 12, 1)] 의 두가지 방법은 KeyError 가 발생해서 사용할 수 없구요, 아래의 3가지 방법만 indexing에 사용 가능합니다. 


(a) iloc[integer] 메소드를 사용하여 위치(position) 로 indexing 하기

(b), (c) loc['label'] 메소드를 사용하여 이름('label')로 indexing 하기



# (a) indexing with index position integer using iloc[]

df_ts.iloc[6]

[Out]:
col    6
Name: 2019-12-01 00:00:00, dtype: int64


# (b) indexing with index labels ['year-month-day']

df_ts.loc['2019-12-01']

[Out]:
col    6
Name: 2019-12-01 00:00:00, dtype: int64


# (c) indexing with index labels ['month/day/year']

df_ts.loc['12/01/2019']

[Out]:
col    6
Name: 2019-12-01 00:00:00, dtype: int64





(2-2) 시계열데이터를 index로 가지는 pandas DataFrame에서 날짜-시간 데이터 Slicing 하기


아래는 '2019-12-01' 일 이후의 값을 모두 slicing 해오는 4가지 방법입니다. 

(a) 위치(position):위치(position)을 이용하여 날짜를 index로 가지는 Series를 slicing을 하였습니다. 

(b), (c)는 loc['년-월-일']:loc['년-월-일'] 혹은 loc['월/일/년']:loc['월/일/년'] 문자열(string)을 이용하여 slicing을 하였습니다. 

(d) 는 loc[datetime.datetime(year, month, day):datetime.datetime(year, month, day)] 로 slicing을 한 예입니다. 



# (a) slicing DataFrame with position integer

df_ts[6:10]

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 


# (b) silcing using date strings 'year-month-day'

df_ts.loc['2019-12-01':'2019-12-10']

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049


# (c) slicing using date strings 'month/day/year'

df_ts.loc['12/01/2019':'12/10/2019']

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049


# (d) slicing using datetime objects

from datetime import datetime

df_ts.loc[datetime(2019, 12, 1):datetime(2019, 12, 10)]

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049






(2-3) 시계열데이터를 index로 가지는 pandas DataFrame 에서 날짜-시간 데이터 Selection 하기


'년', '년-월' 날짜 문자열을 df.loc['year'], df.loc['year-month'] 에 입력하면 해당 년(year), 월(month)의 모든 데이터를 선택할 수 있습니다. 


< '2019년'의 모든 데이터 선택 예 >


# selection of year '2019'

df_ts.loc['2019'] # df_ts['2019']

col
2019-11-250
2019-11-261
2019-11-272
2019-11-283
2019-11-294
2019-11-305
2019-12-016
2019-12-027
2019-12-038
2019-12-049




< '2019년 12월'의 모든 데이터 선택 예 >


# selection of year-month '2019-12'

df_ts.loc['2019-12']

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 




(2-4) 시계열 데이터를 index로 가지는 pandas DataFrame에서 날짜-시간 데이터 잘라내기 (Truncate)


truncate() 메소드를 사용하면 before 이전 기간의 데이터를 잘라내거나 after 이후 기간의 데이터를 잘라낼 수 있습니다. 


< '2019-12-01' 일 이전(before) 기간 데이터 잘라내기 예 >

('2019-12-01'일은 삭제되지 않고 남아 있음)


# truncate before

df_ts.truncate(before='2019-12-01') # '2019-12-01' is not removed

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 



< '2019-11-30'일 이후(after) 기간 데이터 잘라내기 예 >

('2019-11-30'일은 삭제되지 않고 남아 있음)


# truncate after

df_ts.truncate(after='2019-11-30') # '2019-11-30' is not removed

col
2019-11-250
2019-11-261
2019-11-272
2019-11-283
2019-11-294
2019-11-305

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python standard datetimepandas Timestamp 객체로 날짜-시간 데이터를 입력, 변환, 조회하는 방법을 소개하였습니다. 


이번 포스팅에서는 


(1) Python datetime, pandas Timestamp 객체를 문자열(string)로 변환

    (Converting native Python datetime, pandas Timestamp objects to Strings)


(2) 문자열(string)을 Python datetime, pandas Timestamp 객체로 변환

    (Converting Strings to Python datetime, pandas Timestamp)


에 대해서 차례대로 알아보겠습니다. 





  (1) Python datetime, pandas Timestamp 객체를 문자열(string)로 변환

      (Converting native Python datetime, pandas Timestamp objects to Strings)



(1-1) str() 함수를 이용하여 Python datetime 객체를 문자열(string)로 변환하기



# Create Python datetime objects

import datetime as dt

ts = dt.datetime(2019, 12, 22, 13, 30, 59) # (year, month, day, hour, minume, second)

type(ts)

[Out]: datetime.datetime

 

ts

[Out]: datetime.datetime(2019, 12, 22, 13, 30, 59)



# converting Python datetime objects to Strings

ts_str = str(ts)

type(ts_str)

[Out]: str


ts_str

[Out]: '2019-12-22 13:30:59'


# indexing from a string

print('year-month-day:', ts_str[:10])

[Out]: year-month-day: 2019-12-22

print('hour:minute:second:', ts_str[10:])
[Out]: hour:minute:second:  13:30:59





(1-2) strftime() 메소드를 이용하여 Python datetime 객체를 문자열(string)로 변환하기


Python standard datetime 객체의 포맷에 맞추어서 strftime() 메소드의 괄호 안에 형태 지정(format specification)을 해줍니다. 가령 4자리 년(4-digit year), 월(month), 일(day), 0-23시간(0-23 hour), 분, 초의 형태로 지정을 하고 싶으면 strftime('%Y-%m-%d %H:%M:%S') 로 설정을 해주면 됩니다. 



import datetime as dt


ts = dt.datetime(2019, 12, 22, 13, 30, 59) # (year, month, day, hour, minume, second)

ts

[Out]: datetime.datetime(2019, 12, 22, 13, 30, 59)


ts_strftime = ts.strftime('%Y-%m-%d %H:%M:%S')

type(ts_strftime)

[Out]: str


ts_strftime

[Out]: '2019-12-22 13:30:59'




년도를 '2자리 연도(2-digit year)'로 문자열 변환하고자 하면 '%y' 를 사용하며, 0-11시간(0-11 hour) 형태로 시간을 문자열 변환하고자 하면 '%I'를 사용합니다. 



# %Y: 4-digit year vs. %y: 2-digit year

# %H: 24-hour clock[00, 23] vs. %I: 12-hour clock [01, 11]

ts.strftime('%y-%m-%d %I:%M:%S')

[Out]: '19-12-22 01:30:59'

 



datetime.datetime(2019, 12, 22).strftime('%w') 는 주 단위의 요일을 일요일은 '0', 월요일은 '1', ..., 토요일은 '6'으로 순서대로 정수의 문자로 반환합니다. 



# '%w': weekday

print('----- %w: weekday as integer -----')

print('Sunday    :', dt.datetime(2019, 12, 22).strftime('%w'))

print('Monday    :', dt.datetime(2019, 12, 23).strftime('%w'))

print('Tuesday   :', dt.datetime(2019, 12, 24).strftime('%w'))

print('Wednesday :', dt.datetime(2019, 12, 25).strftime('%w'))

print('Thursday  :', dt.datetime(2019, 12, 26).strftime('%w'))

print('Friday    :', dt.datetime(2019, 12, 27).strftime('%w'))

print('Saturday  :', dt.datetime(2019, 12, 28).strftime('%w'))


[Out]: 
----- %w: weekday as integer -----
Sunday    : 0
Monday    : 1
Tuesday   : 2
Wednesday : 3
Thursday  : 4
Friday    : 5
Saturday  : 6



datetime.datetime(2019, 12, 22).strftime('%U')는 년 중 해당 주의 숫자(week nuber of the year)를 반환합니다. 이때 '%U'는 일요일이 주의 첫번째 일로 간주하는 반면에 '%W'는 월요일을 주의 첫번째 일로 간주하는 차이가 있습니다. 



# '%U': week number of the year [00, 53]. 

# Sunday is considered the first day of the week

dt.datetime(2019, 12, 22).strftime('%U') # '2019-12-22' is Sunday

[Out]: '51'

 




# '%W': week number of the year [00, 53]. 

# Monday is considered the first day of the week

dt.datetime(2019, 12, 22).strftime('%W') # '2019-12-22' is Sunday

[Out]: '50'

 

dt.datetime(2019, 12, 23).strftime('%W') # '2019-12-23' is Monday

[Out]: '51'





(1-3) pandas Timestamp를  strftime() 메소드를 사용하여 문자열(string)로 변환하기



import pandas as pd


pd_ts = pd.Timestamp(2019, 12, 22, 13, 30, 59)

pd_ts

[Out]: Timestamp('2019-12-22 13:30:59')


# convert pandas Timestamp to string

pd_ts.strftime('%y-%m-%d %I:%M:%S')

[Out]: '19-12-22 01:30:59'

 





  (2) 문자열(string)을 Python datetime, pandas Timestamp 객체로 변환

       (Converting Strings to Python datetime, pandas Timestamp)



(2-1) datetime.strptime() 함수를 이용하여 문자열을 Python datetime.datetime 객체로 변환하기


strptime(문자열, 날짜-시간 포맷) 의 괄호 안에는 문자열의 형태에 맞추어서 반환하고자 하는 날짜-시간 포맷을 설정해줍니다. 



# create a string with 'year-month-day hour:minute:second'

ts_str = '2019-12-22 13:30:59'

ts_str

[Out]: '2019-12-22 13:30:59'


# convert a string to datetime object

import datetime as dt

dt.datetime.strptime(ts_str, '%Y-%m-%d %H:%M:%S')

[Out]: datetime.datetime(2019, 12, 22, 13, 30, 59)

 



여러개의 날짜-시간 문자열로 구성된 리스트를 List Comprehension 을 이용해서 datetime 객체 리스트로 만들 수 있습니다. 



# convert strings list using list comprehension

ts_str_list = ['2019-12-22', '2019-12-23', '2019-12-24', '2019-12-25']


[dt.datetime.strptime(date, '%Y-%m-%d') for date in ts_str_list]

[Out]: 
[datetime.datetime(2019, 12, 22, 0, 0),
 datetime.datetime(2019, 12, 23, 0, 0),
 datetime.datetime(2019, 12, 24, 0, 0),
 datetime.datetime(2019, 12, 25, 0, 0)]




아래의 예는 pandas DataFrame에서 "Date" 문자열 칼럼과 "Time" 문자열 칼럼이 분리되어 있는 경우, (1) 먼저 이 두 칼럼을 "DateTime" 이라는 하나의 칼럼으로 합치고, (2) 그 다음에 문자열(Strings)을 DateTime 객체로 변환하는 방법에 대한 소개입니다. 


apply() 함수 안에 strptime() 함수를 lambda 무기명 함수 형태로 사용하였으며, 날짜와 시간 포맷도 위와는 조금 다르게 설정해보았습니다. ('%d/%m/%Y %H.%M.%S'))



import pandas as pd


# make a sample DataFrame with Date and Time (string format)

Date = ['01/12/2019', '01/12/2019', '01/12/2019']

Time = ['09.01.00', '09.01.01', '09.01.02']

val = [1, 2, 3]


df = pd.DataFrame({'Date': Date,

                   'Time': Time,

                   'val': val})


df

[Out]:

DateTimeval
001/12/201909.01.001
101/12/201909.01.012
201/12/201909.01.023

# combine 'Date' and 'Time' column as 'DateTime'

df['DateTime'] = df.Date + ' ' + df.Time

df

[Out]:

DateTimevalDateTime
001/12/201909.01.00101/12/2019 09.01.00
101/12/201909.01.01201/12/2019 09.01.01
201/12/201909.01.02301/12/2019 09.01.02


# check date type : strings

type(df.DateTime[0])

[Out]: str


# convert 'DateTime' column from strings to datetime object 

# using datetime.strptime() and lambda, apply function

from datetime import datetime


df.DateTime = df.DateTime.apply(lambda x: datetime.strptime(x, '%d/%m/%Y %H.%M.%S'))

df.set_index('DateTime', inplace=True)

df

[Out]:

DateTimeval
DateTime
2019-12-01 09:01:0001/12/201909.01.001
2019-12-01 09:01:0101/12/201909.01.012
2019-12-01 09:01:0201/12/201909.01.023


# check data type : datetime object Timestamp

type(df.index[0])

[Out]: pandas._libs.tslibs.timestamps.Timestamp




datetime.strptime(문자열, 날짜-시간 포맷) 의 경우 날짜-시간 문자열의 형태가 제한적이어서 아래와 같은 순서/형태의 문자열일 경우 ValueError 가 발생합니다. 이럴 경우 (2-2) dateutil.parser 의 parse() 함수를 사용하여 유연하게 날짜-시간 문자열을 파싱할 수 있습니다. 



dt.datetime.strptime('Dec 22, 2019 13:30:59', '%m %d, %Y %H:%M:%S')

[Out]: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-42-7dfd356853ba> in <module>
----> 1 dt.datetime.strptime('Dec 22, 2019 13:30:59', '%m %d, %Y %H:%M:%S')

~/anaconda3/envs/py3.6_tf2.0/lib/python3.6/_strptime.py in _strptime_datetime(cls, data_string, format)
    563     """Return a class cls instance based on the input string and the
    564     format string."""
--> 565     tt, fraction = _strptime(data_string, format)
    566     tzname, gmtoff = tt[-2:]
    567     args = tt[:6] + (fraction,)

~/anaconda3/envs/py3.6_tf2.0/lib/python3.6/_strptime.py in _strptime(data_string, format)
    360     if not found:
    361         raise ValueError("time data %r does not match format %r" %
--> 362                          (data_string, format))
    363     if len(data_string) != found.end():
    364         raise ValueError("unconverted data remains: %s" %

ValueError: time data 'Dec 22, 2019 13:30:59' does not match format '%m %d, %Y %H:%M:%S'




(2-2) dateutil 라이브러리의 parser.parse() 함수를 이용하여 문자열을 datetime 객체로 변환하기


아래의 3개의 예에서 보는 바와 같이, dateutil 라이브러리의 parser.parse() 함수는 괄호 안에 다양한 형태의 날짜-시간 문자열을 유연하게 인식하여 datetime.datetime 객체로 반환해줍니다. (2-1)의 datetime.strptime() 함수와 비교했을 때 dateutil.parser의 parse() 함수는 상대적으로 편리하고 강력합니다. 



# Converting Strings to datetime.datetime objects using parser.parse()

from dateutil.parser import parse


parse('2019-12-22 13:30:59')

[Out]: datetime.datetime(2019, 12, 22, 13, 30, 59)


parse('Dec 22, 2019 13:30:59')

[Out]: datetime.datetime(2019, 12, 22, 13, 30, 59)


parse('Dec 22, 2019 01:30:59 PM')

[Out]: datetime.datetime(2019, 12, 22, 13, 30, 59)




parse() 함수의 괄호 안 문자열의 첫번째 숫자가 '월(month)' 인지 아니면 '일(day)' 인지를 dayfirst=False (default), dayfirst=True 옵션을 사용하여 명시적으로 지정해줄 수 있습니다. 



# month first

parse('01/12/2019 13:30:59')

[Out]: datetime.datetime(2019, 1, 12, 13, 30, 59)


# day first

parse('01/12/2019 13:30:59', dayfirst=True)

[Out]: datetime.datetime(2019, 12, 1, 13, 30, 59)




여러개의 날짜-시간 문자열로 구성된 리스트를 가지고 List Comprehension을 사용하여 datetime.datetime 객체 리스트를 생성할 수 있습니다. 



# convert strings list using list comprehension

ts_str_list = ['2019-12-22', '2019-12-23', '2019-12-24', '2019-12-25']

[parse(date) for date in ts_str_list]

[Out]: 
[datetime.datetime(2019, 12, 22, 0, 0),
 datetime.datetime(2019, 12, 23, 0, 0),
 datetime.datetime(2019, 12, 24, 0, 0),
 datetime.datetime(2019, 12, 25, 0, 0)]

 




(3-3) pandas 의 pd.to_datetime()으로 날짜-시간 문자열을 pandas Timestamp 로 변환하기


* pandas Timestamp 에 대한 자세한 설명은 https://rfriend.tistory.com/497 참조하세요



# pandas Timestamp

import pandas as pd


pd.to_datetime('2019-12-22 13:30:59')

[Out]: Timestamp('2019-12-22 13:30:59')




여러개의 날짜-시간 문자열로 구성된 문자열 리스트를 가지고 pandas.to_datetime() 함수를 사용하여 pandas DatetimeIndex 를 생성할 수 있습니다. 이렇게 생성된 DatatimeIndex는 시계열 데이터로 이루어진 pandas Series나 pandas DataFrame 를 생성할 때 index로 사용할 수 있습니다. 



# pandas DatetimeIndex

ts_str_list = ['2019-12-22', '2019-12-23', '2019-12-24', '2019-12-25']

pd.to_datetime(ts_str_list)

DatetimeIndex(['2019-12-22', '2019-12-23', '2019-12-24', '2019-12-25'], dtype='datetime64[ns]', freq=None)



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

지난 포스팅에서는 native Python datetime 모듈의 4가지 데이터 유형으로 date, time, datetime, timedelta 에 대해서 소개(https://rfriend.tistory.com/496)하였습니다. 


이번 포스팅에서는 Python datetime과 거의 유사한 기능과 활용법을 가지pandas의 Timestamp 클래스로 날짜-시간 데이터 입력, 변환, 정보추출하는 방법에 대해서 소개하겠습니다.  매우 기본적인 내용들이고 쉽습니다. 




  (1) pandas Timestamp 생성하기 : pd.Timestamp()


pandas.Timestamp() 클래스를 사용해서 (a) 위치(position), (b) 키워드(keyword) 의 두 가지 방식으로 pandas Timestamp 객체를 생성할 수 있습니다. 


(a) 위치(position)의 경우 순서대로 (year, month, day, hour, minute, second) 를 의미합니다. 

(b) 키워드(keyword) 방식의 경우 year, month, day, hour, minute, second 각 키워드별로 순서에 무관하게 값을 입력해주면 됩니다. 


단, position과 keyword 방식을 하나의 Timestamp 객체에 병행해서 사용할 수는 없습니다. 



import pandas as pd


# (a) position: (year, month, day, hour, minute, second)

pd_ts = pd.Timestamp(2019, 12, 22, 13, 30, 59)

pd_ts

[OUT] Timestamp('2019-12-22 13:30:59')

 

# (b) keyword

pd.Timestamp(year=2019, month=12, day=22, hour=13, minute=30, second=59)

[OUT] Timestamp('2019-12-22 13:30:59')


# keyword : with different position

pd.Timestamp(day=22, month=12, year=2019, hour=13, minute=30, second=59)

[OUT] Timestamp('2019-12-22 13:30:59')





  (2) pandas Timestamp Attributes 로 날짜, 시간 정보 확인하기



# pandas Timestamp's Attributes

stamp = pd.Timestamp(2019, 12, 22, 13, 30, 59)


print('---------------------------')

print('pandas Timestamp Attributes')

print('---------------------------')

print('pandas Timestamp:', stamp)

print('year:', stamp.year)

print('month:', stamp.month)

print('day:', stamp.day)

print('hour:', stamp.hour)

print('minute:', stamp.minute)

print('second:', stamp.second)

print('microsecond:', stamp.microsecond)

print('day of week:', stamp.dayofweek) # [Monday 0 ~ Sunday 6]

print('day of year:', stamp.dayofyear)

print('days in month:', stamp.days_in_month) # or daysinmonth

print('quarter:', stamp.quarter)

print('week number of the year:', stamp.week) # or weekofyear


---------------------------
pandas Timestamp Attributes
---------------------------
pandas Timestamp: 2019-12-22 13:30:59
year: 2019
month: 12
day: 22
hour: 13
minute: 30
second: 59
microsecond: 0
day of week: 6
day of year: 356
days in month: 31
quarter: 4
week number of the year: 51





  (3) pandas Timestamp Methods 로 날짜, 시간 정보 확인하기


- date() 메소드: 년-월-일(year-month-day) 객체 반환

- time() 메소드: 시간-분-초(hour-minute-second) 객체 반환



# pandas Timestamp

pd_ts = pd.Timestamp(2019, 12, 22, 13, 30, 59)

pd_ts

[Out] Timestamp('2019-12-22 13:30:59')


# pandas Timestamp date(), time() method

print('date:', pd_ts.date())

print('time', pd_ts.time())

[Out] date: 2019-12-22 [Out] time 13:30:59




- combine() 메소드: 날짜(date)와 시간(time) 객체를 합쳐서 날짜-시간(date-time) 객체 만들기



# combine() method

pd.Timestamp.combine(pd_ts.date(), pd_ts.time())

[Out] Timestamp('2019-12-22 13:30:59')

 



- month_name() 메소드: 월의 영문 이름 반환



pd_ts.month_name()

[Out] 'December'

 



- timestamp() 메소드: float형 POSIX timestamp 반환



pd_ts.timestamp()

[Out1577021459.0

 



- now(), today() 메소드: 현재 날짜와 시간 반환 (current date and time) 

                                      (cf. datetime.now() 와 동일)



# current date and time

pd.Timestamp.now()

[Out] Timestamp('2019-12-22 15:55:52.916704')

pd.Timestamp.today()

[Out] Timestamp('2019-12-22 15:55:52.924013')


# equvalent to datetime.now()

import datetime as dt

dt.datetime.now()

[Out] datetime.datetime(2019, 12, 22, 15, 55, 52, 933711)





  (4) pandas Timestamp를 문자열(string)로 변환, 문자열을 Timestamp로 변환

       (Converting between pandas Timestamp and Strings)



(4-1) strftime(format 설정): pandas Timestamp를 문자열(string)로 변환



# convert pandas Timestamp to string

pd_ts = pd.Timestamp(2019, 12, 22, 13, 30, 59)


pd_ts.strftime('%Y-%m-%d %H:%M:%S') # 4-digit year, 24-hour format

[Out] '2019-12-22 13:30:59'

 

pd_ts.strftime('%y-%m-%d %I:%M:%S') # 2-digit year, 12-hour format

[Out] '19-12-22 01:30:59'




(4-2) pd.to_datetime(문자열): 문자열을 pandas Timestamp로 변환



# convert string to pandas Timestamp

pd.to_datetime('2019-12-22 01:30:59')

[Out] Timestamp('2019-12-22 01:30:59')

 



여러 개의 날짜(-시간) 포맷의 문자열로 이루어지 리스트를 가지고 pd.to_datetime() 을 이용하여 pandas DatetimeIndex 를 만들 수 있습니다. 이렇게 만든 DatetimeIndex를 pandas Series, DataFrame의 index 로 사용할 수 있습니다.  



# convert string list to pandas datetimeIndex

ts_list = ['2019-12-22', '2019-12-23', '2019-12-24', '2019-12-25']

ts_idx = pd.to_datetime(ts_list)

ts_idx

[Out] DatetimeIndex(['2019-12-22', '2019-12-23', '2019-12-24', '2019-12-25'], dtype='datetime64[ns]', freq=None)


val = [1, 2, 3, 4]

ts_series = pd.Series(val, index=ts_idx)

ts_series

[Out]
2019-12-22    1
2019-12-23    2
2019-12-24    3
2019-12-25    4
dtype: int64



ts_df = pd.DataFrame(val, columns=['val'], index=ts_idx)

ts_df

[Out]
val
2019-12-221
2019-12-232
2019-12-243
2019-12-254





  (5) pandas Timestamp를 Python standard datetime 으로 변환하기


pandas의 to_pydatetime() 메소드를 사용하여 pandas Timestamp 객체를 native Python datetime 모듈의 날짜-시간 객체로 변환할 수 있습니다. 



pd_ts = pd.Timestamp(2019, 12, 22, 13, 30, 59)

pd_ts

[Out] Timestamp('2019-12-22 13:30:59')

 

# convert pandas Timestamp to native Python datatime

pd_ts.to_pydatetime()

[Out] datetime.datetime(2019, 12, 22, 13, 30, 59)




많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python 표준 라이브러리(Python standard library)로 시계열 데이터의 날짜, 시간을 처리, 조작, 분석할 때 사용하는 datetime 모듈의 4가지 데이터 유형 (4 data types in datetime module in python pandas library) 에 대해서 알아보겠습니다. 


(1) datetime.date: 년(year), 월(month), 일(day)

(2) datetime.time: 시간(hour), 분(minute), 초(second), 마이크로초(microsecond)

(3) datetime.datetime: date(년, 월, 일) & time(시간, 분, 초, 마이크로초)

(4) datetime.timedelta: 두 개의 datetime 값의 차이 (difference between 2 DateTime values)

                            --> 일(dayes), 초(seconds), 마이크로초(microseconds) 형태로 반환


이외 datetime.tzinfo, datetime.timezone 클래스가 있습니다. 



[ Python standard library: 4 data types in datetime module ]




  (1) datetime.date : 년(year), 월(month), 일(day)


date.date(year, month, day) 의 형태로 년/월/일 정보를 가지는 달력의 날짜(calendar date) 데이터 객체를 생성할 수 있으며, 날짜 객체로 부터 year, month, day attribute로 년(year), 월(month), 일(day) 데이터를 추출할 수 있습니다. 



import pandas as pd

import datetime as dt


# date: (year, month, day)

mydate = dt.date(2019, 12, 21)

mydate

datetime.date(2019, 12, 21)

print('year:', mydate.year)

print('month:', mydate.month)

print('day:', mydate.day)

year: 2019
month: 12
day: 21





  (2) datetime.time : 시간(hour), 분(minute), 초(second), 마이크로초(microsecond)


datetime.time 클래스를 사용하여 시간(hour), 분(minute), 초(second), 마이크로초(microsecond)의 시계의 시간 데이터 객체를 생성, 조회할 수 있습니다. 



# time: (hour, minute, second, microsecond)

mytime = dt.time(20, 46, 22, 445671)

mytime

datetime.time(20, 46, 22, 445671)

print('hour:', mytime.hour)

print('minute:', mytime.minute)

print('second:', mytime.second)

print('microsecond:', mytime.microsecond)

hour: 20
minute: 46
second: 22
microsecond: 445671





  (3) datetime.datetime : date(year, month, day)
                                  & time(hour, minute, second, microsecond)


datetime.datetime 은 위의 (1)번의 datetime.date 와 (2)번의 datetime.time 을 합쳐놓아서 날짜(date)와 시간(time) 정보를 모두 가지는 날짜-시간 객체입니다. 


datetime.datetime.now() 는 현재 날짜-시간을 객체로 가져옵니다. 

year, month, day, hour, minute, second, microsecond attribute를 사용하여 datetime 객체로 부터 년, 월, 일, 시간, 분, 초, 마이크로초 정보를 가져올 수 있습니다. 



# datetime: (year, month, day, hour, minute, second, microsecond)

now = dt.datetime.now() # current date and time

now

datetime.datetime(2019, 12, 21, 20, 46, 22, 445671)


print('year:', now.year)

print('month:', now.month)

print('day:', now.day)

print('hour:', now.hour)

print('minute:', now.minute)

print('second:', now.second)

print('microsecond:', now.microsecond)

year: 2019
month: 12
day: 21
hour: 20
minute: 46
second: 22
microsecond: 445671




두 개의 datetime.datetime 의 날짜-시간 객체끼리 - 연산을 통해 날짜-시간 차이를 계산할 수 있습니다. 

이들 차이(delta)에 대해 days, seconds, microseconds attribute로 날짜 차이, 초 차이, 마이크로초 차이 정보를 추출할 수 있습니다. 



now = dt.datetime.now() # current date and time

delta = now - dt.datetime(2019, 12, 1, 23, 59, 59)

delta

datetime.timedelta(19, 74783, 445671)


print('delta days:', delta.days)

print('delta seconds:', delta.seconds)

print('delta microseconds:', delta.microseconds)

delta days: 19
delta seconds: 74783
delta microseconds: 445671





  (4) datetime.timedelta : 두 개의 datetime 값 간의 차이 

                                   (the difference between 2 datetime values)


두 개의 datetime 객체 값 간의 차이를 구할 때 timedelta 클래스를 사용하면 편리하게 특정 일/시간 차이가 나는 날짜-시간을 구할 수 있습니다. 


datetime.timedelta(days, seconds, microseconds) 의 형태로 날짜-시간 차이 데이터를 저장합니다. 

weeks = 1 은 7 days 로 변환되며, minutes = 1 은 60 seconds 로 변환되고, milliseconds = 1000 은 1 seconds 로 변환됩니다. 



# timedelat() class

import datetime as dt

delta = dt.timedelta(days=1, 

                     seconds=20, 

                     microseconds=1000

                     milliseconds=5000, 

                     minutes=5, 

                     hours=12, 

                     weeks=2)


delta

datetime.timedelta(15, 43525, 1000)

 

# check

days = 1

weeks = 2

seconds = 20

microseconds = 1000

milliseconds = 5000

minutes = 5

hours = 12


print('days:', days + 7*weeks)

print('seconds:', seconds + 60*minutes + 60*60*hours + milliseconds/1000)

print('microsecond:', microseconds)

days: 15
seconds: 43525.0
microsecond: 1000




timedelta 클래스를 사용하여 각각 1 day, 1 day 10 seconds, 1 day 10 seocnds 100 microseconds 를 더해보겠습니다. 



# timedelta: difference between two datetime values

# (days)

dt.datetime(2019, 12, 21) + dt.timedelta(1)  # + 1 day

datetime.datetime(2019, 12, 22, 0, 0)


# (days, seconds)

dt.datetime(2019, 12, 21, 23, 59, 59) + dt.timedelta(1, 10)  # + 1 day 10 seconds

datetime.datetime(2019, 12, 23, 0, 0, 9)


# (days, seconds, microseconds)

dt.datetime(2019, 12, 21, 23, 59, 59, 1000) + dt.timedelta(1, 10, 100)  # + 1day 10seconds 100microseconds

datetime.datetime(2019, 12, 23, 0, 0, 9, 1100)




이번에는 위와 반대로 datetime.timedelta 클래스로 1 day, 10 seconds, 100 microseconds를 빼보겠습니다. 



# minus

dt.datetime(2019, 12, 21, 23, 59, 59, 1000) - dt.timedelta(1, 10, 100)

datetime.datetime(2019, 12, 20, 23, 59, 49, 900)




timedelta 클래스에 곱하기와 나누기를 적용해서 빼는 것도 가능합니다.  첫번째의 - 5 * datetime.timedelta(1) = - 5 days 를 빼라는 의미이며, 두번째의 - datetime.timedelta(10)/ 2 = - 5 days 역시 10 days를 2로 나눈 5 days를 빼라는 의미로 동일한 결과를 반환합니다. 



# multiplication

dt.datetime(2019, 12, 21, 23, 59, 59, 1000) - 5 * dt.timedelta(1)

datetime.datetime(2019, 12, 16, 23, 59, 59, 1000)


# divide

dt.datetime(2019, 12, 21, 23, 59, 59, 1000) - dt.timedelta(10) / 2

datetime.datetime(2019, 12, 16, 23, 59, 59, 1000)




pandas Timestamp 클래스를 이용한 날짜-시간 입력, 변환, 정보조회 방법은 https://rfriend.tistory.com/497 를 참고하세요. 


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



728x90
반응형
Posted by Rfriend
,