16 Tarih ve saatler

16.1 Giriş

Bu bölüm R’da tarih ve saatlerle nasıl çalışacağınızı gösterecektir. İlk bakışta tarih ve saatler basit görünmektedir. Bunları düzenli hayatınızda her zaman kullanıyorsunuz ve fazla kafa karışıklığına sebep olmuyorlar. Bununla birlikte, tarihler ve saatler hakkında ne kadar çok şey öğrenirseniz, o kadar karmaşıklaşırlar. Isınmak için bu basit görünen üç soruyu deneyin:

  • Her yıl 365 gün müdür?
  • Her gün 24 saat midir?
  • Her dakika 60 saniye midir?

Eminim ki her yılın 365 günü olmadığını biliyorsunuzdur, ancak bir yılın artık bir yıl olup olmadığına karar vermenin tüm kurallarını biliyor musunuz? (Üç bölümden oluşur.) Dünyanın birçok bölümünün gün ışığından yararlanma saati (DST) kullandığını, böylece bazı günlerin 23 saat, diğerlerinin ise 25 olduğunu hatırlamış olabilirsiniz. Bazı dakikaların dünyanın dönüşündeki yavaşlamadan kaynaklı olarak artık saniye eklenmesinden dolayı 61 saniye olduğunu bilmiyor olabilirsiniz.

Tarihler ve zamanlar zordur, çünkü iki fiziksel olguyu (Dünya’nın kendi ve güneşin etrafındaki dönüşünü) aylar, zaman dilimleri ve DST de dahil olmak üzere bir çok jeopolitik olay ile bağdaştırmak zorundadırlar. Bu bölüm tarih ve saatlerle ilgili her ayrıntıyı size öğretmeyecek, ancak yaygın veri analizi zorluklarında size yardımcı olacak pratik becerilerin sağlam bir temelini verecektir.

16.1.1 Ön Koşullar

Bu bölüm, R’da tarih ve saatlerle çalışmayı kolaylaştıran lubridate paketine odaklanacaktır. lubridate temel tidyverse paketlerinin bir parçası değil, çünkü ona sadece tarih ve saatlerle çalışırken ihtiyaç duyarsınız. Ayrıca pratik yaparken nycflights13 verisine ihtiyacımız olacak.

library(tidyverse)

library(lubridate)
library(nycflights13)

16.2 Tarih ve Saat oluşturma

Zamanda bir noktaya karşılık gelen üç çeşit tarih/saat verisi vardır:

  • Tarih (date): Tibble bunu <date> olarak belirtir

  • Gün içerisinde bir saat (time). Tibble bunu <time> olarak belirtir

  • Tarih ve saat birlikte (date-time): Bu zamandaki bir anı tanımlar (genelde) en yakın saniyeye kadar). Tibble bunu <dttm> olarak belirtir. R’nin başka yerlerinde buna POSIXct adı verilir, ancak bunun çok kullanışlı bir isim olduğunu sanmıyorum.

Bu bölümde, R’nin zamanları (time) depolamak için yerel bir sınıfı olmadığı için yalnızca tarihlere (dates) ve tarih-saatlere (date-time) odaklanacağız. İhtiyacınız olursa, hms paketini kullanabilirsiniz.

İhtiyaçlarınız için her zaman mümkün olan en basit veri türünü kullanmalısınız. Bu demektir ki bir tarih-saat yerine bir tarih kullanabiliyorsanız tarih kullanmalısınız. Zaman dilimleriyle uğraşmayı gerektirdiğinden tarih-saatler önemli ölçüde daha karışıktır. Bölüm sonunda zaman dilimlerine tekrar döneceğiz.

Şimdiki tarih veya tarih-saati almak için today() veya now() kullanabilirsiniz:

today()
#> [1] "2022-07-05"
now()
#> [1] "2022-07-05 20:00:55 UTC"

Bunun haricinde, tarih / saat oluşturmanızın üç yolu vardır:

  • Bir dizgeden.
  • Tarih-zaman bileşenlerinden.
  • Mevcut bir tarih/saat nesnesinden.

Bunlar aşağıdaki şekilde işler.

16.2.1 Dizgelerden

Tarih / saat verileri genellikle dizge olarak gelir. 11.3.4’te tarih / saat dizgelerini tarih / saat olarak okuma konusunda bir yaklaşım gördünüz. Bir başka yaklaşım, lubridate tarafından sağlanan yardımcıları kullanmaktır. Bileşenin sırasını belirlediğinizde, formatı otomatik olarak bulur. Bunları kullanmak için tarihlerinizde yıl, ay ve günün hangi sırayla göründüğünü belirleyin ve ardından “y”, “m” ve “d” dizilerini aynı sırada düzenleyin. Bu, tarihinizi ayrıştırmak için lubridate işlevinin adını verir. Örneğin:

ymd("2017-01-31")
#> [1] "2017-01-31"
mdy("January 31st, 2017")
#> [1] "2017-01-31"
dmy("31-Jan-2017")
#> [1] "2017-01-31"

Bu fonksiyonlar ayrıca dizge olmayan numaralar da alır. Bu, tarih / saat verilerini filtrelemeye ihtiyaç duyabileceğinizden, tek bir tarih / saat nesnesi oluşturmanın en kısa yoludur. ymd () kısa ve kesindir:

ymd(20170131)
#> [1] "2017-01-31"

ymd() ve arkadaşları tarihleri oluşturur. Bir tarih-zaman oluşturmak için, okuma fonksiyonunun adına bir alt çizgi ve “h”, “m” ve “s” dizilerinden bir ya da daha fazlasını ekleyin.

ymd_hms("2017-01-31 20:11:59")
#> [1] "2017-01-31 20:11:59 UTC"
mdy_hm("01/31/2017 08:01")
#> [1] "2017-01-31 08:01:00 UTC"

Ayrıca, bir saat dilimi sağlayarak da bir tarihten tarih-saat oluşturmaya zorlayabilirsiniz:

ymd(20170131, tz = "UTC")
#> [1] "2017-01-31 UTC"

16.2.2 Tekil bileşenlerden

Tek bir dizge yerine, bazen tarih-saatin bileşenleri birden çok sütuna yayılmış olabilir. Uçuş verisinde elimizde bu var:

flights %>% 
  select(year, month, day, hour, minute)
#> # A tibble: 336,776 × 5
#>    year month   day  hour minute
#>   <int> <int> <int> <dbl>  <dbl>
#> 1  2013     1     1     5     15
#> 2  2013     1     1     5     29
#> 3  2013     1     1     5     40
#> 4  2013     1     1     5     45
#> 5  2013     1     1     6      0
#> 6  2013     1     1     5     58
#> # … with 336,770 more rows

Bu tür girdilerden bir tarih / saat oluşturmak için, tarihler için make_date(), tarih-zamanlar için make_datetime() kullanın:

flights %>% 
  select(year, month, day, hour, minute) %>% 
  mutate(departure = make_datetime(year, month, day, hour, minute))
#> # A tibble: 336,776 × 6
#>    year month   day  hour minute departure          
#>   <int> <int> <int> <dbl>  <dbl> <dttm>             
#> 1  2013     1     1     5     15 2013-01-01 05:15:00
#> 2  2013     1     1     5     29 2013-01-01 05:29:00
#> 3  2013     1     1     5     40 2013-01-01 05:40:00
#> 4  2013     1     1     5     45 2013-01-01 05:45:00
#> 5  2013     1     1     6      0 2013-01-01 06:00:00
#> 6  2013     1     1     5     58 2013-01-01 05:58:00
#> # … with 336,770 more rows

flights verisindeki dört zaman sütununun her biri için aynı şeyi yapalım. Zamanlar biraz garip bir formatta gösterilmiş, bu yüzden saat ve dakika bileşenlerini ayırmak için modüler aritmetik kullanırız. Tarih-zaman değişkenlerini oluşturduktan sonra, bölümün geri kalanında inceleyeceğimiz değişkenlere odaklanacağız.

make_datetime_100 <- function(year, month, day, time) {
  make_datetime(year, month, day, time %/% 100, time %% 100)
}

flights_dt <- flights %>% 
  filter(!is.na(dep_time), !is.na(arr_time)) %>% 
  mutate(
    dep_time = make_datetime_100(year, month, day, dep_time),
    arr_time = make_datetime_100(year, month, day, arr_time),
    sched_dep_time = make_datetime_100(year, month, day, sched_dep_time),
    sched_arr_time = make_datetime_100(year, month, day, sched_arr_time)
  ) %>% 
  select(origin, dest, ends_with("delay"), ends_with("time"))

flights_dt
#> # A tibble: 328,063 × 9
#>   origin dest  dep_delay arr_delay dep_time            sched_dep_time     
#>   <chr>  <chr>     <dbl>     <dbl> <dttm>              <dttm>             
#> 1 EWR    IAH           2        11 2013-01-01 05:17:00 2013-01-01 05:15:00
#> 2 LGA    IAH           4        20 2013-01-01 05:33:00 2013-01-01 05:29:00
#> 3 JFK    MIA           2        33 2013-01-01 05:42:00 2013-01-01 05:40:00
#> 4 JFK    BQN          -1       -18 2013-01-01 05:44:00 2013-01-01 05:45:00
#> 5 LGA    ATL          -6       -25 2013-01-01 05:54:00 2013-01-01 06:00:00
#> 6 EWR    ORD          -4        12 2013-01-01 05:54:00 2013-01-01 05:58:00
#> # … with 328,057 more rows, and 3 more variables: arr_time <dttm>,
#> #   sched_arr_time <dttm>, air_time <dbl>

Bu verilerle, kalkış zamanlarının yıl içindeki dağılımını görselleştirebiliyorum:

flights_dt %>% 
  ggplot(aes(dep_time)) + 
  geom_freqpoly(binwidth = 86400) # 86400 seconds = 1 day

Veya tek bir gün içinde:

flights_dt %>% 
  filter(dep_time < ymd(20130102)) %>% 
  ggplot(aes(dep_time)) + 
  geom_freqpoly(binwidth = 600) # 600 s = 10 minutes

Tarih saatlerini sayısal bir bağlamda (histogramda olduğu gibi) kullandığınızda, 1’in 1 saniye anlamına geldiğini, bu nedenle 86400 birimin bir gün anlamına geldiğini unutmayın. Tarihler için 1, 1 gün anlamına gelir.

16.2.3 Diğer türlerden

Tarih-saat ve tarih arasında geçiş yapmak isteyebilirsiniz. Bu as_datetime() ve as_date() fonksiyonlarının işidir:

as_datetime(today())
#> [1] "2022-07-05 UTC"
as_date(now())
#> [1] "2022-07-05"

Bazen “Unix Epoch”tan, tarih / saatleri sayısal çıktı olarak alırsınız, 1970-01-01. Çıktı saniye cinsinden ise as_datetime(); gün cinsinden ise as_date () kullanın.

as_datetime(60 * 60 * 10)
#> [1] "1970-01-01 10:00:00 UTC"
as_date(365 * 10 + 2)
#> [1] "1980-01-01"

16.2.4 Alıştırmalar

  1. Geçersiz tarih içeren bir dize ayrıştırırsanız ne olur?

    ymd(c("2010-10-10", "bananas"))
  2. today() fonksiyonunun tzone argümanı ne yapar? Neden önemlidir?

  3. Aşağıdaki tarihlerin her birini okumak için uygun lubridate fonksiyonunu kullanın:

    d1 <- "January 1, 2010"
    d2 <- "2015-Mar-07"
    d3 <- "06-Jun-2017"
    d4 <- c("August 19 (2015)", "July 1 (2015)")
    d5 <- "12/30/14" # Dec 30, 2014

16.3 Tarih-saat bileşenleri

Artık tarih-saat verisini nasıl R’ın tarih-saat veri yapılarına getireceğinizi bildiğinize göre, onlarla neler yapabileceğinizi inceleyelim. Bu bölüm, tek tek bileşenleri almanıza ve ayarlamanıza izin veren ek fonksiyonlara odaklanacaktır. Bir sonraki bölüm, aritmetik işlemin tarih zamanları ile nasıl çalıştığını gözden geçirecektir.

16.3.1 Bileşenleri almak

Tarihin belli kısımlarını ek fonksiyonlarla alabilirsiniz: year() (yıl), month() (ay), mday() (ayın günü), yday() (yılın günü), wday() (haftanın günü), hour() (saat), minute() (dakika), and second() (saniye).

datetime <- ymd_hms("2016-07-08 12:34:56")

year(datetime)
#> [1] 2016
month(datetime)
#> [1] 7
mday(datetime)
#> [1] 8

yday(datetime)
#> [1] 190
wday(datetime)
#> [1] 6

Month () ve wday() için, ayın veya haftanın gününün kısaltılmış adını geri almak için label = TRUE ayarını kullanabilirsiniz. Tam adı geri almak için abbr = FALSE ayarını yapın.

month(datetime, label = TRUE)
#> [1] Jul
#> 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec
wday(datetime, label = TRUE, abbr = FALSE)
#> [1] Friday
#> 7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday

Hafta içi hafta sonuna kıyasla daha fazla kalkış olduğunu görmek için wday() kullanabiliriz:

flights_dt %>% 
  mutate(wday = wday(dep_time, label = TRUE)) %>% 
  ggplot(aes(x = wday)) +
    geom_bar()

Ortalama kalkış gecikmelerinin saatlik olarak dakikalarına bakarsak ilginç bir örüntü görüyoruz. 20-30. ve 50-60. dakikalarda kalkan uçuşlar, diğerlerine göre daha az gecikmeye sahip gibi gözüküyor!

flights_dt %>% 
  mutate(minute = minute(dep_time)) %>% 
  group_by(minute) %>% 
  summarise(
    avg_delay = mean(arr_delay, na.rm = TRUE),
    n = n()) %>% 
  ggplot(aes(minute, avg_delay)) +
    geom_line()

İlginçtir ki, scheduled (planlanan) kalkış saatine bakarsak, böyle güçlü bir örüntü görmüyoruz:

sched_dep <- flights_dt %>% 
  mutate(minute = minute(sched_dep_time)) %>% 
  group_by(minute) %>% 
  summarise(
    avg_delay = mean(arr_delay, na.rm = TRUE),
    n = n())

ggplot(sched_dep, aes(minute, avg_delay)) +
  geom_line()

Peki neden bu kalıbı gerçek kalkış zamanlarında görüyoruz? İnsanlar tarafından toplanan birçok veri gibi, “güzel” kalkış saatlerinde yola çıkan uçuşlara karşı güçlü bir önyargı var. İnsan kararını içeren verilerle çalışırken her zaman bu tür bir kalıp için dikkatli olun!

ggplot(sched_dep, aes(minute, n)) +
  geom_line()

16.3.2 Yuvarlama

Bileşenleri ayrı ayrı çizmek için alternatif bir yaklaşım da, tarihi floor_date(), round_date () ve ceiling_date() ile en yakın zaman birimlerine yuvarlamaktır. Her fonksiyon bir tarih vektörü alır ve ardından ünitenin adını aşağıya doğru (floor_date()), yukarı doğru (ceiling_date()) veya en yakın birime yuvarlar. Örneğin bu, haftalık uçuş sayısını belirlememize izin verir:

flights_dt %>% 
  count(week = floor_date(dep_time, "week")) %>% 
  ggplot(aes(week, n)) +
    geom_line()

Yuvarlanmış ve yuvarlanmamış tarihler arasındaki farkın hesaplanması özellikle yararlı olabilir.

16.3.3 Bileşenlerin ayarlanması

Her ek fonksiyonu, tarih / saatin bileşenlerini ayarlamak için de kullanabilirsiniz:

(datetime <- ymd_hms("2016-07-08 12:34:56"))
#> [1] "2016-07-08 12:34:56 UTC"

year(datetime) <- 2020
datetime
#> [1] "2020-07-08 12:34:56 UTC"
month(datetime) <- 01
datetime
#> [1] "2020-01-08 12:34:56 UTC"
hour(datetime) <- hour(datetime) + 1
datetime
#> [1] "2020-01-08 13:34:56 UTC"

Alternatif olarak, yerinde değişiklik yapmak yerine update() fonksiyonu ile yeni bir tarih-saat objesi oluşturabilirsiniz. Bu, aynı anda birden fazla değer ayarlamanıza izin verir.

update(datetime, year = 2020, month = 2, mday = 2, hour = 2)
#> [1] "2020-02-02 02:34:56 UTC"

Değerler çok büyükse, bunlar deveredecektir:

ymd("2015-02-01") %>% 
  update(mday = 30)
#> [1] "2015-03-02"
ymd("2015-02-01") %>% 
  update(hour = 400)
#> [1] "2015-02-17 16:00:00 UTC"

update() fonksiyonunu, uçuşların yıl boyunca her gün için, gün içindeki dağılımını göstermek için kullanabilirsiniz:

flights_dt %>% 
  mutate(dep_hour = update(dep_time, yday = 1)) %>% 
  ggplot(aes(dep_hour)) +
    geom_freqpoly(binwidth = 300)

Bir tarihin büyük bileşenlerini sabit olarak ayarlamak, daha küçük bileşenlerde örüntüleri keşfetmenizi sağlayan güçlü bir tekniktir.

16.3.4 Alıştırmalar

  1. Uçuş zamanlarının bir gün içindeki dağılımı yıl içerisinde nasıl değişiyor?

  2. dep_time, sched_dep_time vedep_delayı karşılaştırın. Tutarlılar mı? Bulgularınızı açıklayın.

  3. air_time ile kalkış ve varış arasındaki süreyi karşılaştırın. Bulgularınızı açıklayın. (İpucu: havaalanının yerini göz önünde bulundurun.)

  4. Ortalama gecikme süresi bir gün içerisinde nasıl değişiyor? dep_timesched_dep_time mı kullanmalısınız? Neden?

  5. Gecikme şansını en aza indirmek için haftanın hangi günü ayrılmalısınız?

  6. diamonds$carat ve flight$sched_dep_time dağılımını benzer yapan nedir?

  7. 20-30 ve 50-60 dakikalardaki uçuşların erken kalkışlarının, planlı uçuşların erken kalkışından kaynaklı olduğuna dair hipotezimi doğrulayın. İpucu: Bir uçuşun ertelenip ertelenmediğini size söyleyen bir ikili değişken oluşturun.

16.4 Zaman aralıkları

Sırada, çıkarma, toplama ve bölme gibi tarihlerle aritmetik işlemlerin nasıl yapıldığını öğreneceksiniz. Bu sırada, zaman aralıklarını temsil eden üç önemli sınıf hakkında bilgi edineceksiniz:

  • tam saniye sayısını gösteren durations (süreler)
  • haftalar ve aylar gibi insan birimlerini temsil eden periods (dönemler)
  • Bir başlangıç ve bitiş noktasını belirten intervals (aralıklar)

16.4.1 Süreler

R’de iki tarihi birbirinden çıkardığınızda, difftime nesnesi alırsınız:

# Hadley kaç yaşında?
h_age <- today() - ymd(19791014)
h_age
#> Time difference of 15605 days

Difftime sınıfından bir nesne saniye, dakika, saat, gün veya hafta gibi bir zaman aralığı kaydeder. Bu belirsizlik difftimes ile çalışmak için biraz acı verici olabilir, bu yüzden lubridate her zaman saniye kullanan bir alternatif sağlar: duration.

as.duration(h_age)
#> [1] "1348272000s (~42.72 years)"

Süreler bir sürü uygun kurucu ile birlikte gelir:

dseconds(15)
#> [1] "15s"
dminutes(10)
#> [1] "600s (~10 minutes)"
dhours(c(12, 24))
#> [1] "43200s (~12 hours)" "86400s (~1 days)"
ddays(0:5)
#> [1] "0s"                "86400s (~1 days)"  "172800s (~2 days)"
#> [4] "259200s (~3 days)" "345600s (~4 days)" "432000s (~5 days)"
dweeks(3)
#> [1] "1814400s (~3 weeks)"
dyears(1)
#> [1] "31557600s (~1 years)"

Süreler daima zaman aralığını saniye cinsinden kaydeder. Daha büyük birimler standart oranlarla dakikaları, saatleri, günleri, haftaları ve yılları saniyelere dönüştürerek oluşturulur (dakikada 60 saniye, saatte 60 dakika, günde 24 saat, haftada 7 gün, günde 365 gün yıl).

Süreleri ekleyebilir ve çarpabilirsiniz:

2 * dyears(1)
#> [1] "63115200s (~2 years)"
dyears(1) + dweeks(12) + dhours(15)
#> [1] "38869200s (~1.23 years)"

Günlere süreleri ekleyebilir ve çıkarabilirsiniz:

tomorrow <- today() + ddays(1)
last_year <- today() - dyears(1)

Bununla birlikte, süreler tam saniye sayısını temsil ettiğinden, bazen beklenmeyen bir sonuç alabilirsiniz:

one_pm <- ymd_hms("2016-03-12 13:00:00", tz = "America/New_York")

one_pm
#> [1] "2016-03-12 13:00:00 EST"
one_pm + ddays(1)
#> [1] "2016-03-13 14:00:00 EDT"

Neden 12 Mart 13:00dan bir gün sonrası, 13 Mart 14:00dır? Tarihe dikkatlice bakarsanız, saat dilimlerinin de değiştiğini fark edebilirsiniz. Gün ışığından yararlanma uygulaması nedeniye, 12 Mart sadece 23 saate sahiptir, bu yüzden eğer tam bir güne eşit miktarda saniye eklersek, değişik bir saat elde ederiz.

16.4.2 Dönemler

Bu sorunu çözmek için, lubridate periods u (dönemleri) sağlar. Dönemler zaman aralığıdır ancak saniye cinsinden belirli bir uzunluğu yoktur, bunun yerine günler ve aylar gibi “insan” zamanları ile çalışırlar. Bu, daha sezgisel bir şekilde çalışmalarını sağlar:

one_pm
#> [1] "2016-03-12 13:00:00 EST"
one_pm + days(1)
#> [1] "2016-03-13 13:00:00 EDT"

Süreler gibi, birkaç yapıcı işlevle dönemler oluşturulabilir.

seconds(15)
#> [1] "15S"
minutes(10)
#> [1] "10M 0S"
hours(c(12, 24))
#> [1] "12H 0M 0S" "24H 0M 0S"
days(7)
#> [1] "7d 0H 0M 0S"
months(1:6)
#> [1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S"
#> [5] "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
weeks(3)
#> [1] "21d 0H 0M 0S"
years(1)
#> [1] "1y 0m 0d 0H 0M 0S"

Dönemleri ekleyebilir ve çarpabilirsiniz:

10 * (months(6) + days(1))
#> [1] "60m 10d 0H 0M 0S"
days(50) + hours(25) + minutes(2)
#> [1] "50d 25H 2M 0S"

Ve elbette onları tarihlere ekleyebilirsiniz. Sürelerle karşılaştırıldığında, sürelerin beklediğiniz şeyi yapması daha olasıdır:

# Artık yıl
ymd("2016-01-01") + dyears(1)
#> [1] "2016-12-31 06:00:00 UTC"
ymd("2016-01-01") + years(1)
#> [1] "2017-01-01"

# Gün ışığından yararlanma zamanı
one_pm + ddays(1)
#> [1] "2016-03-13 14:00:00 EDT"
one_pm + days(1)
#> [1] "2016-03-13 13:00:00 EDT"

Uçuş tarihlerimizle ilgili bir tuhaflığı düzeltmek için periyotları kullanalım. Bazı uçaklar, New York City’den ayrılmadan önce varış yerlerine gelmiş gibi görünüyorlar.

flights_dt %>% 
  filter(arr_time < dep_time) 
#> # A tibble: 10,633 × 9
#>   origin dest  dep_delay arr_delay dep_time            sched_dep_time     
#>   <chr>  <chr>     <dbl>     <dbl> <dttm>              <dttm>             
#> 1 EWR    BQN           9        -4 2013-01-01 19:29:00 2013-01-01 19:20:00
#> 2 JFK    DFW          59        NA 2013-01-01 19:39:00 2013-01-01 18:40:00
#> 3 EWR    TPA          -2         9 2013-01-01 20:58:00 2013-01-01 21:00:00
#> 4 EWR    SJU          -6       -12 2013-01-01 21:02:00 2013-01-01 21:08:00
#> 5 EWR    SFO          11       -14 2013-01-01 21:08:00 2013-01-01 20:57:00
#> 6 LGA    FLL         -10        -2 2013-01-01 21:20:00 2013-01-01 21:30:00
#> # … with 10,627 more rows, and 3 more variables: arr_time <dttm>,
#> #   sched_arr_time <dttm>, air_time <dbl>

Bunlar gece uçuşları. Kalkış ve varış saatleri için aynı tarih bilgilerini kullandık, ancak bu uçuşlar ertesi gün geldi. Bunu, her bir gece uçuşunun varış saatine days(1) ekleyerek düzeltebiliriz.

flights_dt <- flights_dt %>% 
  mutate(
    overnight = arr_time < dep_time,
    arr_time = arr_time + days(overnight * 1),
    sched_arr_time = sched_arr_time + days(overnight * 1)
  )

Şimdi bütün uçuşlarımız fizik yasalarına uyuyor.

flights_dt %>% 
  filter(overnight, arr_time < dep_time) 
#> # A tibble: 0 × 10
#> # … with 10 variables: origin <chr>, dest <chr>, dep_delay <dbl>,
#> #   arr_delay <dbl>, dep_time <dttm>, sched_dep_time <dttm>, arr_time <dttm>,
#> #   sched_arr_time <dttm>, air_time <dbl>, overnight <lgl>

16.4.3 Aralıklar

dyears(1) / ddays(365) ifadesinin sonucunun ne olması gerektiği aşikardır: bir, çünkü süreler her zaman saniye cinsinden temsil edilirler, ve bir yılın süresi 365 günlük saniye olarak tanımlıdır.

Peki ya years(1) / days(1) ifadesinin sonucu ne olmalı? Eğer yıl 2015se, 365 sonucunu vermeli, ancak yıl 2016 ise 366 olmalı! Lubridate’in net bir cevap vermesi için yeterli bilgi yok. Bunun yerine, bir uyarı ile yaklaşık bir değer verir:

years(1) / days(1)
#> [1] 365

Daha doğru bir ölçüm istiyorsanız, interval kullanmanız gerekir. Bir aralık, başlangıç noktası olan bir süredir: bu onu, ne uzunlukta olduğunu net bir şekilde belirleyebilmeniz için kesin kılar.

next_year <- today() + years(1)
(today() %--% next_year) / ddays(1)
#> [1] 365

Bir dönemde kaç periyod olduğunu bulmak için tam sayı bölümü kullanmanız gerekir:

(today() %--% next_year) %/% days(1)
#> [1] 365

16.4.4 Özet

Süre, periyot ve aralıklar arasında nasıl seçim yaparsınız? Her zaman olduğu gibi, probleminizi çözen en basit veri yapısını seçin. Sadece fiziksel zamanı önemsiyorsanız, bir süre (duration) kullanın; insan zamanını eklemeniz gerekirse, bir dönem (period) kullanın; bir sürenin insan birimlerinde ne kadar sürdüğünü bulmanız gerekirse, bir aralık kullanın.

Figür 16.1, farklı veri tipleri arasında izin verilen aritmetik işlemleri özetler.

Tarih/saat sınıflarındaki veri tipleri arasında izin verilen aritmetik işlemler.

Figure 16.1: Tarih/saat sınıflarındaki veri tipleri arasında izin verilen aritmetik işlemler.

16.4.5 Alıştırmalar

  1. Neden months() vardır ama dmonths() yoktur?

  2. Yeni R öğrenmeye başlayan birisine days(overnight * 1) işlemini açıklayın. Nasıl çalışır?

  3. 2015teki her ayın ilk gününü veren bir vektör oluşturun. Şu andaki yılda her ayın ilk gününü veren bir vektör oluşturun.

  4. Doğum gününüzü (tarih olarak) verdiğinizde, yıl olarak kaç yaşında olduğunuzu veren bir fonksiyon yazın.

  5. Neden (today() %--% (today() + years(1)) / months(1) çalışamaz?

16.5 Zaman dilimleri

Zaman dilimleri, jeopolitik varlıklarla etkileşimlerinden dolayı oldukça karmaşık bir konudur. Neyse ki veri analizi için hepsi önemli olmadığından tüm detayları incelememiz gerekmiyor, ancak başa çıkmamız gereken bazı zorluklar var.

İlk zorluk, zaman dilimlerinin günlük isimlerinin belirsiz olma eğilimi göstermesidir. Örneğin, Amerikalıysanız, muhtemelen EST veya Eastern Standard Time (Doğu standart zamanı) ile aşinasınızdır. Ancak hem Avustralya hem de Kanada’da EST var! Karışıklığı önlemek için, R uluslararası standart IANA zaman dilimlerini kullanır. Bunlar, genelde “<kıta>/<şehir>” (“<continent>/<city>” ) biçiminde, “/” (“/”) şeklinde tutarlı bir adlandırma şeması kullanır (birkaç istisna var çünkü her ülke bir kıtada değil). Örnekler arasında “America/New_York”, “Europe/Paris”, ve “Pacific/Auckland” sayılabilir.

Zaman dilimlerini tipik olarak ülke ya da ülke içerisindeki bölgeyle ilişkili olarak düşündüğünüzden, zaman dilimlerinin neden şehri kullandığını merak edebilirsiniz. Bunun sebebi IANA veritabanının onyıllarca geçerli zaman dilimi kuralları kaydetmesinin gerekmesidir. Yıllar içinde, ülkeler oldukça sık isim değiştirir (ya da parçalanır), ancak şehir isimleri aynı kalma eğilimindedir. Diğer bir problem ise ismin sadece şu anki durumu değil ancak tüm geçmişi yansıtmasının gerekliliğidir. Örneğin, hem “America/New_York” hem de “America/Detroit” için zaman dilimi vardır. Bu iki şehir de şu anda EST kullanmaktadır ancak 1962-1972 tarihlerinde Michigan (Detroit’in bulunduğu eyalet), gün ışığından yararlanma saatini kullanmıyordu, bu yüzden farklı bir isme ihtiyaç vardır. Sadece bu hikayelerden bazılarını okumak için http://www.iana.org/time-zones adresinde bulunan ham zaman dilimi veritabanı okunmaya değer.

R’nin şu anki saat diliminizin ne olduğunu düşündüğünü Sys.timezone() ile öğrenebilirsiniz:

Sys.timezone()
#> [1] "UTC"

(R bilmiyorsa, NA sonucunu alırsınız.)

Ve OlsonNames() ile tüm zaman dilimi adlarının tam listesine bakabilirsiniz:

length(OlsonNames())
#> [1] 595
head(OlsonNames())
#> [1] "Africa/Abidjan"     "Africa/Accra"       "Africa/Addis_Ababa"
#> [4] "Africa/Algiers"     "Africa/Asmara"      "Africa/Asmera"

R’da, zaman dilimi, tarih-saatin sadece yazdırmayı kontrol eden bir niteliğidir. Örneğin, bu üç nesne zaman içinde aynı anı temsil eder:

(x1 <- ymd_hms("2015-06-01 12:00:00", tz = "America/New_York"))
#> [1] "2015-06-01 12:00:00 EDT"
(x2 <- ymd_hms("2015-06-01 18:00:00", tz = "Europe/Copenhagen"))
#> [1] "2015-06-01 18:00:00 CEST"
(x3 <- ymd_hms("2015-06-02 04:00:00", tz = "Pacific/Auckland"))
#> [1] "2015-06-02 04:00:00 NZST"

Çıkarma kullanarak aynı saatte olduklarını doğrulayabilirsiniz:

x1 - x2
#> Time difference of 0 secs
x1 - x3
#> Time difference of 0 secs

Aksi belirtilmedikçe, lubridate her zaman UTC kullanır. UTC (Eş Güdümlü Evrensel Zaman), bilimsel topluluk tarafından kullanılan ve bir önceki versiyon olan GMT’ye (Greenwich Mean Time) kabaca eşdeğer bir standart zaman dilimidir. Gün ışığından yararlanma zamanlarından etkilenmez, bu da hesaplama için onu uygun kılar. Tarih-saatleri birleştiren c() gibi işlemler genelde saat dilimini düşürür. Bu durumda, saat-tarih yerel saat diliminde gösterilecektir.

x4 <- c(x1, x2, x3)
x4
#> [1] "2015-06-01 12:00:00 EDT" "2015-06-01 12:00:00 EDT"
#> [3] "2015-06-01 12:00:00 EDT"

Saat dilimini iki yolla değiştirebilirsiniz:

  • Zamandaki anı sabit tutun ve nasıl gösterildiğini değiştirin. Bunu an doğru olduğunda ancak daha doğal bir gösterim istediğinizde kullanın.

    x4a <- with_tz(x4, tzone = "Australia/Lord_Howe")
    x4a
    #> [1] "2015-06-02 02:30:00 +1030" "2015-06-02 02:30:00 +1030"
    #> [3] "2015-06-02 02:30:00 +1030"
    x4a - x4
    #> Time differences in secs
    #> [1] 0 0 0

    (Bu aynı zamanda zaman dilimlerinin başka bir zorluğunu daha göstermektedir: hepsi tamsayı değildir!)

  • Zamanın altında yatan anı değiştirin. Bunu yanlış bir zaman dilimiyle etiketlenmiş bir anınız varsa ve bunu düzeltmeniz gerekiyorsa kullanın.

    x4b <- force_tz(x4, tzone = "Australia/Lord_Howe")
    x4b
    #> [1] "2015-06-01 12:00:00 +1030" "2015-06-01 12:00:00 +1030"
    #> [3] "2015-06-01 12:00:00 +1030"
    x4b - x4
    #> Time differences in hours
    #> [1] -14.5 -14.5 -14.5