timetable
을 사용하여 타임스탬프가 지정된 데이터 전처리 후 탐색하기
이 예제에서는 timetable
데이터 컨테이너를 사용하여 센서 데이터에서 자전거 통행량의 패턴을 분석하는 방법을 보여줍니다. timetable 데이터 컨테이너는 타임스탬프가 지정된 데이터를 구성하고 전처리하는 데 사용됩니다. 데이터는 매사추세츠주 케임브리지에 있는 브로드웨이 스트리트(Broadway Street)의 센서 데이터입니다. 케임브리지시는 일반인들이 케임브리지 오픈 데이터(Cambridge Open Data) 사이트에서 전체 데이터 세트에 액세스할 수 있도록 하고 있습니다.
이 예제에서는 누락된 값 제거, 여러 시간 스텝을 가진 타임스탬프 데이터의 동기화 등 다양한 데이터 정리, 먼징(Munging), 전처리 작업을 수행하는 방법을 보여줍니다. 또한 timetable
데이터 컨테이너로 다음 작업을 수행하여 시각화와 그룹형 계산을 비롯한 데이터 탐색을 강조 표시합니다.
일일 자전거 통행량 탐색
자전거 통행량과 현지 기상 조건 비교
다양한 요일과 시간에 대한 자전거 통행량 분석
자전거 통행량 데이터를 타임테이블로 가져오기
쉼표로 구분된 텍스트 파일에서 자전거 통행량 데이터 샘플을 가져옵니다. readtable
함수는 데이터를 테이블 형식으로 반환합니다. head
함수를 사용하여 처음 8개 행을 표시합니다.
bikeTbl = readtable('BicycleCounts.csv');
head(bikeTbl)
Timestamp Day Total Westbound Eastbound ___________________ _____________ _____ _________ _________ 2015-06-24 00:00:00 {'Wednesday'} 13 9 4 2015-06-24 01:00:00 {'Wednesday'} 3 3 0 2015-06-24 02:00:00 {'Wednesday'} 1 1 0 2015-06-24 03:00:00 {'Wednesday'} 1 1 0 2015-06-24 04:00:00 {'Wednesday'} 1 1 0 2015-06-24 05:00:00 {'Wednesday'} 7 3 4 2015-06-24 06:00:00 {'Wednesday'} 36 6 30 2015-06-24 07:00:00 {'Wednesday'} 141 13 128
이 데이터에는 타임스탬프가 있어 타임테이블을 사용하여 데이터를 저장하고 분석하기가 편리합니다. 타임테이블은 테이블과 유사하지만 데이터 행과 연결된 타임스탬프를 포함합니다. 타임스탬프와 행 시간값은 각각 datetime
값과 duration
값으로 표시됩니다. 시점과 경과 시간을 표시할 때는 각각 datetime
데이터형과 duration
데이터형을 사용하는 것이 좋습니다.
table2timetable
함수를 사용하여 bikeTbl
을 타임테이블로 변환해 보겠습니다. readtable
은 테이블을 반환하기 때문에 변환 함수를 사용해야 합니다. table2timetable
은 테이블의 첫 번째 datetime
변수 또는 duration
변수를 타임테이블의 행 시간값으로 변환합니다. 행 시간값은 행에 레이블을 지정하는 메타데이터입니다. 그러나 타임테이블을 표시할 경우에는 행 시간값과 타임테이블 변수가 비슷한 방식으로 표시됩니다. 참고로, 테이블의 변수는 5개인 반면, 타임테이블의 변수는 4개입니다.
bikeData = table2timetable(bikeTbl); head(bikeData)
Timestamp Day Total Westbound Eastbound ___________________ _____________ _____ _________ _________ 2015-06-24 00:00:00 {'Wednesday'} 13 9 4 2015-06-24 01:00:00 {'Wednesday'} 3 3 0 2015-06-24 02:00:00 {'Wednesday'} 1 1 0 2015-06-24 03:00:00 {'Wednesday'} 1 1 0 2015-06-24 04:00:00 {'Wednesday'} 1 1 0 2015-06-24 05:00:00 {'Wednesday'} 7 3 4 2015-06-24 06:00:00 {'Wednesday'} 36 6 30 2015-06-24 07:00:00 {'Wednesday'} 141 13 128
whos bikeTbl bikeData
Name Size Bytes Class Attributes bikeData 9387x4 1412425 timetable bikeTbl 9387x5 1487735 table
시간값과 데이터에 액세스하기
Day
변수를 categorical형으로 변환합니다. categorical형 데이터는 유한한 이산 값 집합(예: 요일 이름)으로 구성된 데이터를 위해 설계되었습니다. 요일 순서대로 범주를 나열합니다. 점 첨자를 사용하여 이름으로 변수에 액세스해 보겠습니다.
bikeData.Day = categorical(bikeData.Day,{'Sunday','Monday','Tuesday',... 'Wednesday','Thursday','Friday','Saturday'});
타임테이블에서 시간값은 데이터 변수와는 별도로 처리됩니다. 타임테이블의 Properties
에 액세스하여 행 시간값이 타임테이블의 첫 번째 차원이고 변수가 두 번째 차원이라는 것을 확인합니다. DimensionNames
속성은 두 개 차원의 이름을 표시하고, VariableNames
속성은 두 번째 차원의 변수 이름들을 표시합니다.
bikeData.Properties
ans = TimetableProperties with properties: Description: '' UserData: [] DimensionNames: {'Timestamp' 'Variables'} VariableNames: {'Day' 'Total' 'Westbound' 'Eastbound'} VariableDescriptions: {} VariableUnits: {} VariableContinuity: [] RowTimes: [9387x1 datetime] StartTime: 2015-06-24 00:00:00 SampleRate: NaN TimeStep: NaN Events: [] CustomProperties: No custom properties are set. Use addprop and rmprop to modify CustomProperties.
table2timetable
이 테이블을 타임테이블로 변환할 때 기본적으로 Timestamp
를 첫 번째 차원 이름으로 할당했습니다. Timestamp가 원래 테이블의 변수 이름이기 때문입니다. Properties
를 통해 차원 이름과, 타임테이블의 기타 메타데이터를 변경할 수 있습니다.
차원 이름을 Time
과 Data
로 변경해 보겠습니다.
bikeData.Properties.DimensionNames = {'Time' 'Data'}; bikeData.Properties
ans = TimetableProperties with properties: Description: '' UserData: [] DimensionNames: {'Time' 'Data'} VariableNames: {'Day' 'Total' 'Westbound' 'Eastbound'} VariableDescriptions: {} VariableUnits: {} VariableContinuity: [] RowTimes: [9387x1 datetime] StartTime: 2015-06-24 00:00:00 SampleRate: NaN TimeStep: NaN Events: [] CustomProperties: No custom properties are set. Use addprop and rmprop to modify CustomProperties.
타임테이블의 처음 8개 행을 표시합니다.
head(bikeData)
Time Day Total Westbound Eastbound ___________________ _________ _____ _________ _________ 2015-06-24 00:00:00 Wednesday 13 9 4 2015-06-24 01:00:00 Wednesday 3 3 0 2015-06-24 02:00:00 Wednesday 1 1 0 2015-06-24 03:00:00 Wednesday 1 1 0 2015-06-24 04:00:00 Wednesday 1 1 0 2015-06-24 05:00:00 Wednesday 7 3 4 2015-06-24 06:00:00 Wednesday 36 6 30 2015-06-24 07:00:00 Wednesday 141 13 128
가장 늦은 행 시간값과 가장 이른 행 시간값 사이의 경과일을 확인합니다. 차례로 변수를 참조할 때 점 표기법을 사용하여 변수에 액세스할 수 있습니다.
elapsedTime = max(bikeData.Time) - min(bikeData.Time)
elapsedTime = duration
9383:30:00
elapsedTime.Format = 'd'
elapsedTime = duration
390.98 days
지정된 날짜의 일반적인 자전거 수를 검토하려면 총 자전거 수의 평균과 서쪽 방향과 동쪽 방향으로 이동한 횟수를 계산하십시오.
중괄호를 사용하여 bikeData
의 내용을 참조하면 숫자형 데이터를 행렬로 반환할 수 있습니다. 처음 8개 행을 표시해 보겠습니다. 표준 테이블 첨자를 사용하여 여러 변수에 액세스합니다.
counts = bikeData{:,2:end}; counts(1:8,:)
ans = 8×3
13 9 4
3 3 0
1 1 0
1 1 0
1 1 0
7 3 4
36 6 30
141 13 128
평균은 숫자형 데이터에만 적합하므로 숫자형 변수 선택을 위해 vartype
함수를 사용합니다. 변수를 선택할 때 수동으로 테이블 또는 타임테이블의 요소를 참조하는 것보다 vartype
을 사용하는 것이 더 편리합니다. 평균을 계산하고 NaN
값은 생략해 보겠습니다.
counts = bikeData{:,vartype('numeric')}; mean(counts,'omitnan')
ans = 1×3
49.8860 24.2002 25.6857
날짜와 시간으로 데이터 선택하기
휴일에 얼마나 많은 사람들이 자전거를 이용하는지 확인하기 위해 7월 4일 공휴일의 데이터를 검토해 보겠습니다. 2015년 7월 4일의 행 시간값을 사용하여 타임테이블의 요소를 참조합니다. 행 시간값에 대해 인덱싱할 때는 시간이 정확히 일치해야 합니다. 시간 인덱스는 datetime
값, duration
값, 또는 날짜와 시간으로 변환할 수 있는 문자형 벡터로 지정할 수 있습니다. 여러 시간을 하나의 배열로 지정할 수 있습니다.
특정 날짜와 시간으로 bikeData
의 요소를 참조하여 2015년 7월 4일의 데이터를 추출해 보겠습니다. 날짜만 지정하면 시간은 자정 또는 00:00:00으로 가정됩니다.
bikeData('2015-07-04',:)
ans=1×4 timetable
Time Day Total Westbound Eastbound
___________________ ________ _____ _________ _________
2015-07-04 00:00:00 Saturday 8 7 1
d = {'2015-07-04 08:00:00','2015-07-04 09:00:00'}; bikeData(d,:)
ans=2×4 timetable
Time Day Total Westbound Eastbound
___________________ ________ _____ _________ _________
2015-07-04 08:00:00 Saturday 15 3 12
2015-07-04 09:00:00 Saturday 21 4 17
이 방법을 사용하여 하루 전체에 대한 데이터를 추출하는 것은 번거로운 일입니다. 특정 시간값에 대해 인덱싱하지 않고 시간 범위를 지정해도 됩니다. 시간 범위 첨자를 헬퍼로 생성하려면 timerange
함수를 사용하십시오.
2015년 7월 4일 하루 동안의 시간 범위를 타임테이블에 첨자로 사용합니다. 시작 시간을 7월 4일 자정으로, 종료 시간을 7월 5일 자정으로 지정합니다. 기본적으로 timerange
는 시작 시간부터 종료 시간까지의 모든 시간값을 포함하되, 종료 시간은 포함하지 않습니다. 하루 동안의 자전거 수를 플로팅해 보겠습니다.
tr = timerange('2015-07-04','2015-07-05'); jul4 = bikeData(tr,'Total'); head(jul4)
Time Total ___________________ _____ 2015-07-04 00:00:00 8 2015-07-04 01:00:00 13 2015-07-04 02:00:00 4 2015-07-04 03:00:00 1 2015-07-04 04:00:00 0 2015-07-04 05:00:00 1 2015-07-04 06:00:00 8 2015-07-04 07:00:00 16
bar(jul4.Time,jul4.Total) ylabel('Bicycle Counts') title('Bicycle Counts on July 4, 2015')
플롯에서 하루 동안 자전거 수는 점점 늘어나다가 오후가 되면 안정을 유지합니다. 많은 기업들이 근무를 하지 않기 때문에 플롯에서 출퇴근 시간에 일반적인 통행량이 표시되지 않습니다. 어두워지면 축하 불꽃놀이가 시작되어 늦은 저녁 시간에는 자전거 수가 최대치에 달합니다. 이러한 추세를 더 자세히 검토하려면 이 데이터를 보통날의 데이터와 비교해야 합니다.
7월 4월의 데이터와 나머지 7월 날짜들의 데이터를 비교해 보겠습니다.
jul = bikeData(timerange('2015-07-01','2015-08-01'),:); plot(jul.Time,jul.Total) hold on plot(jul4.Time,jul4.Total) ylabel('Total Count') title('Bicycle Counts in July') hold off legend('Bicycle Count','July 4 Bicycle Count')
플롯은 주중과 주말의 통행량 차이로 인한 변동 추세를 보여줍니다. 7월 4일과 5일의 통행량 패턴은 주말 통행량 패턴과 일치합니다. 7월 5일은 월요일이지만 공휴일로 지정되어 있습니다. 이러한 추세는 추가적인 전처리와 분석을 통해 더 자세히 검토할 수 있습니다.
timetable
을 사용하여 시간값과 데이터 전처리하기
보통 타임스탬프가 지정된 데이터 세트는 정리가 되지 않기 때문에 이상과 오류가 있을 수 있습니다. 타임테이블은 이상과 오류를 해결하는 데 매우 적합합니다.
타임테이블은 행 시간값을 특정 순서대로 정렬할 필요가 없습니다. 해당 행 시간값별로 정렬되지 않은 행을 포함할 수 있습니다. 또한 타임테이블에는 같은 행 시간값을 가진 행이 여러 개 포함될 수 있습니다. 물론 행에는 서로 다른 데이터 값이 있을 수도 있습니다. 행 시간값이 고유하고 정렬되어 있는 경우에도 행 시간값 간의 시간 스텝의 크기는 다를 수 있습니다. 타임테이블에는 누락된 행 시간값을 나타내는 NaT
값 또는 NaN
값도 포함될 수 있습니다.
timetable
데이터형은 누락된 시간값, 중복된 시간값 또는 균일하지 않은 시간값을 해결할 수 있는 다양한 방법들을 제공합니다. 또한 데이터를 리샘플링하거나 집계하여 규칙적인 타임테이블을 생성할 수도 있습니다. 타임테이블이 규칙적이면 여기에 포함된 행 시간값은 정렬되어 있고 고유하며 균일한 간격의 시간 스텝을 가집니다.
누락된 행 시간값을 찾으려면
ismissing
을 사용하십시오.누락된 시간값과 데이터를 제거하려면
rmmissing
을 사용하십시오.타임테이블을 행 시간값을 기준으로 정렬하려면
sortrows
를 사용하십시오.고유하고 정렬된 행 시간값이 포함된 타임테이블을 만들려면
unique
와retime
을 사용하십시오.규칙적인 타임테이블을 만들려면 균일한 간격의 시간 벡터를 지정하고
retime
을 사용하십시오.
시간순으로 정렬하기
타임테이블이 정렬되어 있는지 확인합니다. 행 시간값이 오름차순으로 나열되어 있으면 타임테이블이 정렬된 것입니다.
issorted(bikeData)
ans = logical
0
타임테이블을 정렬해 보겠습니다. sortrows
함수는 행을 해당 행 시간값을 기준으로 가장 이른 시간에서 가장 늦은 시간으로 정렬합니다. 중복된 행 시간값을 가진 행이 있으면 sortrows
는 모든 중복 항목을 출력값으로 복사합니다.
bikeData = sortrows(bikeData); issorted(bikeData)
ans = logical
1
누락된 시간값과 데이터를 식별하고 제거하기
타임테이블에서는 변수 또는 행 시간값에 누락된 데이터 지정자를 사용할 수 있습니다. 예를 들어, 누락된 숫자형 값은 NaN
으로, 누락된 datetime형 값은 NaT
로 표시할 수 있습니다. standardizeMissing
함수, ismissing
함수, rmmissing
함수, fillmissing
함수를 사용하여 각각 누락된 값을 할당하고 찾고 제거하고 채울 수 있습니다.
타임테이블 변수의 누락된 값을 찾아 개수를 세어 보겠습니다. 이 예제에서 누락된 값은 수집된 데이터가 없는 경우를 나타냅니다.
missData = ismissing(bikeData); sum(missData)
ans = 1×4
1 3 3 3
ismissing
의 출력값은 테이블과 크기가 같은 logical
행렬로, 누락된 데이터 값을 true로 식별합니다. 누락된 데이터 지정자가 있는 행을 표시해 보겠습니다.
idx = any(missData,2); bikeData(idx,:)
ans=3×4 timetable
Time Day Total Westbound Eastbound
___________________ ___________ _____ _________ _________
2015-08-03 00:00:00 Monday NaN NaN NaN
2015-08-03 01:00:00 Monday NaN NaN NaN
NaT <undefined> NaN NaN NaN
ismissing(bikeData)
는 시간값이 아니라 타임테이블의 변수에서만 누락된 데이터를 찾습니다. 누락된 행 시간값을 찾으려면 행 시간값에 대해 ismissing
을 호출하십시오.
missTimes = ismissing(bikeData.Time); bikeData(missTimes,:)
ans=2×4 timetable
Time Day Total Westbound Eastbound
____ ___________ _____ _________ _________
NaT <undefined> NaN NaN NaN
NaT Friday 6 3 3
이 예제에서 누락된 시간값 또는 데이터 값은 계측 오류를 나타내므로 제외할 수 있습니다. rmmissing
을 사용하여 누락된 데이터 값이나 누락된 행 시간값이 포함된 테이블 행을 제거해 보겠습니다.
bikeData = rmmissing(bikeData); sum(ismissing(bikeData))
ans = 1×4
0 0 0 0
sum(ismissing(bikeData.Time))
ans = 0
중복된 시간값이나 중복된 데이터 제거하기
데이터에 중복된 시간값 및/또는 중복된 행이 있는지 확인합니다. 명확한 중복 항목은 계측 오차로 간주될 수도 있으므로 제외하는 것이 좋습니다. 정렬된 시간값들 사이의 차이가 정확하게 0인 경우를 찾아 중복된 시간값을 식별해 보겠습니다.
idx = diff(bikeData.Time) == 0; dup = bikeData.Time(idx)
dup = 3x1 datetime
2015-08-21 00:00:00
2015-11-19 23:00:00
2015-11-19 23:00:00
반복되는 시간값은 3개이고, 2015년 11월 19일은 두 번 반복됩니다. 반복된 시간값과 관련된 데이터를 검토해 보겠습니다.
bikeData(dup(1),:)
ans=2×4 timetable
Time Day Total Westbound Eastbound
___________________ ______ _____ _________ _________
2015-08-21 00:00:00 Friday 14 9 5
2015-08-21 00:00:00 Friday 11 7 4
bikeData(dup(2),:)
ans=3×4 timetable
Time Day Total Westbound Eastbound
___________________ ________ _____ _________ _________
2015-11-19 23:00:00 Thursday 17 15 2
2015-11-19 23:00:00 Thursday 17 15 2
2015-11-19 23:00:00 Thursday 17 15 2
첫 번째의 경우 시간값이 중복되지만 데이터는 중복되지 않는 반면, 나머지의 경우는 시간값과 데이터가 완전히 중복됩니다. 타임테이블의 행에 다른 행과 동일한 행 시간값이 포함되어 있고, 데이터 값 또한 동일하게 포함되어 있으면 해당 행은 중복 항목으로 간주됩니다. unique
를 사용하여 타임테이블에서 중복된 행을 제거할 수 있습니다. 또한 unique
함수는 행 시간값을 기준으로 행을 정렬합니다.
bikeData = unique(bikeData);
시간값이 중복되지만 데이터는 중복되지 않는 행은 확인이 필요합니다. 이러한 시간값에 대한 데이터를 검토해 보겠습니다.
d = dup(1) + hours(-2:2); bikeData(d,:)
ans=5×4 timetable
Time Day Total Westbound Eastbound
___________________ ________ _____ _________ _________
2015-08-20 22:00:00 Thursday 40 30 10
2015-08-20 23:00:00 Thursday 25 18 7
2015-08-21 00:00:00 Friday 11 7 4
2015-08-21 00:00:00 Friday 14 9 5
2015-08-21 02:00:00 Friday 6 5 1
이 경우, 데이터와 그 관련 시간값들이 일관되기 때문에 중복된 시간값은 실수일 수 있습니다. 중복 항목이 01:00:00을 나타내는 것으로 보이지만 원래 몇 시여야 하는지 확실하지는 않습니다. 두 시간 지점의 데이터를 설명하기 위해 데이터를 누적할 수 있습니다.
sum(bikeData{dup(1),2:end})
ans = 1×3
25 16 9
유일하게 수동으로 수행할 수 있는 작업입니다. 하지만 대부분의 행에서는 retime
함수로 이 계산을 수행할 수 있습니다. sum
함수를 사용하여 집계함으로써 데이터를 누적하고 고유 시간값으로 만듭니다. sum은 숫자형 데이터에는 적합하지만 타임테이블의 categorical형 데이터에는 적합하지 않습니다. vartype
을 사용하여 숫자형 변수를 식별해 보겠습니다.
vt = vartype('numeric'); t = unique(bikeData.Time); numData = retime(bikeData(:,vt),t,'sum'); head(numData)
Time Total Westbound Eastbound ___________________ _____ _________ _________ 2015-06-24 00:00:00 13 9 4 2015-06-24 01:00:00 3 3 0 2015-06-24 02:00:00 1 1 0 2015-06-24 03:00:00 1 1 0 2015-06-24 04:00:00 1 1 0 2015-06-24 05:00:00 7 3 4 2015-06-24 06:00:00 36 6 30 2015-06-24 07:00:00 141 13 128
categorical형 데이터의 합을 구할 수는 없지만 하나의 레이블이 하루를 나타내기 때문에 각 시간값의 첫 번째 값을 취하면 됩니다. 동일한 시간 벡터로 다시 retime
연산을 수행하여 타임테이블을 결합할 수 있습니다.
vc = vartype('categorical'); catData = retime(bikeData(:,vc),t,'firstvalue'); bikeData = [catData,numData]; bikeData(d,:)
ans=4×4 timetable
Time Day Total Westbound Eastbound
___________________ ________ _____ _________ _________
2015-08-20 22:00:00 Thursday 40 30 10
2015-08-20 23:00:00 Thursday 25 18 7
2015-08-21 00:00:00 Friday 25 16 9
2015-08-21 02:00:00 Friday 6 5 1
시간 간격의 균등성 검사하기
이 데이터는 균등하게 1시간 간격의 시간 스텝을 가지는 것으로 보입니다. 타임테이블의 모든 행 시간값에 균등한 간격이 적용되었는지 확인하려면 isregular
함수를 사용하십시오. isregular
는 중복되거나 누락된 시간값(NaT
또는 NaN
) 없이 균일한 간격으로 정렬된 시간값(단조 증가)에 대해서는 true
를 반환합니다.
isregular(bikeData)
ans = logical
0
출력값 0
또는 false
는 타임테이블의 시간값에 균일한 간격이 적용되지 않았다는 것을 나타냅니다. 시간 간격을 더 자세히 탐색해 보겠습니다.
dt = diff(bikeData.Time); [min(dt); max(dt)]
ans = 2x1 duration
00:30:00
03:00:00
타임테이블에 규칙적인 시간 간격을 적용하려면 retime
또는 synchronize
를 사용하여 원하는 시간 간격을 지정하십시오.
일일 자전거 수량 확인하기
retime
함수를 사용하여 일일 자전거 수를 확인해 보겠습니다. sum
메서드를 사용하여 각 날짜의 개수 데이터를 누적할 수 있습니다. sum은 숫자형 데이터에는 적합하지만 타임테이블의 categorical형 데이터에는 적합하지 않습니다. 데이터형으로 변수를 식별하려면 vartype
을 사용하십시오.
dayCountNum = retime(bikeData(:,vt),'daily','sum'); head(dayCountNum)
Time Total Westbound Eastbound ___________________ _____ _________ _________ 2015-06-24 00:00:00 2141 1141 1000 2015-06-25 00:00:00 2106 1123 983 2015-06-26 00:00:00 1748 970 778 2015-06-27 00:00:00 695 346 349 2015-06-28 00:00:00 153 83 70 2015-06-29 00:00:00 1841 978 863 2015-06-30 00:00:00 2170 1145 1025 2015-07-01 00:00:00 997 544 453
또다시, categorical형 데이터를 표시하기 위해 retime
연산을 적합한 메서드와 함께 다시 수행하고 타임테이블을 결합할 수 있습니다.
dayCountCat = retime(bikeData(:,vc),'daily','firstvalue'); dayCount = [dayCountCat,dayCountNum]; head(dayCount)
Time Day Total Westbound Eastbound ___________________ _________ _____ _________ _________ 2015-06-24 00:00:00 Wednesday 2141 1141 1000 2015-06-25 00:00:00 Thursday 2106 1123 983 2015-06-26 00:00:00 Friday 1748 970 778 2015-06-27 00:00:00 Saturday 695 346 349 2015-06-28 00:00:00 Sunday 153 83 70 2015-06-29 00:00:00 Monday 1841 978 863 2015-06-30 00:00:00 Tuesday 2170 1145 1025 2015-07-01 00:00:00 Wednesday 997 544 453
자전거 수와 기상 데이터 동기화하기
자전거 수와 기상 데이터를 비교하여 날씨가 자전거 이용에 미치는 영향을 검토해 보겠습니다. 폭풍을 비롯해 매사추세츠주 보스턴의 과거 기상 데이터가 포함된 기상 타임테이블을 불러와 보겠습니다.
load BostonWeatherData
head(weatherData)
Time TemperatureF Humidity Events ___________ ____________ ________ ____________ 01-Jul-2015 72 78 Thunderstorm 02-Jul-2015 72 60 None 03-Jul-2015 70 56 None 04-Jul-2015 67 75 None 05-Jul-2015 72 67 None 06-Jul-2015 74 69 None 07-Jul-2015 75 77 Rain 08-Jul-2015 79 68 Rain
타임테이블의 시간값과 변수를 요약하려면 summary
함수를 사용하십시오.
summary(weatherData)
RowTimes: Time: 383x1 datetime Values: Min 01-Jul-2015 Median 08-Jan-2016 Max 17-Jul-2016 TimeStep 24:00:00 Variables: TemperatureF: 383x1 double Values: Min 2 Median 55 Max 85 Humidity: 383x1 double Values: Min 29 Median 64 Max 97 Events: 383x1 categorical Values: Fog 7 Hail 1 Rain 108 Rain-Snow 4 Snow 18 Thunderstorm 12 None 233
synchronize
를 사용하여 자전거 데이터와 기상 데이터를 공통된 시간 벡터로 결합합니다. synchronize
함수 도움말 페이지에 설명된 메서드를 사용하여 타임테이블 데이터를 리샘플링하거나 집계할 수 있습니다.
두 타임테이블의 데이터를 각 일별 시간 벡터의 교집합에서 생성된, 공통된 시간 벡터로 동기화해 보겠습니다.
data = synchronize(dayCount,weatherData,'intersection');
head(data)
Time Day Total Westbound Eastbound TemperatureF Humidity Events ___________________ _________ _____ _________ _________ ____________ ________ ____________ 2015-07-01 00:00:00 Wednesday 997 544 453 72 78 Thunderstorm 2015-07-02 00:00:00 Thursday 1943 1033 910 72 60 None 2015-07-03 00:00:00 Friday 870 454 416 70 56 None 2015-07-04 00:00:00 Saturday 669 328 341 67 75 None 2015-07-05 00:00:00 Sunday 702 407 295 72 67 None 2015-07-06 00:00:00 Monday 1900 1029 871 74 69 None 2015-07-07 00:00:00 Tuesday 2106 1140 966 75 77 Rain 2015-07-08 00:00:00 Wednesday 1855 984 871 79 68 Rain
별도의 y축에서 자전거 통행량과 실외 온도를 비교하여 추세를 검토합니다. 시각화를 위해 데이터에서 주말 부분은 제거해 보겠습니다.
idx = ~isweekend(data.Time); weekdayData = data(idx,{'TemperatureF','Total'}); figure yyaxis left plot(weekdayData.Time, weekdayData.Total) ylabel('Bicycle Count') yyaxis right plot(weekdayData.Time,weekdayData.TemperatureF) ylabel('Temperature (\circ F)') title('Bicycle Counts and Temperature Over Time') xlim([min(data.Time) max(data.Time)])
플롯에서 통행량 데이터와 기상 데이터가 유사한 추세를 보이는 것을 알 수 있습니다. 플롯을 확대해 보겠습니다.
xlim([datetime('2015-11-01'),datetime('2016-05-01')])
추세가 유사한 것으로 보아 날씨가 추울수록 자전거를 이용하는 사람들이 줄어든다는 것을 알 수 있습니다.
요일과 시간 모두를 기준으로 분석하기
요일이나 시간 등 다양한 간격을 기준으로 데이터를 검토합니다. varfun
을 사용하여 변수에 대해 그룹형 계산을 수행하면 일별 총 자전거 수를 확인할 수 있습니다. sum
함수를 함수 핸들로 지정하고 이름-값 쌍을 사용하여 그룹화 변수와 원하는 출력 유형을 지정해 보겠습니다.
byDay = varfun(@sum,bikeData,'GroupingVariables','Day',... 'OutputFormat','table')
byDay=7×5 table
Day GroupCount sum_Total sum_Westbound sum_Eastbound
_________ __________ _________ _____________ _____________
Sunday 1344 25315 12471 12844
Monday 1343 79991 39219 40772
Tuesday 1320 81480 39695 41785
Wednesday 1344 86853 41726 45127
Thursday 1344 87516 42682 44834
Friday 1342 76643 36926 39717
Saturday 1343 30292 14343 15949
figure bar(byDay{:,{'sum_Westbound','sum_Eastbound'}}) legend({'Westbound','Eastbound'},'Location','eastoutside') xticklabels({'Sun','Mon','Tue','Wed','Thu','Fri','Sat'}) title('Bicycle Count by Day of Week')
막대 플롯에서 주중에 통행량이 더 많은 것을 알 수 있습니다. 동쪽 방향 통행량과 서쪽 방향 통행량에 차이가 있는 것도 알 수 있습니다. 이는 사람들이 도시로 올 때와 도시에서 나갈 때 다른 경로를 이용한다는 의미인 것 같습니다. 또는 일부 사람들이 특정 날짜에 도시로 온 후 다른 날 돌아간 것일 수도 있습니다.
시간을 확인하고 varfun
을 사용하여 그룹형 계산을 수행해 보겠습니다.
bikeData.HrOfDay = hour(bikeData.Time); byHr = varfun(@mean,bikeData(:,{'Westbound','Eastbound','HrOfDay'}),... 'GroupingVariables','HrOfDay','OutputFormat','table'); head(byHr)
HrOfDay GroupCount mean_Westbound mean_Eastbound _______ __________ ______________ ______________ 0 389 5.4396 1.7686 1 389 2.7712 0.87147 2 391 1.8696 0.58312 3 391 0.7468 0.289 4 391 0.52685 1.0026 5 391 0.70588 4.7494 6 391 3.1228 22.097 7 391 9.1176 63.54
bar(byHr{:,{'mean_Westbound','mean_Eastbound'}}) legend('Westbound','Eastbound','Location','eastoutside') xlabel('Hour of Day') ylabel('Bicycle Count') title('Mean Bicycle Count by Hour of Day')
일반적인 출퇴근 시간인 오전 9시와 오후 5시 무렵에 통행량이 최고치에 달합니다. 그리고 동쪽 방향 추세와 서쪽 방향 추세가 다릅니다. 일반적으로 서쪽 방향은 케임브리지 근교의 주거 지역과 대학교로 향하는 경로입니다. 동쪽 방향은 보스턴으로 향하는 경로입니다.
늦은 오후에는 동쪽 방향보다 서쪽 방향으로 향하는 통행량이 더 많습니다. 아마 대학교 일정과 해당 지역의 식당가로 향하는 통행량 때문인 것 같습니다. 시간과 요일 모두를 기준으로 하여 추세를 검토해 보겠습니다.
byHrDay = varfun(@sum,bikeData,'GroupingVariables',{'HrOfDay','Day'},... 'OutputFormat','table'); head(byHrDay)
HrOfDay Day GroupCount sum_Total sum_Westbound sum_Eastbound _______ _________ __________ _________ _____________ _____________ 0 Sunday 56 473 345 128 0 Monday 55 202 145 57 0 Tuesday 55 297 213 84 0 Wednesday 56 374 286 88 0 Thursday 56 436 324 112 0 Friday 55 442 348 94 0 Saturday 56 580 455 125 1 Sunday 56 333 259 74
요일을 변수로 취하도록 타임테이블을 정렬하려면 unstack
함수를 사용하십시오.
hrAndDayWeek = unstack(byHrDay(:,{'HrOfDay','Day','sum_Total'}),'sum_Total','Day'); head(hrAndDayWeek)
HrOfDay Sunday Monday Tuesday Wednesday Thursday Friday Saturday _______ ______ ______ _______ _________ ________ ______ ________ 0 473 202 297 374 436 442 580 1 333 81 147 168 173 183 332 2 198 77 68 93 128 141 254 3 86 41 43 44 50 61 80 4 51 81 117 101 108 80 60 5 105 353 407 419 381 340 128 6 275 1750 1867 2066 1927 1625 351 7 553 5355 5515 5818 5731 4733 704
ribbon(hrAndDayWeek.HrOfDay,hrAndDayWeek{:,2:end}) ylim([0 24]) xlim([0 8]) xticks(1:7) xticklabels({'Sun','Mon','Tue','Wed','Thu','Fri','Sat'}) ylabel('Hour') title('Bicycle Count by Hour and Day of Week')
월요일부터 금요일까지 평일에는 통행량이 러시아워에 최대치를 기록하고 저녁에는 점점 줄어드는 유사한 추세를 보입니다. 금요일의 경우 통행량이 줄어들지만 전반적인 추세는 다른 평일과 유사합니다. 토요일과 일요일은 러시아워에 통행량이 최대치가 아니며 오후로 갈수록 증가하는 서로 유사한 추세를 보입니다. 늦은 저녁의 추세도 월요일부터 금요일까지 유사하지만 금요일에는 통행량이 줄어듭니다.
러시아워의 통행량 분석하기
하루 전체 시간의 추세를 검토하기 위해 러시아워 시간값을 기준으로 데이터를 분할합니다. discretize
함수에 여러 시간값 또는 시간값 단위를 사용할 수 있습니다. 예를 들어, 데이터를 AM
, AMRush
, Day
, PMRush
, PM
에 대한 그룹으로 구분합니다. 그런 다음, varfun
을 사용하여 그룹별로 평균을 계산합니다.
bikeData.HrLabel = discretize(bikeData.HrOfDay,[0,6,10,15,19,24],'categorical',... {'AM','RushAM','Day','RushPM','PM'}); byHrBin = varfun(@mean,bikeData(:,{'Total','HrLabel'}),'GroupingVariables','HrLabel',... 'OutputFormat','table')
byHrBin=5×3 table
HrLabel GroupCount mean_Total
_______ __________ __________
AM 2342 3.5508
RushAM 1564 94.893
Day 1955 45.612
RushPM 1564 98.066
PM 1955 35.198
bar(byHrBin.mean_Total)
cats = categories(byHrBin.HrLabel);
xticklabels(cats)
title('Mean Bicycle Count During Rush Hours')
일반적으로 이 지역의 저녁과 아침 러시아워 시간대의 자전거 통행량은 다른 시간대에 비해 2배 많습니다. 이른 아침에는 통행량이 매우 적지만, 늦은 저녁에는 아침과 저녁 러시아워를 제외한 낮 시간과 견줄 만큼 상당한 통행량이 있습니다.
참고 항목
timetable
| table2timetable
| head
| summary
| varfun
| timerange
| sortrows
| rmmissing
| retime
| datetime
| unstack