Rで(年の)週番号に日付を変換する関数を探して、パッケージdata.table
からweek
を探しました。しかし、私はいくつかの奇妙な行動を観察しました:
> week("2014-03-16") # Sun, expecting 11
[1] 11
> week("2014-03-17") # Mon, expecting 12
[1] 11
> week("2014-03-18") # Tue, expecting 12
[1] 12
火曜日に月曜日ではなく週番号が12に切り替わるのはなぜですか?私は何が欠けていますか? (タイムゾーンは日付があるだけなので、関係ないはずです!)
(ベース)R関数に関するその他の提案も歓迎します。
基本パッケージ
関数strftime
を使用して、引数%V
を渡して、ISO 8601で定義されているように、年の週を10進数(01〜53)で取得します。
strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%V")
出力:
[1] "11" "12" "12" "01"
潤滑油を使用する場合:
library(lubridate)
lubridate::week(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01'))
[1] 11 11 12 1
パターンは同じです。 isoweek
を試してください
lubridate::isoweek(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01'))
[1] 11 12 12 1
実際、week(...)
関数のバグ、または少なくともドキュメントのエラーを発見したかもしれません。うまくいけば、誰かが飛び込んで、なぜ私が間違っているのかを説明してくれるでしょう。
コードを見る:
_library(lubridate)
> week
function (x)
yday(x)%/%7 + 1
<environment: namespace:lubridate>
_
ドキュメントの状態:
週は、日付から1月1日までに発生した完全な7日間の期間に1を足した数です。
ただし、1月1日は年の最初の日(ゼロ日ではない)なので、最初の「週」は6日間になります。コードは(??)
_(yday(x)-1)%/%7 + 1
_
NB:_data.table
_パッケージでweek(...)
を使用していますが、これは_lubridate::week
_と同じコードですが、効率のためにすべてを数値ではなく整数に強制します。したがって、この関数には同じ問題があります(??)。
年を使用して週番号を取得する場合は、"%Y-W%V"
:
e.g yearAndweeks <- strftime(dates, format = "%Y-W%V")
そう
> strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%Y-W%V")
になる:
[1] "2014-W11" "2014-W12" "2014-W12" "2014-W01
"
問題は、week
計算が何らかの形で年の最初の日を使用することだと思います。私は内部の仕組みを理解していませんが、この例で私が意味することを見ることができます:
library(data.table)
dd <- seq(as.IDate("2013-12-20"), as.IDate("2014-01-20"), 1)
# dd <- seq(as.IDate("2013-12-01"), as.IDate("2014-03-31"), 1)
dt <- data.table(i = 1:length(dd),
day = dd,
weekday = weekdays(dd),
day_rounded = round(dd, "weeks"))
## Now let's add the weekdays for the "rounded" date
dt[ , weekday_rounded := weekdays(day_rounded)]
## This seems to make internal sense with the "week" calculation
dt[ , weeknumber := week(day)]
dt
i day weekday day_rounded weekday_rounded weeknumber
1: 1 2013-12-20 Friday 2013-12-17 Tuesday 51
2: 2 2013-12-21 Saturday 2013-12-17 Tuesday 51
3: 3 2013-12-22 Sunday 2013-12-17 Tuesday 51
4: 4 2013-12-23 Monday 2013-12-24 Tuesday 52
5: 5 2013-12-24 Tuesday 2013-12-24 Tuesday 52
6: 6 2013-12-25 Wednesday 2013-12-24 Tuesday 52
7: 7 2013-12-26 Thursday 2013-12-24 Tuesday 52
8: 8 2013-12-27 Friday 2013-12-24 Tuesday 52
9: 9 2013-12-28 Saturday 2013-12-24 Tuesday 52
10: 10 2013-12-29 Sunday 2013-12-24 Tuesday 52
11: 11 2013-12-30 Monday 2013-12-31 Tuesday 53
12: 12 2013-12-31 Tuesday 2013-12-31 Tuesday 53
13: 13 2014-01-01 Wednesday 2014-01-01 Wednesday 1
14: 14 2014-01-02 Thursday 2014-01-01 Wednesday 1
15: 15 2014-01-03 Friday 2014-01-01 Wednesday 1
16: 16 2014-01-04 Saturday 2014-01-01 Wednesday 1
17: 17 2014-01-05 Sunday 2014-01-01 Wednesday 1
18: 18 2014-01-06 Monday 2014-01-01 Wednesday 1
19: 19 2014-01-07 Tuesday 2014-01-08 Wednesday 2
20: 20 2014-01-08 Wednesday 2014-01-08 Wednesday 2
21: 21 2014-01-09 Thursday 2014-01-08 Wednesday 2
22: 22 2014-01-10 Friday 2014-01-08 Wednesday 2
23: 23 2014-01-11 Saturday 2014-01-08 Wednesday 2
24: 24 2014-01-12 Sunday 2014-01-08 Wednesday 2
25: 25 2014-01-13 Monday 2014-01-08 Wednesday 2
26: 26 2014-01-14 Tuesday 2014-01-15 Wednesday 3
27: 27 2014-01-15 Wednesday 2014-01-15 Wednesday 3
28: 28 2014-01-16 Thursday 2014-01-15 Wednesday 3
29: 29 2014-01-17 Friday 2014-01-15 Wednesday 3
30: 30 2014-01-18 Saturday 2014-01-15 Wednesday 3
31: 31 2014-01-19 Sunday 2014-01-15 Wednesday 3
32: 32 2014-01-20 Monday 2014-01-15 Wednesday 3
i day weekday day_rounded weekday_rounded weeknumber
私の回避策はこの関数です: https://github.com/geneorama/geneorama/blob/master/R/round_weeks.R
round_weeks <- function(x){
require(data.table)
dt <- data.table(i = 1:length(x),
day = x,
weekday = weekdays(x))
offset <- data.table(weekday = c('Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday'),
offset = -(0:6))
dt <- merge(dt, offset, by="weekday")
dt[ , day_adj := day + offset]
setkey(dt, i)
return(dt[ , day_adj])
}
もちろん、月曜日を最初にするなど、オフセットを簡単に変更できます。これを行う最良の方法は、オフセットにオフセットを追加することですが、まだそれを行っていません。
単純なジェネララパッケージへのリンクを提供しましたが、変更される可能性が高く、あまり文書化されていないため、あまり頼りすぎないでください。
特定の状況ではパッケージの必要性を理解していますが、基本言語は非常に洗練されており、実証済みです(そしてデバッグおよび最適化されています)。
何故なの:
dt <- as.Date("2014-03-16")
dt2 <- as.POSIXlt(dt)
dt2$yday
[1] 74
そして、年の最初の週がゼロ(Cのインデックス付けの場合)か1(Rのインデックス付けの場合)かを選択します。
学習、更新、バグの心配をするパッケージはありません。
年の週番号を取得する場合、strftimeを使用したGrant Shannonのソリューションは機能しますが、1月1日前後の日付を修正する必要があります。たとえば、2016-01-03(yyyy-mm-dd)は2016年ではなく2015年の53週目です。そして2018-12-31は2018年ではなく2019年の1週目です。このコードはいくつかの例と解決策を提供します。 「yearweek」列では年が間違っている場合があり、「yearweek2」では修正されています(行2および5)。
library(dplyr)
library(lubridate)
# create a testset
test <- data.frame(matrix(data = c("2015-12-31",
"2016-01-03",
"2016-01-04",
"2018-12-30",
"2018-12-31",
"2019-01-01") , ncol=1, nrow = 6 ))
# add a colname
colnames(test) <- "date_txt"
# this codes provides correct year-week numbers
test <- test %>%
mutate(date = as.Date(date_txt, format = "%Y-%m-%d")) %>%
mutate(yearweek = as.integer(strftime(date, format = "%Y%V"))) %>%
mutate(yearweek2 = ifelse(test = day(date) > 7 & substr(yearweek, 5, 6) == '01',
yes = yearweek + 100,
no = ifelse(test = month(date) == 1 & as.integer(substr(yearweek, 5, 6)) > 51,
yes = yearweek - 100,
no = yearweek)))
# print the result
print(test)
date_txt date yearweek yearweek2
1 2015-12-31 2015-12-31 201553 201553
2 2016-01-03 2016-01-03 201653 201553
3 2016-01-04 2016-01-04 201601 201601
4 2018-12-30 2018-12-30 201852 201852
5 2018-12-31 2018-12-31 201801 201901
6 2019-01-01 2019-01-01 201901 201901
ベースのみを使用して、次の関数を作成しました。
注意:
ニーズに合わせて微調整します。
findWeekNo <- function(myDate){
# Find out the start day of week 1; that is the date of first Mon in the year
weekday <- switch(weekdays(as.Date(paste(format(as.Date(myDate),"%Y"),"01-01", sep = "-"))),
"Monday"={1},
"Tuesday"={2},
"Wednesday"={3},
"Thursday"={4},
"Friday"={5},
"Saturday"={6},
"Sunday"={7}
)
firstMon <- ifelse(weekday==1,1, 9 - weekday )
weekNo <- floor((as.POSIXlt(myDate)$yday - (firstMon-1))/7)+1
return(weekNo)
}
findWeekNo("2017-01-15") # 2