Вменение наблюдений, чтобы сделать поперечное сечение временным рядом в R

Я надеюсь взять набор данных с перекрестными данными о заработной плате сотрудников и создать большой непрерывный временной ряд, попутно вводя значения. Предположим, у меня есть:

name <- c("carl","carl","bob","rick","rick","rick","rick")
sex <- c(rep("M",7))
salary <- c(18000, 14000, 34000, 11000, 23000, 23000, 25000)
date <- as.Date(c("2007-04-30","2007-07-30","2009-12-09","2006-01-01",
                 "2008-01-01","2009-12-09", "2010-01-01"))

salaries <- data.frame(name,sex,salary,date)
salaries
  name sex salary       date
  carl   M  18000 2007-04-30
  carl   M  14000 2007-07-30
   bob   M  34000 2009-12-09
  rick   M  11000 2006-01-01
  rick   M  23000 2008-01-01
  rick   M  23000 2009-12-09
  rick   M  25000 2010-01-01

Как мы видим, бедняге Карлу в июле сократили зарплату на 4 тысячи. До этого он зарабатывал 18к. Так было в течение 3 месяцев до того, как он получил порез, но мои данные этого не отражают. Я хотел бы сделать красивую картинку, показывающую эту тенденцию, но сначала мне нужно изменить данные, чтобы они выглядели так (где * обозначает вмененные значения):

head(salaries)
  name sex salary       date change
  carl   M  18000 2007-04-30    0
  carl   M  18000 2007-05-30*   0 
  carl   M  18000 2007-06-30*   0 
  carl   M  14000 2007-07-30    1
   bob   M  34000 2009-12-09    0 
  rick   M  11000 2006-01-01    0
  rick   M  11000 2006-02-01*   0
  ...   .. ....... ...... ....
  rick   M  11000 2007-12-01*   0
  rick   M  23000 2008-01-01    1
  rick   M  23000 2008-02-01*   1
  ....   ...... ...... ........
  rick   M  23000 2009-12-09    1     
  rick   M  25000 2010-01-01    2 

Поэтому я хотел бы ввести промежуточные значения, а также отметить, когда происходит изменение. Такой парень, как Боб, у которого никогда не менялась зарплата, просто остается на 0. Но Рик, у которого было несколько изменений зарплаты, каждый раз помечается, поэтому мы знаем, когда произошло изменение и какой это номер. Меня интересует только месяц как единица анализа, но было бы полезно знать, как вычислять также и день.


person kpeyton    schedule 04.04.2013    source источник


Ответы (2)


Если у вас есть один временной ряд, вы можете использовать na.locf, чтобы заменить отсутствующие значения последним доступным значением, или approx, если вы хотите интерполировать только между значениями. Чтобы создать эти отдельные временные ряды, вы можете преобразовать данные между «высоким» (нормализованным) форматом и «широким» форматом с помощью dcast и melt. Чтобы подсчитать количество изменений, вы можете использовать ddply и cumsum.

library(reshape2)
library(plyr)
library(zoo)

# Convert to wide format
d <- dcast( salaries, date ~ name, value.var = "salary" )

# Add all the dates you want
dates <- seq.Date( from = min(d$date), max(d$date), by="month" )
d <- merge( d, data.frame(date=dates), all=TRUE )

# Fill in the missing values
# If you want the last non-missing value:
#d <- as.data.frame(lapply(d, na.locf, na.rm=FALSE))
# If you only want to interpolate between values:
d <- as.data.frame(lapply(d, 
  function(x) approx( seq_along(x), x, seq_along(x), method="constant" )$y
))

# Convert back to the tall format
d <- melt(d, id.vars="date", value.name="salary", variable.name="name", na.rm=TRUE)

# Add the number of changes
d <- ddply(
  d, "name", transform, 
  change = cumsum(c(0, diff(salary) != 0))
)
person Vincent Zoonekynd    schedule 04.04.2013
comment
Подсчет работает, но ежемесячные вменения поддельных данных не проходят. И по какой-то причине для боба есть дополнительное значение. - person kpeyton; 04.04.2013
comment
Я обновил свой ответ, чтобы интерполировать только между значениями и обеспечить (по крайней мере) одно наблюдение в месяц. - person Vincent Zoonekynd; 04.04.2013
comment
спасибо, можно ли ограничить интерполяцию только одним значением в месяц? И сохранить другие ковариаты (например, пол) - person kpeyton; 05.04.2013
comment
Вы можете ограничить данные одним наблюдением в месяц, например, объединив (с merge или sqldf) с вектором желаемых дат, но что произойдет с сотрудниками с одним наблюдением, не входящим в этот список дат? Их либо отбрасывали, либо наблюдение сохранялось, но не согласовывалось с остальными (что, казалось, было вашей первоначальной целью). Если другие переменные не меняются со временем, вы можете извлечь их значения с помощью unique(salaries[,c("name","sex")]) и соединить их с результатом. - person Vincent Zoonekynd; 05.04.2013
comment
Понятно, спасибо за ответ. И что, если у меня есть дополнительные ковариаты, изменяющиеся во времени, могу ли я добавить их на этапе обратного преобразования в высокий формат (команда плавления)? - person kpeyton; 05.04.2013
comment
Если они числовые, вы можете обрабатывать их таким же образом, отдельно (в широком формате есть только одна переменная) и объединять полученные data.frames в конце. Если они не числовые, approx не будет работать, а na.locf будет. - person Vincent Zoonekynd; 05.04.2013
comment
кажется, делает трюк! Ниже я разместил пример слияния символьных и числовых векторов. - person kpeyton; 05.04.2013

Развивая совет @Vincent:

        name <- c("carl","carl","bob","rick","rick","rick","rick")
        sex <- c(rep("M",7))
        salary <- c(18000, 14000, 34000, 11000, 23000, 23000, 25000)
        office <- c('melbourne','sydney','adelaide','perth','perth','melbourne','melbourne')
        date <- as.Date(c("2007-04-30","2007-07-30","2009-12-09","2006-01-01",
                          "2008-01-01","2009-12-09", "2010-01-01"))

        salaries <- data.frame(name,sex,salary,date, office)
        salaries


        library(reshape2)
        library(plyr)
        library(zoo)

Работа с числовым вектором с помощью приблизительно

        # Convert to wide format
        d <- dcast( salaries, date ~ name, value.var = "salary" )

        # Add all the dates you want
        dates <- seq.Date( from = min(d$date), max(d$date), by="month" )
        d <- merge( d, data.frame(date=dates), all=TRUE )

        # Fill in the missing values
        # If you want the last non-missing value:
        #d <- as.data.frame(lapply(d, na.locf, na.rm=FALSE, fromLast = T))
        #If you only want to interpolate between values:
        d <- as.data.frame(lapply(d, 
                                  function(x) approx( seq_along(x), x, seq_along(x), method="constant" )$y
        ))

        # Convert back to the tall format
        d <- melt(d, id.vars="date", value.name="salary", variable.name="name", na.rm=TRUE)

        # Add the number of changes
        d <- ddply(
          d, "name", transform, 
          change = cumsum(c(0, diff(salary) != 0))
        )

Преобразовать вектор символов с помощью na.locf

        # Convert to wide format
        a <- dcast( salaries, date ~ name, value.var = "office" )

        # Add all the dates you want
        dates <- seq.Date( from = min(a$date), max(a$date), by="month" )
        a <- merge( a, data.frame(date=dates), all=TRUE )

        # Fill in the missing values using na.locf
        a <- as.data.frame(lapply(a, na.locf, na.rm=FALSE, fromLast = T))

        # Convert back to the tall format
        a <- melt(a, id.vars="date", value.name="office", variable.name="name", na.rm=TRUE)

Объединить результаты

        d$date <- as.Date(d$date)
        out = merge(a,d, by = c('name','date'))
person kpeyton    schedule 05.04.2013