2015-11-23 12 views
6

Sto cercando di ottenere un codice che guarderà una data e quindi assegnarla a un anno fiscale. Sono completamente bloccato.Assegnazione delle date per l'anno fiscale

Ho una variabile che contiene le date in formato POSIXct:

df$Date 
#2015-05-01 CST 
#2015-04-30 CST 
#2014-09-01 CST 

cosa ho bisogno di essere in grado di fare è prendere quelle date e tornare un anno fiscale, che va da maggio 1 - 30 aprile per Ad esempio, l'anno fiscale 2016 è in corso dal 2015-05-01 al 2016-04-30. I risultati sarebbero simili a questo:

df$Date    df$FiscalYear 
#2015-05-01 CST  #FY2016 
#2015-04-30 CST  #FY2015 
#2014-09-01 CST  #FY2015 

C'è un modo semplice per farlo?

+0

Possibile duplicato? http://stackoverflow.com/questions/26684514/how-to-calculate-mean-by-date-grouped-as-fiscal-quarters –

+0

Non trovo questa soluzione particolarmente chiara - O non sono intelligente abbastanza per capirlo Probabilmente quest'ultimo. –

risposta

10

Ecco alcune alternative. Restituiscono tutti gli anni numerici, ma se hai davvero bisogno di una stringa che inizia con FY, allora utilizza paste0("FY", result) dove result è uno dei risultati qui sotto. Tutti supportano l'input vettoriale, ovvero l'input dates può essere un vettore.

1) zoo :: as.yearmon Il pacchetto zoo ha una classe che rappresenta "yearmon" anno/mesi dell'anno + frazione dove frazione = 0 per gennaio, 1/12 per febbraio, 2/12 per la marcia e così sopra.

Usando questo one-liner lo farà. Sottrae 4/12 (da aprile è fine anno) e aggiunge 1 (cioè aggiungi un anno). Quindi per ottenere il convertito anno a numerico e prendere la parte intera:

library(zoo) 

as.numeric(as.yearmon(dates) - 4/12 + 1) %/% 1 
## [1] 2016 2015 2015 

2) POSIXlt Ecco una soluzione che non utilizza alcun pacchetto. Converti le date nella classe POSIXlt. Il componente mo rappresenta Jan come 0, Feb 1, ecc. Quindi se siamo maggio o successivo (mo è 4 o più), l'anno fiscale è il seguente anno solare, altrimenti è l'anno solare corrente. La componente year degli oggetti POSIXlt è il numero di anni a partire dal 1900 in modo da aggiungere l'anno 1900 più 1 se siamo a maggio o poi:

lt <- as.POSIXlt(dates) 
lt$year + (lt$mo >= 4) + 1900 
## [1] 2016 2015 2015 

3) formato Aggiungi l'anno a 1 se il mese è maggiore o uguale a 5 (oa zero se non). Questo utilizza anche senza pacchetti:

as.numeric(format(dates, "%Y")) + (format(dates, "%m") >= "05") 
## [1] 2016 2015 2015 

4) substr. Possiamo estrarre l'anno utilizzando substr, convertire in numerico e aggiungere 1 se il mese estratto (estratto anche utilizzando substr) è "05" o superiore .; Ancora nessun pacchetto è usato.

as.numeric(substr(dates, 1, 4)) + (substr(dates, 6, 7) >= "05") 
## [1] 2016 2015 2015 

5) read.table usa questo anche senza pacchetti.

with(read.table(text = format(dates), sep = "-"), V1 + (V2 >= 5)) 
## [1] 2016 2015 2015 

Nota: Abbiamo usato questo come l'ingresso dates:

dates <- as.Date(c("2015-05-01", "2015-04-30", "2014-09-01")) 
+0

aggiunto (2), (3) e (4) –

+1

(3) è molto leggibile – chinsoon12

+0

Grazie GG. L'equivalente britannico è penso 'as.numeric (formato (date,"% Y ")) - (formato (date,"% m ") <=" 03 ")' – geotheory

7

È possibile utilizzare seq con gli oggetti POSIXct per generare un elenco dei "punti di divisione" o 1 ° giorno dell'anno fiscale per gli anni che abbracciano i dati, quindi utilizzare findInterval per calcolare quale delle intervalli una data particolare cade in:

> dates <- as.POSIXct(c('2015-05-01','2015-04-30','2014-09-01')) 
> fy.tmp <- seq(as.POSIXct('2000-05-01'), length=25, by='year') 
> fiscalYear <- (2001:2025)[ findInterval(dates,fy.tmp) ] 
> fiscalYear 
[1] 2016 2015 2015 

si potrebbe anche utilizzare la funzione invece di findIntervalcut se si desidera un fattore come il risultato.

+0

Testato questo e funziona benissimo. Grazie! –

0

provare a modificare questa:

Federal.FY <- function(x,firstMonth=10, # I've altered this line to follow the federal fiscal year, October 
         fy.prefix='FY', 
         quarter.prefix='Q', 
         sep='-', 
         level.range=c(min(x), max(x))) {if(level.range[1] > min(x) | level.range[2] < max(x)) { 
warning(paste0('The range of x is greater than level.range. Values ', 
       'outside level.range will be returned as NA.'))} 
quarterString <- function(d) { 
year <- as.integer(format(d, format='%Y')) 
month <- as.integer(format(d, format='%m')) 
y <- ifelse(firstMonth > 1 & month >= firstMonth, year+1, year) 
q <- cut((month - firstMonth) %% 12, breaks=c(-Inf,2,5,8,Inf), 
      labels=paste0(quarter.prefix, 1:4)) 
return(paste0(fy.prefix, y, sep, q))} 
vals <- quarterString(x) 
levels <- unique(quarterString(seq(
as.Date(format(level.range[1], '%Y-%m-01')), 
as.Date(format(level.range[2], '%Y-%m-28')), by='month'))) 
return(factor(vals, levels=levels, ordered=TRUE))} 

d <- as.Date("2016-10-02") 
Federal.FY(d) 
##[1] FY2017-Q1 
##Levels: FY2017-Q1 
3

Aggiungendo alla bella risposta di G. Grothendieck. Con lubridate:

year(dates) + (month(dates) >= 5)