सवाल प्रत्येक समूह के भीतर एक अंतराल चर बनाने के लिए कैसे?


मेरे पास एक डेटा.table है:

set.seed(1)
data <- data.table(time = c(1:3, 1:4),
                   groups = c(rep(c("b", "a"), c(3, 4))),
                   value = rnorm(7))

data
#    groups time      value
# 1:      b    1 -0.6264538
# 2:      b    2  0.1836433
# 3:      b    3 -0.8356286
# 4:      a    1  1.5952808
# 5:      a    2  0.3295078
# 6:      a    3 -0.8204684
# 7:      a    4  0.4874291

मैं "मूल्य" कॉलम के एक लगी संस्करण को गणना करना चाहता हूं, अंदर "समूह" के प्रत्येक स्तर।

परिणाम इसकी तरह दिखना चाहिए

#   groups time      value  lag.value
# 1      a    1  1.5952808         NA
# 2      a    2  0.3295078  1.5952808
# 3      a    3 -0.8204684  0.3295078
# 4      a    4  0.4874291 -0.8204684
# 5      b    1 -0.6264538         NA
# 6      b    2  0.1836433 -0.6264538
# 7      b    3 -0.8356286  0.1836433

मैंने उपयोग करने की कोशिश की है lag सीधे:

data$lag.value <- lag(data$value) 

... जो स्पष्ट रूप से काम नहीं करेगा।

मैंने भी कोशिश की है:

unlist(tapply(data$value, data$groups, lag))
 a1         a2         a3         a4         b1         b2         b3 
 NA -0.1162932  0.4420753  2.1505440         NA  0.5894583 -0.2890288 

जो मैं चाहता हूं वह लगभग है। हालांकि जेनरेट किए गए वेक्टर को डेटाटेबल में ऑर्डरिंग से अलग-अलग आदेश दिया जाता है जो समस्याग्रस्त है।

बेस आर, प्लीयर, dplyr, और data.table में ऐसा करने का सबसे प्रभावी तरीका क्या है?


44
2017-10-10 04:33


मूल


क्षमा करें, साथ गठबंधन करें group_by - Alex
unlist(by(data, data$groups, function(x) c(NA, head(x$value, -1)))) एक आधार तरीका होगा - rawr
@ xiaodai यदि आपके पास सिर्फ एक कॉलम है lag और डेटासेट इतना बड़ा नहीं है, इसमें दक्षता में बहुत अंतर नहीं होगा base R, plyr, data.table तरीकों। - akrun
@akrun समझो। हालांकि मैं वास्तव में इसे सरल बना दिया। मुझे वास्तव में कई कॉलम के लिए इसकी आवश्यकता है और अन्य उपयोगों के लाभ के लिए सामान्य समाधान पसंद किए जाते हैं - xiaodai
@xiaodai मैंने कई कॉलम के लिए अद्यतन किया। क्यों के बारे में lag धीमा है, यह कोड पर निर्भर होना चाहिए lag। आप देख सकते हैं getAnywhere('lag.default')[1] - akrun


जवाब:


आप इसे अंदर कर सकते हैं data.table

 library(data.table)
 data[, lag.value:=c(NA, value[-.N]), by=groups]
  data
 #   time groups       value   lag.value
 #1:    1      a  0.02779005          NA
 #2:    2      a  0.88029938  0.02779005
 #3:    3      a -1.69514201  0.88029938
 #4:    1      b -1.27560288          NA
 #5:    2      b -0.65976434 -1.27560288
 #6:    3      b -1.37804943 -0.65976434
 #7:    4      b  0.12041778 -1.37804943

एकाधिक कॉलम के लिए:

nm1 <- grep("^value", colnames(data), value=TRUE)
nm2 <- paste("lag", nm1, sep=".")
data[, (nm2):=lapply(.SD, function(x) c(NA, x[-.N])), by=groups, .SDcols=nm1]
 data
#    time groups      value     value1      value2  lag.value lag.value1
#1:    1      b -0.6264538  0.7383247  1.12493092         NA         NA
#2:    2      b  0.1836433  0.5757814 -0.04493361 -0.6264538  0.7383247
#3:    3      b -0.8356286 -0.3053884 -0.01619026  0.1836433  0.5757814
#4:    1      a  1.5952808  1.5117812  0.94383621         NA         NA
#5:    2      a  0.3295078  0.3898432  0.82122120  1.5952808  1.5117812
#6:    3      a -0.8204684 -0.6212406  0.59390132  0.3295078  0.3898432
#7:    4      a  0.4874291 -2.2146999  0.91897737 -0.8204684 -0.6212406
#    lag.value2
#1:          NA
#2:  1.12493092
#3: -0.04493361
#4:          NA
#5:  0.94383621
#6:  0.82122120
#7:  0.59390132

अद्यतन करें

से data.table संस्करण> = v1.9.5, हम प्रयोग कर सकते हैं shift साथ में type जैसा lag या lead। डिफ़ॉल्ट रूप से, प्रकार है lag

data[, (nm2) :=  shift(.SD), by=groups, .SDcols=nm1]
#   time groups      value     value1      value2  lag.value lag.value1
#1:    1      b -0.6264538  0.7383247  1.12493092         NA         NA
#2:    2      b  0.1836433  0.5757814 -0.04493361 -0.6264538  0.7383247
#3:    3      b -0.8356286 -0.3053884 -0.01619026  0.1836433  0.5757814
#4:    1      a  1.5952808  1.5117812  0.94383621         NA         NA
#5:    2      a  0.3295078  0.3898432  0.82122120  1.5952808  1.5117812
#6:    3      a -0.8204684 -0.6212406  0.59390132  0.3295078  0.3898432
#7:    4      a  0.4874291 -2.2146999  0.91897737 -0.8204684 -0.6212406
#    lag.value2
#1:          NA
#2:  1.12493092
#3: -0.04493361
#4:          NA
#5:  0.94383621
#6:  0.82122120
#7:  0.59390132

यदि आपको रिवर्स की आवश्यकता है, तो उपयोग करें type=lead

nm3 <- paste("lead", nm1, sep=".")

मूल डेटासेट का उपयोग करना

  data[, (nm3) := shift(.SD, type='lead'), by = groups, .SDcols=nm1]
  #  time groups      value     value1      value2 lead.value lead.value1
  #1:    1      b -0.6264538  0.7383247  1.12493092  0.1836433   0.5757814
  #2:    2      b  0.1836433  0.5757814 -0.04493361 -0.8356286  -0.3053884
  #3:    3      b -0.8356286 -0.3053884 -0.01619026         NA          NA
  #4:    1      a  1.5952808  1.5117812  0.94383621  0.3295078   0.3898432
  #5:    2      a  0.3295078  0.3898432  0.82122120 -0.8204684  -0.6212406
  #6:    3      a -0.8204684 -0.6212406  0.59390132  0.4874291  -2.2146999
  #7:    4      a  0.4874291 -2.2146999  0.91897737         NA          NA
 #   lead.value2
 #1: -0.04493361
 #2: -0.01619026
 #3:          NA
 #4:  0.82122120
 #5:  0.59390132
 #6:  0.91897737
 #7:          NA

डेटा

 set.seed(1)
 data <- data.table(time =c(1:3,1:4),groups = c(rep(c("b","a"),c(3,4))),
             value = rnorm(7), value1=rnorm(7), value2=rnorm(7))

67
2017-10-10 04:40



मैं सोच रहा हूं क्यों डेटा [, lag.value: = lag (value)), = समूहों द्वारा] जो आपके समाधान से धीमा परिणाम देता है? - xiaodai
मैं यह कैसे करूँगा, लेकिन विपरीत में? दूसरे शब्दों में, एक (पिछली पंक्ति लेते हुए) के आधार पर, यह एक से आगे होगा (निम्नलिखित पंक्ति मान लेना)? महान प्रवेश के लिए धन्यवाद! - verybadatthis
@verybadatthis कृपया जांचें कि क्या अपडेट मदद करता है - akrun
क्या एक से अधिक मूल्यों से गुजरना भी संभव है? (यानी हो रही है data[, lag.value.1:=c(NA, lag.value[-.N]), by=groups] गणना के बिना lag.value?) - greyBag
मेरी राय में यह दिखाने के लिए अद्यतन / अद्यतन किया जाना चाहिए shift रास्ता, या कम से कम इसे शीर्ष पर रखने के लिए, अब यह बेकार से बाहर है। हम इस क्यू एंड ए को डुप्ली लक्ष्य के रूप में उपयोग कर रहे हैं। - Frank


पैकेज का उपयोग करना dplyr:

library(dplyr)
data <- 
    data %>%
    group_by(groups) %>%
    mutate(lag.value = dplyr::lag(value, n = 1, default = NA))

देता है

> data
Source: local data table [7 x 4]
Groups: groups

  time groups       value   lag.value
1    1      a  0.07614866          NA
2    2      a -0.02784712  0.07614866
3    3      a  1.88612245 -0.02784712
4    1      b  0.26526825          NA
5    2      b  1.23820506  0.26526825
6    3      b  0.09276648  1.23820506
7    4      b -0.09253594  0.09276648

जैसा कि @ बीआरडीडी द्वारा उल्लेख किया गया है, यह स्पष्ट रूप से मानता है कि मूल्य पहले से ही समूह द्वारा क्रमबद्ध किया गया है। यदि नहीं, तो इसे समूह द्वारा क्रमबद्ध करें, या इसका उपयोग करें order_by में तर्क lag। यह भी ध्यान दें कि एक के कारण मौजूदा मुद्दा dplyr के कुछ संस्करणों के साथ, सुरक्षा, तर्क और नामस्थान के लिए स्पष्ट रूप से दिया जाना चाहिए।


46
2017-10-10 04:38



जब आप एक अंतराल बनाने के लिए आवश्यक सभी चरों पर लूपिंग करते समय इसका उपयोग कैसे करते हैं? - derp92
क्या आपका मतलब है कि आपके पास कई कॉलम हैं जिन्हें आप अंतराल पर करना चाहते हैं? चेक आउट mutate_each, mutate_all, mutate_at आदि आदेश - Alex
क्या यह समाधान मानता है कि स्रोत डेटासेट उचित रूप से पूर्व-क्रमबद्ध है? - Brian D
@ ब्रायन डी हाँ यह करता है, लेकिन यह ओपी की टिप्पणी में निहित है कि वे चाहते हैं value समूह द्वारा लगी हुई - Alex
@ ब्रायनडी मुझे नहीं लगता कि इसमें कोई भ्रम है lag मेरे दिमाग में पिछले मूल्यों को लेना और उन्हें स्थानांतरित करना है n पदों, लेकिन यह ध्यान देने योग्य है कि आप अंतराल के लिए एक आदेश तर्क पारित कर सकते हैं, धन्यवाद। - Alex


आधार आर में, यह काम करेगा:

data$lag.value <- c(NA, data$value[-nrow(data)])
data$lag.value[which(!duplicated(data$groups))] <- NA

पहली पंक्ति लगी हुई (+1) अवलोकनों की एक स्ट्रिंग जोड़ती है। दूसरी स्ट्रिंग प्रत्येक समूह की पहली प्रविष्टि को सुधारती है, क्योंकि पिछले समूह से पिछला अवलोकन है।

ध्यान दें कि data प्रारूप का है data.frame उपयोग नहीं करने के लिए data.table


4
2018-04-14 13:32





यदि आप यह सुनिश्चित करना चाहते हैं कि आपने डेटा को ऑर्डर करने के साथ किसी भी मुद्दे से परहेज किया है, तो आप इसे डीएलईआर का उपयोग करके मैन्युअल रूप से कुछ कर सकते हैं:

df <- data.frame(Names = c(rep('Dan',50),rep('Dave',100)),
            Dates = c(seq(1,100,by=2),seq(1,100,by=1)),
            Values = rnorm(150,0,1))

df <- df %>% group_by(Names) %>% mutate(Rank=rank(Dates),
                                    RankDown=Rank-1)

df <- df %>% left_join(select(df,Rank,ValueDown=Values,Names),by=c('RankDown'='Rank','Names')
) %>% select(-Rank,-RankDown)

head(df)

या वैकल्पिक रूप से मुझे इसे एक चुने हुए समूहिंग चर (ओं), रैंकिंग कॉलम (जैसे तिथि या अन्यथा) के साथ फ़ंक्शन में डालने का विचार पसंद है, और चयनित संख्याएं हैं। इसके लिए आलसी के साथ-साथ dplyr भी आवश्यक है।

groupLag <- function(mydf,grouping,ranking,lag){
  df <- mydf
  groupL <- lapply(grouping,as.symbol)

  names <- c('Rank','RankDown')
  foos <- list(interp(~rank(var),var=as.name(ranking)),~Rank-lag)

  df <- df %>% group_by_(.dots=groupL) %>% mutate_(.dots=setNames(foos,names))

  selectedNames <- c('Rank','Values',grouping)
  df2 <- df %>% select_(.dots=selectedNames)
  colnames(df2) <- c('Rank','ValueDown',grouping)

  df <- df %>% left_join(df2,by=c('RankDown'='Rank',grouping)) %>% select(-Rank,-RankDown)

  return(df)
}

groupLag(df,c('Names'),c('Dates'),1)

2
2018-01-24 10:25





मैं पिछले मुद्दों को दो तरीकों का उल्लेख करके पूरक करना चाहता हूं जिसमें मैं इस मामले में महत्वपूर्ण मामले में पहुंचता हूं जब आपको गारंटी नहीं दी जाती है कि प्रत्येक समूह के पास हर समय अवधि के लिए डेटा होता है। यही है, आपके पास अभी भी नियमित रूप से समय की श्रृंखला है, लेकिन वहां और वहां मिसाल हो सकती है। मैं सुधार करने के दो तरीकों पर ध्यान केंद्रित करूंगा dplyr उपाय।

हम उसी डेटा से शुरू करते हैं जिसका आपने उपयोग किया था ...

library(dplyr)
library(tidyr)

set.seed(1)
data_df = data.frame(time   = c(1:3, 1:4),
                     groups = c(rep(c("b", "a"), c(3, 4))),
                     value  = rnorm(7))
data_df
#>   time groups      value
#> 1    1      b -0.6264538
#> 2    2      b  0.1836433
#> 3    3      b -0.8356286
#> 4    1      a  1.5952808
#> 5    2      a  0.3295078
#> 6    3      a -0.8204684
#> 7    4      a  0.4874291

... लेकिन अब हम कुछ पंक्तियों को हटा देते हैं

data_df = data_df[-c(2, 6), ]
data_df
#>   time groups      value
#> 1    1      b -0.6264538
#> 3    3      b -0.8356286
#> 4    1      a  1.5952808
#> 5    2      a  0.3295078
#> 7    4      a  0.4874291

सरल dplyr समाधान अब काम नहीं करता है

data_df %>% 
  arrange(groups, time) %>% 
  group_by(groups) %>% 
  mutate(lag.value = lag(value)) %>% 
  ungroup()
#> # A tibble: 5 x 4
#>    time groups  value lag.value
#>   <int> <fct>   <dbl>     <dbl>
#> 1     1 a       1.60     NA    
#> 2     2 a       0.330     1.60 
#> 3     4 a       0.487     0.330
#> 4     1 b      -0.626    NA    
#> 5     3 b      -0.836    -0.626

आप देखते हैं कि, हालांकि हमारे पास मामले के लिए मूल्य नहीं है (group = 'a', time = '3'), उपर्युक्त अभी भी मामले में अंतराल के लिए एक मूल्य दिखाता है (group = 'a', time = '4'), जो वास्तव में मूल्य है time = 2

सही बात dplyr उपाय

विचार यह है कि हम लापता (समूह, समय) संयोजन जोड़ते हैं। ये है बहुत मेमोरी-अक्षम जब आपके पास बहुत सारे संभव (समूह, समय) संयोजन होते हैं, लेकिन मानों को कम से कम कब्जा कर लिया जाता है।

dplyr_correct_df = expand.grid(
  groups = sort(unique(data_df$groups)),
  time   = seq(from = min(data_df$time), to = max(data_df$time))
) %>% 
  left_join(data_df, by = c("groups", "time")) %>% 
  arrange(groups, time) %>% 
  group_by(groups) %>% 
  mutate(lag.value = lag(value)) %>% 
  ungroup()
dplyr_correct_df
#> # A tibble: 8 x 4
#>   groups  time   value lag.value
#>   <fct>  <int>   <dbl>     <dbl>
#> 1 a          1   1.60     NA    
#> 2 a          2   0.330     1.60 
#> 3 a          3  NA         0.330
#> 4 a          4   0.487    NA    
#> 5 b          1  -0.626    NA    
#> 6 b          2  NA        -0.626
#> 7 b          3  -0.836    NA    
#> 8 b          4  NA        -0.836

ध्यान दें कि अब हमारे पास एनए है (group = 'a', time = '4'), जो अपेक्षित व्यवहार होना चाहिए। के जैसा (group = 'b', time = '3')

कक्षा का उपयोग करके कठिन लेकिन सही समाधान भी zoo::zooreg

इस समाधान को स्मृति के मामले में बेहतर काम करना चाहिए जब मामलों की मात्रा बहुत बड़ी है, क्योंकि लापता मामलों को एनए के साथ भरने की बजाय, यह सूचकांक का उपयोग करता है।

library(zoo)

zooreg_correct_df = data_df %>% 
  as_tibble() %>% 
  # nest the data for each group
  # should work for multiple groups variables
  nest(-groups, .key = "zoo_ob") %>%
  mutate(zoo_ob = lapply(zoo_ob, function(d) {

    # create zooreg objects from the individual data.frames created by nest
    z = zoo::zooreg(
      data      = select(d,-time),
      order.by  = d$time,
      frequency = 1
    ) %>% 
      # calculate lags
      # we also ask for the 0'th order lag so that we keep the original value
      zoo:::lag.zooreg(k = (-1):0) # note the sign convention is different

    # recover df's from zooreg objects
    cbind(
      time = as.integer(zoo::index(z)),
      zoo:::as.data.frame.zoo(z)
    )

  })) %>% 
  unnest() %>% 
  # format values
  select(groups, time, value = value.lag0, lag.value = `value.lag-1`) %>% 
  arrange(groups, time) %>% 
  # eliminate additional periods created by lag
  filter(time <= max(data_df$time))
zooreg_correct_df
#> # A tibble: 8 x 4
#>   groups  time   value lag.value
#>   <fct>  <int>   <dbl>     <dbl>
#> 1 a          1   1.60     NA    
#> 2 a          2   0.330     1.60 
#> 3 a          3  NA         0.330
#> 4 a          4   0.487    NA    
#> 5 b          1  -0.626    NA    
#> 6 b          2  NA        -0.626
#> 7 b          3  -0.836    NA    
#> 8 b          4  NA        -0.836

अंत में, जांचें कि दोनों सही समाधान वास्तव में बराबर हैं:

all.equal(dplyr_correct_df, zooreg_correct_df)
#> [1] TRUE

1
2018-06-27 15:54