#r #date
#r #Дата
Вопрос:
У меня есть дата в P
date = as.Date("2011-02-23", "%Y-%m-%d")
Возможно ли узнать количество дней месяца на эту конкретную дату? (Относительно leapyears). В PHP это выглядело бы примерно так (http://www.php.net/manual/en/function.date.php ):
days = format(date, "%t")
но «%t», похоже, имеет другое значение в R. Есть ли решение для этой проблемы?
Ответ №1:
lubridate
пакет имеет обязательную функцию «days_in_month» по отношению к leapyears.
Ваш пример:
date <- as.Date("2011-02-23", "%Y-%m-%d")
lubridate::days_in_month(date)
# Feb
# 28
Високосный год (2016):
date_leap <- as.Date("2016-02-23", "%Y-%m-%d")
lubridate::days_in_month(date_leap)
# Feb
# 29
Ответ №2:
В библиотеке Hmisc есть пара полезных функций для этого:
require(Hmisc)
monthDays(as.Date('2010-01-01'))
Ответ №3:
Для этого можно написать простую функцию:
numberOfDays <- function(date) {
m <- format(date, format="%m")
while (format(date, format="%m") == m) {
date <- date 1
}
return(as.integer(format(date - 1, format="%d")))
}
Вызвать как:
> date = as.Date("2011-02-23", "%Y-%m-%d")
> numberOfDays(date)
[1] 28
> date # date is unchanged
[1] "2011-02-23"
Комментарии:
1. Я не уверен, что это работает, если переданная дата является первой в месяце.
2. Не работает для векторов … ознакомьтесь с моим методом ниже
Ответ №4:
Это так же просто, как вычислить разницу между двумя датами — поэтому сделайте это первым числом месяца и следующим месяцем:
R> difftime( as.Date("2011-06-01"), as.Date("2011-05-01") )
Time difference of 31 days
R> as.numeric(difftime( as.Date("2011-06-01"), as.Date("2011-05-01") ))
[1] 31
R>
as.numeric()
Преобразует это в число, которое вы можете использовать.
Ответ №5:
Вот несколько подходов. Оба подхода являются векторизованными, т. е. входными x
могут быть даты одного "Date"
класса или вектор таких дат.
1) Это преобразует входную дату x
в "yearmon"
объект, а затем преобразует ее обратно в последнее число месяца и первое число месяца и вычитает два, добавляя 1.
x <- Sys.Date() # use today as the test date
library(zoo)
ym <- as.yearmon(x)
as.Date(ym, frac = 1) - as.Date(ym) 1
## Time difference of 30 days
или для числового результата используйте:
as.numeric(as.Date(ym, frac = 1) - as.Date(ym) 1)
## [1] 30
1a) Вариант может быть:
as.Date(ym 1/12) - as.Date(ym)
## Time difference of 30 days
или для числового результата:
as.numeric(as.Date(ym 1/12) - as.Date(ym))
## [1] 30
2) Это базовое решение. Сначала мы определяем, fom
который вводит "Date"
вектор и возвращает первое число месяца каждого компонента. Теперь мы просто берем разницу между первым числом следующего месяца и первым числом текущего месяца.
fom <- function(x) as.Date(cut(x, "month"))
fom1 <- fom(x)
fom2 <- fom(fom1 32)
as.numeric(fom2 - fom1)
## [1] 30
Комментарии:
1. 1 Я не знал о
frac
аргументеas.Date()
— где это задокументировано? Это не отображается, когда я это делаю?as.Date
.2. @Prasad, попробуйте это:
library(zoo); ?as.Date.yearmon
.
Ответ №6:
Вот простой способ: для n
в 28:31
найдите наибольшее число, в результате которого получается действительная дата. В моих тестах это как минимум в 4 раза быстрее, чем любой из методов, основанных на разнице во времени:
ndays <- function(d) {
last_days <- 28:31
rev(last_days[which(!is.na(
as.Date( paste( substr(d, 1, 8),
last_days, sep = ''),
'%Y-%m-%d')))])[1]
}
> ndays('1999-03-10')
[1] 31
> ndays('1999-04-10')
[1] 30
> ndays('2000-02-10')
[1] 29
Сравнение сроков с некоторыми другими предлагаемыми здесь методами:
> system.time( replicate( 5000,
nd <- {
ym <- as.yearmon('2011-06-01');
as.numeric( as.Date(ym, frac = 1) - as.Date(ym) 1)
}))
user system elapsed
16.634 1.807 18.238
> system.time( replicate( 5000,
nd <- as.numeric( difftime( as.Date("2011-06-01"),
as.Date("2011-05-01") ))))
user system elapsed
3.137 0.341 3.470
> system.time( replicate( 5000, nd <- ndays('2011-06-01')))
user system elapsed
0.729 0.044 0.771
Комментарии:
1. Если вы делаете это только в одну дату, как в примере выше, не имеет значения, какова скорость, тогда как если вы делаете это во многих датах, то
yearmon
подход быстрее, поскольку он уже векторизован. Попробуйте рассчитать времяd <- Sys.Date() 1:100; sapply(d, ndays)
по сравнениюd <- Sys.Date() 1:100; ym <- as.yearmon(d); as.numeric(as.Date(ym, frac = 1) - as.Date(ym) 1)
В моей системе последнее выполняется в 5 раз быстрее. Тем не менее, скорость обычно неважна в любом случае. Простотаyearmon
решения кажется более важным преимуществом.2. @G.Grothendieck Очень хорошее замечание, я сравнил время и согласен с вами.
Ответ №7:
Вот еще одна возможная функция, которая не требует установки каких-либо пакетов. Вы просто передаете функции объект date. Поскольку здесь есть много других отличных ответов, я написал это с прицелом на то, чтобы быть достаточно простым и (надеюсь) легким для чтения 🙂
daysInMonth <- function(d = Sys.Date()){
m = substr((as.character(d)), 6, 7) # month number as string
y = as.numeric(substr((as.character(d)), 1, 4)) # year number as numeric
# Quick check for leap year
leap = 0
if ((y %% 4 == 0 amp; y %% 100 != 0) | y %% 400 == 0)
leap = 1
# Return the number of days in the month
return(switch(m,
'01' = 31,
'02' = 28 leap, # adds 1 if leap year
'03' = 31,
'04' = 30,
'05' = 31,
'06' = 30,
'07' = 31,
'08' = 31,
'09' = 30,
'10' = 31,
'11' = 30,
'12' = 31))
}
Комментарии:
1. Если вы выполните проверку leap
y %% 4 == 0 amp; (y %% 100 != 0 | y %% 400 == 0)
, это всегда будет работать.
Ответ №8:
По сути, это подход Дирка, но на самом деле он помещен в функцию, чтобы проверить, когда следующий месяц также относится к следующему году; и простое вычитание дат аналогично использованию difftime()
:
numberOfDays <- function(d){
temp <- unlist(strsplit(as.character(d),"-"))
begin <- as.Date(paste(temp[1],temp[2],"01",sep="-"))
if (temp[2] != "12"){
nextMonth <- as.character(as.integer(temp[2]) 1)
end <- as.Date(paste(temp[1],nextMonth,"01",sep="-"))
return(as.integer(as.Date(end) - as.Date(begin)))
}
else{
nextYear <- as.character(as.integer(temp[1]) 1)
end <- as.Date(paste(nextYear,"01","01",sep="-"))
return(as.integer(as.Date(end) - as.Date(begin)))
}
}
Ответ №9:
Вы можете передать год или взять текущий год по умолчанию:
days.in.month = function(month, year = NULL){
month = as.integer(month)
if (is.null(year))
year = as.numeric(format(Sys.Date(), '%Y'))
dt = as.Date(paste(year, month, '01', sep = '-'))
dates = seq(dt, by = 'month', length = 2)
as.numeric(difftime(dates[2], dates[1], units = 'days'))
}
Ответ №10:
Я предоставляю еще один однострочный файл, дополнительные пакеты не требуются:
NumberOfDays <- function(date)
return(as.numeric(format(as.Date(paste0(format(date,format="%Y"),formatC(ifelse(format(date,format="%m")=="12",0,as.numeric(format(date,format="%m"))) 1,width=2,format="d",flag="0"),"01"),"%Y%m%d")-1,format="%d")))
> NumberOfDays(as.Date("2015-02-14","%Y-%m-%d"))
[1] 28
> system.time(NumberOfDays(as.Date("2015-02-14","%Y-%m-%d")))
user system elapsed
0.0010000000 0.0000000000 0.0009999999
Вы должны предоставить этой функции объект Date с форматированием по вашим собственным предпочтениям.
Ответ №11:
Привет, другим способом получения дней может быть:
# Any date
Today <- Sys.Date()
# Get Month and Year
Month <- format(Today,"%m")
Year <- format(Today,"%Y")
# get first date for the next month and then subtract 1
Days <- format(
as.Date(
paste0("01/",(as.numeric(Month)%%12) 1,"/",Year)
,"%d/%m/%Y")-1,"%d")
Ответ №12:
> Ymd<-"201602"
> lastDate<-format(seq(as.POSIXct(sprintf("%s%s",substr(Ymd,1,6),"01"),format="%Y%m%d"),length=2,by="1 month")-1,"%Y%m%d")[2]
> cat(as.integer(lastDate)%%100)
29
# Other Output
> lastDate
[1] "20160229"
> cat(substr(lastDate,7,8))
29
Комментарии:
1. Ответы «Только код» не обеспечивают хорошего решения проблемы. Пожалуйста, попробуйте объяснить, что вы сделали, и добавить информацию.
Ответ №13:
Это другое альтернативное решение, использующее yearmon от zoo. Это быстрая утилита.
require(zoo)
days_in_month <- function(d) {
rigor_days_in_month <- function(d) {
get_day <- function(x) as.numeric(format(x,"%d"))
m <- zoo::as.yearmon(d)
get_day(as.Date(m, frac = 1))
}
mm <- as.numeric(format(d,"%m")) # get_month
ifelse(mm %in% c(1,3,5,7,8,10,12), 31,
ifelse(mm != 2, 30,
rigor_days_in_month(d)))
}
Вы можете развернуть get_day(), чтобы преобразовать его в однострочный формат, если хотите.
Ответ №14:
Да!
Вам просто нужно использовать функцию days_in_month()
из lubridate
пакета. Следуйте краткому примеру, как вы можете использовать функцию для указания дней в месяце.
lubridate::days_in_month(as.Date("2018-02-01"))
Вывод:
28 февраля
Для получения дополнительной информации об этой функции: нажмите здесь
Ответ №15:
Я опаздываю на вечеринку, но эта функция намного лучше и поддерживает векторы:
month_days <- function(mon) {
# input as class "Date"
require(lubridate)
day(mon) <- 1
given <- mon
month(given) <- month(given) 1
as.numeric(given - mon)
}