2011-12-07 9 views
10

Oltre alla convalida incrociata, ho chiesto un question sull'analisi dei dati per data, ma non volendo generare picchi e spurie spurie dividendo i dati entro il mese. Ad esempio, se si paga una bolletta l'ultimo giorno di ogni mese, ma in un'occasione si paga un paio di giorni di ritardo, allora il mese rifletterà la spesa zero e il mese successivo rifletterà il doppio della spesa usuale. Tutta roba spuria.Come ottengo le pendenze di un'interpolazione a intervalli regolari su un grafico a somma cumulativa?

Uno dei answers alla mia domanda ha spiegato il concetto di interpolazione utilizzando il livellamento lineare della spline sulla somma cumulativa per superare i singhiozzi nel binning. Sono incuriosito da esso e voglio implementarlo in R ma non riesco a trovare alcun esempio online. Non voglio solo stampare grafici. Voglio ottenere la pendenza istantanea in ogni punto temporale (forse ogni giorno) ma quella pendenza dovrebbe essere derivata da una spline che immette punti da alcuni giorni (o forse poche settimane o pochi mesi) prima di alcuni giorni dopo il punto temporale. In altre parole, alla fine del giorno voglio ottenere qualcosa come un frame di dati in cui una colonna è denaro al giorno o pazienti a settimana ma che non è soggetto a capricci come se ho pagato qualche giorno di ritardo o se ci sono stati 5 giorni operativi nel mese (rispetto al solito 4).

Ecco alcune simulazioni e trame semplificate per mostrare a cosa mi trovo di fronte.

library(lubridate) 
library(ggplot2) 
library(reshape2) 
dates <- seq(as.Date("2010-02-01"), length=24, by="1 month") - 1 
dates[5] <- dates[5]+3 #we are making one payment date that is 3 days late 
dates#look how the payment date is the last day of every month except for 
#2010-05 where it takes place on 2010-06-03 - naughty boy! 
amounts <- rep(50,each=24)# pay $50 every month 
register <- data.frame(dates,amounts)#this is the starting register or ledger 
ggplot(data=register,aes(dates,amounts))+geom_point()#look carefully and you will see that 2010-05 has no dots in it and 2010-06 has two dots 
register.by.month <- ddply(register,.(y=year(dates),month=month(dates)),summarise,month.tot=sum(amounts))#create a summary of totals by month but it lands up omiting a month in which nothing happened. Further badness is that it creates a new dataframe where one is not needed. Instead I created a new variable that allocates each date into a particular "zone" such as month or 
register$cutmonth <- as.Date(cut(register$dates, breaks = "month"))#until recently I did not know that the cut function can handle dates 
table(register$cutmonth)#see how there are two payments in the month of 2010-06 
#now lets look at what we paid each month. What is the total for each month 
ggplot(register, aes(cutmonth, amounts))+ stat_summary(fun.y = sum, geom = "bar")#that is the truth but it is a useless truth 

When one is late with a payment by a couple of days it appears as if the expense was zero in one month and double in the next. That is spurious

#so lets use cummulated expense over time 
register$cumamount <- cumsum(register$amounts) 
cum <- ggplot(data=register,aes(dates,cumamount))+geom_point() 
cum+stat_smooth() 

cumulative amount over time smooths out variability that changes an item's bin

#That was for everything the same every month, now lets introduce a situation where there is a trend that in the second year the amounts start to go up, 
increase <- c(rep(1,each=12),seq(from=1.01,to=1.9,length.out=12)) 
amounts.up <- round(amounts*increase,digits=2)#this is the monthly amount with a growth of amount in each month of the second year 
register <- cbind(register,amounts.up)#add the variable to the data frarme 
register$cumamount.up <- cumsum(register$amounts.up) #work out th cumulative sum for the new scenario 
ggplot(data=register,aes(x=dates))+ 
    geom_point(aes(y=amounts, colour="amounts",shape="amounts"))+ 
    geom_point(aes(y=amounts.up, colour="amounts.up",shape="amounts.up"))# the plot of amount by date 
#I am now going to plot the cumulative amount over time but now that I have two scenarios it is easier to deal with the data frame in long format (melted) rather than wide format (casted) 
#before I can melt, the reshape2 package unforutnately can't handle date class so will have to turn them int o characters and then back again. 
register[,c("dates","cutmonth")] <- lapply(register[,c("dates","cutmonth")],as.character) 
register.long <- melt.data.frame(register,measure.vars=c("amounts","amounts.up")) 
register.long[,c("dates","cutmonth")] <- lapply(register.long[,c("dates","cutmonth")],as.Date) 
ggplot(register.long, aes(cutmonth,value))+ stat_summary(fun.y = sum, geom = "bar")+facet_grid(. ~ variable) #that is the truth but it is a useless truth, 
cum <- ggplot(data=register,aes(dates,cumamount))+geom_point() 
#that is the truth but it is a useless truth. Furthermore it appears as if 2010-06 is similar to what is going on in 2011-12 
#that is patently absurd. All that happened was that the 2010-05 payment was delayed by 3 days. 

two scenarios but showing the amount of money paid in each month

#so lets use cummulated expense over time  
ggplot(data=register.long,aes(dates,c(cumamount,cumamount.up)))+geom_point() + scale_y_continuous(name='cumulative sum of amounts ($)') 

Here we see the cumulative sum data for the two scenarios

Quindi per la trama semplice la variabile interpolate.daily sarebbe di circa $ 50/30.4 = $ 1,64 al giorno per ogni giorno dell'anno. Per la seconda trama in cui l'importo pagato ogni mese inizia a salire ogni mese nel secondo anno dovrebbe mostrare una tariffa giornaliera di $ 1,64 al giorno per ogni giorno del primo anno e per le date del secondo anno si vedrebbero le tariffe giornaliere aumentando gradualmente da $ 1,64 al giorno a circa $ 3,12 al giorno.

Grazie mille per aver letto tutto fino alla fine. Devi essere stato così affascinato come lo ero io!

+1

Penso che hai un cattivo consiglio - un modo statistico più comune per fare questo sarebbe usare una stima della densità del kernel. – hadley

+0

@hadley Uno dei rispondenti alla mia domanda [ha parlato delle stime di densità e dei kernel] (http://stats.stackexchange.com/a/2737/104). Purtroppo non l'ho capito molto e mi ha fornito un'implementazione in MATLAB con cui non ho mai lavorato. – Farrel

+1

Beh, è ​​banale in ggplot2 - usa solo 'geom =" density "' – hadley

risposta

1

Ecco un modo semplice per farlo. Naturalmente ci sono opzioni più complesse e parametri da modificare, ma questo dovrebbe essere un buon punto di partenza.

dates <- seq(as.Date("2010-02-01"), length=24, by="1 month") - 1 
dates[5] <- dates[5]+3 
amounts <- rep(50,each=24) 
increase <- c(rep(1,each=12),seq(from=1.01,to=1.9,length.out=12)) 
amounts.up <- round(amounts*increase,digits=2) 

df = data.frame(dates=dates, cumamount.up=cumsum(amounts.up)) 

df.spline = splinefun(df$dates, df$cumamount.up) 

newdates = seq(min(df$dates), max(df$dates), by=1) 
money.per.day = df.spline(newdates, deriv=1) 

Se si traccia, è possibile vedere il comportamento interessante delle scanalature:

plot(newdates, money.per.day, type='l') 

enter image description here

+0

Grazie mille. Dalla persona che mi ha parlato di questa tecnica, mi è stato consigliato che la somma cumulativa sarebbe aumentata monotonicamente a causa della non negatività dei valori originali Detto questo, ritengo che la spline debba essere lineare o impostata su monotonic, esplorerò la funzione splinefun, ma sono lieto che tu mi abbia mostrato come gettare un mucchio di x contro la formula derivata – Farrel

+0

@Farrel Great Sono felice che sia d'aiuto, decisamente intelligente per esplorare le opzioni come hai detto tu. L'cumsum è in effetti monotonicamente crescente (prova 'plot (df); lines (newdates, df.spline (newdates))' per vedere), ma la prima derivata che volevi (Cioè soldi/giorno) non lo è. Andrà su e giù mentre passi da un mese lungo a uno corto, ecc. –

+0

Ho intenzione di giocare con questo domani. Non posso aspettare – Farrel

Problemi correlati