How to Account for Leap Years

How to account for leap years?

You can check if a year is a leap year with leap_year from lubridate.

years <- 1895:2005
years[leap_year(years)]

This package will also handle not generating impossible 29ths of February.

ymd("2000-2-29") + years(1)    # NA
ymd("2000-2-29") %m+% years(1) # "2001-02-28"

The %m+% "add months" operator, as mentioned by @VitoshKa, rolls the date back to the end of the previous month if the actual day doesn't exist.

Does Pandas account for leap years when calculating dates

Yes, it does. However, your conversion from years to days is already ignoring the leap years. You can multiply by 365.25 (365.242, as suggested in the comments) which gives better results.

You can check the accuracy of the results on wolfram alpha: https://www.wolframalpha.com/input/?i=148.328971+years++from+01%2F01%2F2000

In addition, you can use pandas DateOffset with years. However, currently only integer values are supported.

import pandas as pd
start = "01/01/2000"
end = pd.to_datetime(start) + pd.DateOffset(years =148, days =0.328971*365.242)
print(end)

# 2148-04-30 03:45:35.229600

It seems to work well but misses by few hours.

How to replicate one year daily data to multiple years taking into account leap years?

Edit: Added output for 2014-18.

Here's a function to help with that. Feed in year and day_num (for that year), and it outputs the standard values for that date. I assume that you want April 1 in a leap year to output April 1 from the standard year, which requires shifting from day 92 (in the leap year) to day 91 in a 365-day year.

daily_value <- function(year, day_num) {
leap <- year %in% c(2008, 2012, 2016, 2020, 2024)
leap_day_val <- 0.5 * (mydata[59,2] + mydata[60,2])
day_num_adj <- day_num + ifelse(leap & day_num >= 61, -1, 0)
day_value <- ifelse(leap & day_num == 60,
leap_day_val,
mydata[day_num_adj,2])
day_value
}

Tests

mydata[59,]
# myday myvalue
#59 59 0.5697196
daily_value(2016,59)
#[1] 0.5697196

mydata[59:60,]
# myday myvalue
#59 59 0.5697196
#60 60 -0.1350546
mean(c(0.5697196, -0.1350546))
#[1] 0.2173325
daily_value(2016,60)
#[1] 0.2173325

# Day 61 of 2016 was March 1, which is day 60 in years with 365 days
mydata[60,]
# myday myvalue
#60 60 -0.1350546
daily_value(2016,61)
#[1] -0.1350546

Now, we can apply that to all the days in 2014-18:

output <- data.frame(dates = seq.Date(as.Date("2014-01-01"), as.Date("2018-12-31"), 1))
output$day_of_year = lubridate::yday(output$dates)
output$value = daily_value(lubridate::year(output$dates), output$day_of_year)

subset(output, day_of_year > 58 & day_of_year <= 61)
# dates day_of_year value
#59 2014-02-28 59 0.5697196
#60 2014-03-01 60 -0.1350546
#61 2014-03-02 61 2.4016178

#424 2015-02-28 59 0.5697196
#425 2015-03-01 60 -0.1350546
#426 2015-03-02 61 2.4016178

#789 2016-02-28 59 0.5697196
#790 2016-02-29 60 0.2173325 # Leap day gets avg of 2/28 and 3/01
#791 2016-03-01 61 -0.1350546 # Rest of leap year shifted back one day

#1155 2017-02-28 59 0.5697196
#1156 2017-03-01 60 -0.1350546
#1157 2017-03-02 61 2.4016178

#1520 2018-02-28 59 0.5697196
#1521 2018-03-01 60 -0.1350546
#1522 2018-03-02 61 2.4016178

How do I account for leap years and months with 30 days in custom date picker drop downs?

The JavaScript Date object has all the capabilities of determining the number of days in a given month and year.

Here's a CodeSandbox Demonstration showing leap years and the number of days for a selected month and year.

How do I account for leap years when mapping to an Ordinal Scale in d3?

One way to do it is to ensure that x.domain() is set to an array containing every date in a typical leap year. I.e. the domain should be

["01-01", "01-02", ... , "02-28", "02-29", "03-01", ... "12-31"]

It would mean that every charted year will include the leap day — even non-leap years, in which case that leap day would be bar-less. I'm not sure whether that acceptable for you, but seems reasonable to me.

In order to construct this kind of domain, you need to loop over every day of a leap year (2016 is as good as any) and append it to an array:

var leapYearDays = [];// will be populated with all dates
var currentDate = new Date(2016, 0, 1);// the date we'll be incrementing
var safety = 0;// helps prevent infinite looping during development

while(safety < 400 && currentDate.getFullYear() == 2016) {
leapYearDays.push(formatMth(currentDate));
currentDate.setDate(currentDate.getDate() + 1);
safety++;
}

x.domain(leapYearDays);// apply as the x domain

Updated blockbuilder here.

Counting days of the year with leap years

You could use the lubridate's leap_year function. E.g.,

library(lubridate)
dates <- c(as.Date("2017-12-31"), as.Date("2016-12-31"))
y <- as.Date("2016-12-31")
z <- as.Date("2017-12-31")
leap_every_year <- function(x) {
ifelse(yday(x) > 59 & leap_year(x) == FALSE, yday(x) + 1, yday(x))
}
leap_every_year(y)
[1] 366
leap_every_year(z)
[1] 366
leap_every_year(dates)
[1] 366 366

EDIT: saw this was very similar to @MDEWITT's solution but it uses lubridate instead. Similar ideas though. Good luck!



Related Topics



Leave a reply



Submit