2
votes

I'm new to the tapply function and didn't succeed computing a time difference calculation within each asked subset.

I have an input dataframe containing observations dates (column DATE) for some RN. In my script, I'm subsetting this dataframe in another (lets call it DF) restraining the data to only one RN.

I'm trying to automatically calculate the difference between a date and the previous one. I'm able to do that on DF with the following code:

as.numeric(c(NA, as.Date(tail(DF$DATE,-1)) - as.Date(head(DF$DATE,-1)) ))

I need to run this within each subset based on 3 variables (VAR1,VAR2,VAR3), so I tried the so called tapply.

I tried with: (DATE is my third column)

tapply(DF$DATE, list(DF$SITE, DF$YEAR, DF$SP), FUN =  function(x){
as.numeric(c(NA, as.Date(tail(DF[x,3],-1)) - as.Date(head(DF[x,3],-1))))})

I might miss an important point here because it returns me an array filled with TONS of NULL. Worst, it returns me NULL data for all the sites, not existing in DF.

How is that function calling factors levels that aren't even in the input? Is there a way to skip all this NULL data, or at least restraining it at the boundaries of my subset.

I searched on SO and other helps sites but didn't found a working answer.

Second question: I would like to write the output (time differences) in a new column. Since tapply result is an array, how can I do that ? Where do I have to put this command? In the function(x){} part or outside the apply command?

EDIT: here is a part of DF, i edited the commands too EDIT2 : While doing dput i saw gthat all my factors levels still exists. I dropped them with factor(), but now tapply() doesn't work anymore.

EDIT 3: @sgibb: I tried your command but now get another error: "Error in as.Date.numeric(value) : 'origin' must be supplied". So i tried to supply an origin as the following shows, but still got the error

SUBTOT$DIFF <- ave(SUBTOT$DATE, SUBTOT$SITE, SUBTOT$YEAR, SUBTOT$SP, FUN=function(x) {
as.numeric(c(NA, as.Date(tail(x,-1),origin="1960-10-01") -      as.Date(head(x,-1),origin="1960-10-01")))})


dput(head(SUBTOT,n=100))
structure(list(YEAR = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("1994", 
"1995", "1996", "1997", "1998", "1999", "2000", "2001", "2002", 
"2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", 
"2011", "2012"), class = "factor"), RN = structure(c(1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L), .Label = "RNN144", class = "factor"), DATE = c("1994-04-16", 
"1994-07-23", "1994-10-01", "1994-04-16", "1994-05-07", "1994-10-01", 
"1994-04-16", "1994-07-05", "1994-07-10", "1994-07-17", "1994-10-01", 
"1994-04-16", "1994-06-20", "1994-10-01", "1994-04-16", "1994-06-06", 
"1994-10-01", "1994-04-16", "1994-07-23", "1994-10-01", "1994-04-16", 
"1994-09-19", "1994-09-24", "1994-10-01", "1994-04-16", "1994-04-23", 
"1994-04-30", "1994-05-07", "1994-05-23", "1994-05-29", "1994-07-10", 
"1994-07-17", "1994-07-23", "1994-08-08", "1994-10-01", "1994-04-16", 
"1994-07-17", "1994-07-23", "1994-10-01", "1994-04-16", "1994-07-17", 
"1994-10-01", "1994-04-16", "1994-07-05", "1994-07-10", "1994-07-17", 
"1994-07-23", "1994-08-20", "1994-10-01", "1994-04-16", "1994-06-28", 
"1994-07-05", "1994-07-10", "1994-10-01", "1994-04-16", "1994-06-20", 
"1994-06-28", "1994-10-01", "1994-04-16", "1994-07-17", "1994-07-23", 
"1994-08-20", "1994-08-27", "1994-09-04", "1994-10-01", "1994-04-16", 
"1994-08-20", "1994-09-19", "1994-09-24", "1994-10-01", "1994-04-16", 
"1994-05-29", "1994-10-01", "1994-04-16", "1994-07-10", "1994-07-17", 
"1994-07-23", "1994-07-29", "1994-08-20", "1994-08-27", "1994-10-01", 
"1994-04-16", "1994-07-05", "1994-07-17", "1994-10-01", "1994-04-16", 
"1994-07-17", "1994-10-01", "1994-04-16", "1994-05-23", "1994-10-01", 
"1994-04-23", "1994-07-05", "1994-07-17", "1994-07-23", "1994-09-20", 
"1994-04-23", "1994-05-23", "1994-05-29", "1994-09-20"), NOM = structure(c(16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L), .Label = c("ANCIENNES CARRIERES D'ORIVAL", 
"BAIE DE L'AIGUILLON (VENDEE)", "CHERINE", "COMBE LAVAUX-JEAN ROLAND", 
"COTE DE MANCY", "Espace protégé - code", "ESTAGNOL", "GRAND PIERRE ET VITAIN", 
"ILE DE LA PLATIERE", "LAC DE REMORAY", "MARAIS DE LAVOURS", 
"PETITE CAMARGUE ALSACIENNE", "PINAIL", "RAMIERES DU VAL DE DROME", 
"RAVIN DE VALBOIS", "TOURBIERE DES DAUGES"), class = "factor"), 
SITE = structure(c(104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 104L, 
104L, 104L, 104L, 104L, 105L, 105L, 105L, 105L, 105L, 105L, 
105L, 105L, 105L), .Label = c("Libellé du site", "RNN027-Estagnol 01", 
"RNN027-Estagnol 02", "RNN027-Estagnol 03", "RNN027-Estagnol 04", 
"RNN027-Estagnol 05", "RNN027-Estagnol 06", "RNN037-GPV 01", 
"RNN037-GPV 02", "RNN037-GPV 03", "RNN037-GPV 04", "RNN037-GPV 05", 
"RNN044-Pinail A", "RNN044-Pinail B", "RNN044-Pinail C", 
"RNN044-Pinail D", "RNN044-Pinail E", "RNN044-Pinail F", 
"RNN044-Pinail G", "RNN044-Pinail I", "RNN044-Pinail J", 
"RNN044-Pinail K", "RNN046-Remoray 01", "RNN046-Remoray 02", 
"RNN046-Remoray 03", "RNN046-Remoray 04", "RNN046-Remoray 05", 
"RNN046-Remoray 06", "RNN046-Remoray 07", "RNN046-Remoray 08", 
"RNN046-Remoray 09", "RNN046-Remoray 10", "RNN060-PCA Canal", 
"RNN060-PCA Heid", "RNN060-PCA Luzernière", "RNN060-PCA Mitlere-au", 
"RNN060-PCA Petite Heid", "RNN066-Valbois Ourlet bas", "RNN066-Valbois Ourlet haut", 
"RNN066-Valbois Pel Humbert", "RNN066-Valbois Pel Martin", 
"RNN066-Valbois Pel Podgo", "RNN066-Valbois Pel Podgo corniche", 
"RNN066-Valbois Pel temoin", "RNN066-Valbois Vignes parc2bas", 
"RNN066-Valbois Vignes parc2haut", "RNN066-Valbois Vignes parc3bas", 
"RNN066-Valbois Vignes parc3haut", "RNN068-Marais Lavours beon1", 
"RNN068-Marais Lavours beon2", "RNN068-Marais Lavours beon3", 
"RNN068-Marais Lavours beon4", "RNN068-Marais Lavours beon5", 
"RNN068-Marais Lavours ceyzerieu1", "RNN068-Marais Lavours culoz1", 
"RNN078-Chérine 01", "RNN078-Chérine 02", "RNN078-Chérine 03", 
"RNN078-Chérine 04", "RNN078-Chérine 05", "RNN078-Chérine 06", 
"RNN078-Chérine 07", "RNN078-Chérine 08", "RNN078-Chérine 09", 
"RNN078-Chérine 10", "RNN078-Chérine 11", "RNN078-Chérine 12", 
"RNN079-Platière 01LGRA01", "RNN079-Platière 01LGRA03", "RNN079-Platière 01LGRA04", 
"RNN079-Platière 01LGRA07", "RNN079-Platière 01LGRA08", "RNN079-Platière 01LGRA11", 
"RNN079-Platière 01LGRA12", "RNN079-Platière 01LPGO01", "RNN079-Platière 01LPGO02", 
"RNN079-Platière 01LPGO03", "RNN079-Platière 01LPGO04", "RNN079-Platière 01LPGO05", 
"RNN079-Platière 01LPGO06", "RNN079-Platière 01LPGO07", "RNN079-Platière 01LPOV01", 
"RNN079-Platière 01LPOV02", "RNN079-Platière 01LPOV03", "RNN079-Platière 01LPOV04", 
"RNN079-Platière 01LPOV05", "RNN079-Platière 01LPOV06", "RNN089-Ramières 24", 
"RNN089-Ramières 25", "RNN089-Ramières 26", "RNN089-Ramières 27", 
"RNN089-Ramières 35", "RNN089-Ramières 36", "RNN089-Ramières 37", 
"RNN089-Ramières 43", "RNN089-Ramières 46", "RNN089-Ramières 47", 
"RNN130-Baie Aiguillon Charron-Casiers-vase", "RNN130-Baie Aiguillon Charron-Mizottes-Chaînes", 
"RNN130-Baie Aiguillon Charron-Pd-Digue-mer", "RNN130-Baie Aiguillon Triaize-Mizottes", 
"RNN130-Baie Aiguillon Triaize-Pd-Digue-1", "RNN130-Baie Aiguillon Triaize-Pd-Digue-2", 
"RNN144-Dauges 01", "RNN144-Dauges 02", "RNN144-Dauges 03", 
"RNN144-Dauges 04", "RNN144-Dauges 05", "RNN144-Dauges 06", 
"RNN144-Dauges 07", "RNN144-Dauges 08", "RNN144-Dauges 09", 
"RNN144-Dauges 10", "RNN144-Dauges 11", "RNN144-Dauges 12", 
"RNN144-Dauges 13", "RNN144-Dauges 14", "RNN144-Dauges 15", 
"RNN157-Combe Lavaux 01 Sentier des crêtes", "RNN157-Combe Lavaux 02 Sentier des dalles", 
"RNN157-Combe Lavaux 03 Plain des Essoyottes partie humide", 
"RNN157-Combe Lavaux 04 Plain des Essoyottes partie sèches", 
"RNN157-Combe Lavaux 05 Pelouses de Fontenottes", "RNN157-Combe Lavaux 06 Friches parc de nuit", 
"RNN157-Combe Lavaux 07 Friches", "RNN157-Combe Lavaux 08 Friches ouverture ONF", 
"RNN157-Combe Lavaux 09 Haut du Champ Sement", "RNN157-Combe Lavaux 10 Bas du Champ Sement", 
"RNR117-TRMANCY 01", "RNR117-TRMANCY 02", "RNR117-TRMANCY 03", 
"RNR117-TRMANCY 04", "RNR117-TRMANCY 05", "RNR117-TRMANCY 06", 
"RNR189-RNR_TRACO_01", "RNR189-RNR_TRACO_02", "RNR189-RNR_TRACO_03", 
"RNR189-RNR_TRACO_04", "RNR189-RNR_TRACO_05", "RNR189-RNR_TRACO_06a", 
"RNR189-RNR_TRACO_06b", "RNR189-RNR_TRACO_07", "RNR189-RNR_TRACO_08", 
"RNR189-RNR_TRACO_09a", "RNR189-RNR_TRACO_09b", "RNR189-RNR_TRACO_10", 
"RNR189-RNR_TRACO_11", "RNR189-RNR_TRACO_12", "RNR189-RNR_TRACO_13", 
"RNR189-RNR_TRACO_14", "RNR189-RNR_TRACO_15"), class = "factor"), 
LONG = c(80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 
80, 80, 80, 80, 115, 115, 115, 115, 115, 115, 115, 115, 115
), SP = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 4L, 4L, 4L, 4L, 
4L, 5L, 5L, 5L, 10L, 10L, 10L, 12L, 12L, 12L, 20L, 20L, 20L, 
20L, 29L, 29L, 29L, 29L, 29L, 29L, 29L, 29L, 29L, 29L, 29L, 
31L, 31L, 31L, 31L, 33L, 33L, 33L, 43L, 43L, 43L, 43L, 43L, 
43L, 43L, 44L, 44L, 44L, 44L, 44L, 56L, 56L, 56L, 56L, 61L, 
61L, 61L, 61L, 61L, 61L, 61L, 62L, 62L, 62L, 62L, 62L, 69L, 
69L, 69L, 70L, 70L, 70L, 70L, 70L, 70L, 70L, 70L, 72L, 72L, 
72L, 72L, 73L, 73L, 73L, 75L, 75L, 75L, 4L, 4L, 4L, 4L, 4L, 
15L, 15L, 15L, 15L), .Label = c("Aglais urticae (Linnaeus, 1758)", 
"Anthocharis cardamines (Linnaeus, 1758)", "Apatura iris (Linnaeus, 1758)", 
"Aphantopus hyperantus (Linnaeus, 1758)", "Aporia crataegi (Linnaeus, 1758)", 
"Araschnia levana (Linnaeus, 1758)", "Argynnis aglaja (Linnaeus, 1758)", 
"Argynnis paphia (Linnaeus, 1758)", "Boloria dia (Linnaeus, 1767)", 
"Boloria euphrosyne (Linnaeus, 1758)", "Boloria selene (Denis & Schiffermüller, 1775)", 
"Brenthis daphne (Bergsträsser, 1780)", "Brintesia circe (Fabricius, 1775)", 
"Callophrys rubi (Linnaeus, 1758)", "Carterocephalus palaemon (Pallas, 1771)", 
"Celastrina argiolus (Linnaeus, 1758)", "Clossiana dia (Linnaeus, 1767)", 
"Clossiana selene (Denis & Schiffermüller, 1775)", "Coenonympha pamphilus (Linnaeus, 1758)", 
"Colias croceus (Fourcroy, 1785)", "Colias hyale (Linnaeus, 1758)", 
"Colias PC (hyale / alfacariensis) #complexe", "Cupido argiades (Pallas, 1771)", 
"Erebia meolans (de Prunner, 1798)", "Erynnis tages (Linnaeus, 1758)", 
"Euchloe PC (ausonia / simplonia) #complexe", "Euphydryas aurinia (Rottemburg, 1775)", 
"Everes argiades (Pallas, 1771)", "Gonepteryx rhamni (Linnaeus, 1758)", 
"Hesperia comma (Linnaeus, 1758)", "Heteropterus morpheus (Pallas, 1771)", 
"Hipparchia fagi (Scopoli, 1763)", "Inachis io (Linnaeus, 1758)", 
"Iphiclides podalirius (Linnaeus, 1758)", "Issoria lathonia (Linnaeus, 1758)", 
"Ladoga camilla (Linnaeus, 1764)", "Lampides boeticus (Linnaeus, 1767)", 
"Lasiommata megera (Linnaeus, 1767)", "Limenitis camilla (Linnaeus, 1764)", 
"Limenitis reducta Staudinger, 1901", "Lycaena phlaeas (Linnaeus, 1761)", 
"Lycaena tityrus (Poda, 1761)", "Maniola jurtina (Linnaeus, 1758)", 
"Melanargia galathea (Linnaeus, 1758)", "Melitaea cinxia (Linnaeus, 1758)", 
"Melitaea diamina (Lang, 1789)", "Melitaea didyma (Esper, 1778)", 
"Melitaea phoebe (Denis & Schiffermüller, 1775)", "Mellicta athalia (Rottemburg, 1775)", 
"Mellicta C (athalia / deione / parthenoides) #complexe", 
"Mellicta parthenoides (Keferstein, 1851)", "Mesoacidalia aglaja (Linnaeus, 1758)", 
"Nymphalis antiopa (Linnaeus, 1758)", "Nymphalis polychloros (Linnaeus, 1758)", 
"Ochlodes venatus (Bremer & Grey, 1853)", "Ochlodes venatus faunus (Turati, 1905)", 
"Papilio machaon Linnaeus, 1758", "Pararge aegeria (Linnaeus, 1758)", 
"Pieris 2 (rapae / mannii / napi) #complexe", "Pieris brassicae (Linnaeus, 1758)", 
"Pieris napi (Linnaeus, 1758)", "Pieris PC (rapae / mannii) #complexe", 
"Plebeius agestis (Denis & Schiffermüller, 1775)", "Plebejus argus (Linnaeus, 1758)", 
"Polygonia c-album (Linnaeus, 1758)", "Polyommatus icarus (Rottemburg, 1775)", 
"Polyommatus semiargus (Rottemburg, 1775)", "Pseudophilotes baton (Bergsträsser, 1779)", 
"Pyrgus 1 C (malvae / malvoides) #complexe", "Pyronia tithonus (Linnaeus, 1767)", 
"Quercusia quercus (Linnaeus, 1758)", "Thymelicus lineola (Ochsenheimer, 1808)", 
"Thymelicus sylvestris (Poda, 1761)", "Vanessa atalanta (Linnaeus, 1758)", 
"Vanessa cardui (Linnaeus, 1758)"), class = "factor"), SUMNB = c(0, 
2, 0, 0, 2, 0, 0, 12, 18, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 
0, 4, 2, 0, 0, 2, 2, 2, 2, 6, 2, 2, 4, 2, 0, 0, 2, 2, 0, 
0, 2, 0, 0, 2, 2, 2, 6, 6, 0, 0, 4, 4, 4, 0, 0, 4, 2, 0, 
0, 2, 2, 4, 2, 2, 0, 0, 4, 2, 2, 0, 0, 2, 0, 0, 2, 2, 6, 
4, 2, 4, 0, 0, 2, 2, 0, 0, 4, 0, 0, 2, 0, 0, 4, 6, 2, 0, 
0, 2, 2, 0), NB100 = c(0, 2.5, 0, 0, 2.5, 0, 0, 15, 22.5, 
2.5, 0, 0, 2.5, 0, 0, 2.5, 0, 0, 2.5, 0, 0, 5, 2.5, 0, 0, 
2.5, 2.5, 2.5, 2.5, 7.5, 2.5, 2.5, 5, 2.5, 0, 0, 2.5, 2.5, 
0, 0, 2.5, 0, 0, 2.5, 2.5, 2.5, 7.5, 7.5, 0, 0, 5, 5, 5, 
0, 0, 5, 2.5, 0, 0, 2.5, 2.5, 5, 2.5, 2.5, 0, 0, 5, 2.5, 
2.5, 0, 0, 2.5, 0, 0, 2.5, 2.5, 7.5, 5, 2.5, 5, 0, 0, 2.5, 
2.5, 0, 0, 5, 0, 0, 2.5, 0, 0, 3.47826086956522, 5.21739130434783, 
1.73913043478261, 0, 0, 1.73913043478261, 1.73913043478261, 
0)), .Names = c("YEAR", "RN", "DATE", "NOM", "SITE", "LONG", 
"SP", "SUMNB", "NB100"), class = c("data.table", "data.frame"
), row.names = c(NA, -100L), .internal.selfref = <pointer: 0x0000000000210788>)
1
Please supply a sample of your data, e.g., using dput in order to make this a reproducible example.Thomas
Please follow @Thomas advice to dput your sample data. With spaces both within and beteen variables it is a bit tricky to read. Please include only variables relevant for your question.Henrik
Was doing it, see EDIT 2.user2542995

1 Answers

1
votes

Maybe you don't look for tapply but for ave (see ?ave for details).

I create a simplified example dataset:

## create example dataset
set.seed(1)
df <- data.frame(YEAR=rep(2001:2005, each=4), MONTH=rep(1:10, each=2),
                 DAY=rep(1:10, each=2),
                 VALUE=sample(1:20))

## turn YEAR, MONTH, DAY into factors
df <- within(df, {
  YEAR <- factor(YEAR)
  MONTH <- factor(MONTH)
  DAY <- factor(DAY)
})

## your function (simplified for my example)
your_function <- function(x) {
  as.numeric(c(NA, tail(x, -1)-head(x,-1)))
}

## run `your_function` for equal combinations of YEAR, MONTH, DAY
df$DIFF <- ave(df$VALUE, df$YEAR, df$MONTH, df$DAY, FUN=your_function)
df
#   YEAR MONTH DAY VALUE DIFF
#1  2001     1   1     6   NA
#2  2001     1   1     8    2
#3  2001     2   2    11   NA
#4  2001     2   2    16    5
#5  2002     3   3     4   NA
#6  2002     3   3    14   10
#7  2002     4   4    15   NA
#8  2002     4   4     9   -6
#9  2003     5   5    19   NA
#10 2003     5   5     1  -18
#11 2003     6   6     3   NA
#12 2003     6   6     2   -1
#13 2004     7   7    20   NA
#14 2004     7   7    10  -10
#15 2004     8   8     5   NA
#16 2004     8   8     7    2
#17 2005     9   9    12   NA
#18 2005     9   9    17    5
#19 2005    10  10    18   NA
#20 2005    10  10    13   -5

In your example it should be:

DF$DIFF <- ave(DF$DATE, DF$SITE, DF$YEAR, DF$SP, FUN=function(x) {
   as.numeric(c(NA, as.Date(tail(x,-1)) - as.Date(head(x,-1)))))
}