2016-09-19 54 views
7

benim [basitleştirilmiş] Veri şuna benzer:R: döngü önceki sütuna dayalı koşullu deyimi tarafından doldurulan yeni sütunlar oluşturmak için

id = sample(1:20, 5) 
first_active = c(1,1,1,2,3) 
week1 = c(1,1,1,0,0) 
week2 = c(1,0,0,1,0) 
week3 = c(1,0,1,0,1) 
week4 = c(1,0,0,0,1) 
week5 = c(0,0,0,0,1) 

df = data.frame(cbind(id, first_active, week1, week2, week3, week4, week5)) 

Ben döngü için oluşturmak istediğinizi olur:

i) Aynı data.frame'de, p1, p2, ... sütunlarını, 1. haftaya, ... sütunlarına karşılık gelen sütunlar oluşturun ve bunları aşağıdakilerle doldurun:

i) İlgili hafta değeri 0 değilse "etkin"

belirli bir hafta değeri 0 ise,

ii) daha sonra bir önceki p-sütunlar durumunu kontrol if p[i-1] == "active" then "lapsed1"

belirli bir hafta için değeri 0 iii), sonra önceki p-sütunlar durumunu kontrol if p[i-1] == "lapsed[j]" then "lapsed[j+1]"

() dplyr içinde mutate kullanılarak

iv) olarak, NA

yukarıdaki örnekte bu çözüm olacaktır dönüş:

df %>% 
mutate(p1 = ifelse(week1 != 0, "active", NA), 
     p2 = ifelse(week2 !=0, "active", 
        ifelse(p1 == "active", "lapsed1", NA)), 
     p3 = ifelse(week3 !=0, "active", 
        ifelse(p2 == "lapsed1", "lapsed2", 
        ifelse(p2 == "active", "lapsed1", NA))), 
     p4 = ifelse(week4 !=0, "active", 
        ifelse(p3 == "lapsed2", "lapsed3", 
        ifelse(p3 == "lapsed1", "lapsed2", 
         ifelse(p3 == "active", "lapsed1", NA)))), 
     p5 = ifelse(week5 !=0, "active", 
        ifelse(p4 == "lapsed3", "lapsed4", 
        ifelse(p4 == "lapsed2", "lapsed3", 
         ifelse(p4 == "lapsed1", "lapsed2", 
           ifelse(p4 == "active", "lapsed1", NA))))) 
) 


id first_active week1 week2 week3 week4 week5  p1  p2  p3  p4  p5 
    9   1  1  1  1  1  0 active active active active lapsed1 
    5   1  1  0  0  0  0 active lapsed1 lapsed2 lapsed3 lapsed4 
14   1  1  0  1  0  0 active lapsed1 active lapsed1 lapsed2 
    3   2  0  1  0  0  0 <NA> active lapsed1 lapsed2 lapsed3 
    8   3  0  0  1  1  1 <NA> <NA> active active active 

istediğim Özgün verilere atıfta bulunulacak 'hafta' sütunları olduğu için, bunu otomatik olarak yapacak bir işlev/döngü oluşturmak.

df$p1 = ifelse(df$week1 > 0, "active", NA) # initiating the first p-column 

for(i in 2:(ncol(df)-2)) { # defining dynamically number of periods 

column_to_write = paste0("p", i, sep="") # column to be populated 
prev_column = paste0("p", i-1, sep="") #previous p-column to the one that's being populated 
orig_column = paste0("week", i, sep="") #reference 'week' column 
j = 1 #initiating 'lapsed' number 

df[column_to_write] = ifelse(df[orig_column]> 0, "active", 
            ifelse(df[prev_column] == "active", paste("lapsed", j, sep=""), 
            ifelse(df[prev_column] == paste0("lapsed", j, sep=""), paste0("lapsed", j=j+1, sep=""), NA))) 

}

ama bu sadece bana "lapsed2" maksimum değerleri verir ve yeni sütunlar week[i] yerine p[i] denilen oluşturur: Şimdiye kadar başardı Ne

olduğunu. "lapsed" değerlerinde sayılar 2'ye ötesinde yükselmeye devam böylece

id first_active week1 week2 week3 week4 week5  p1 week2 week3 week4 week5 
    9   1  1  1  1  1  0 active active active active lapsed1 
    5   1  1  0  0  0  0 active lapsed1 lapsed2 <NA> <NA> 
14   1  1  0  1  0  0 active lapsed1 active lapsed1 lapsed2 
    3   2  0  1  0  0  0 <NA> active lapsed1 lapsed2 <NA> 
    8   3  0  0  1  1  1 <NA> <NA> active active active 

nasıl kodunu değiştiririm?

Yardımlarınız için teşekkürler! Kasia

+1

Elinizde olduğu gibi bu çok el ile yapılır. Verilerinizi uzun biçime ('reshape2 :: melt' veya' tidyr :: gather') dönüştürmelisiniz, böylece 1 ile 5 arasındaki değerlere sahip bir 'week_num' sütunu ve 1s ve 0s formuna sahip bir' week_val' sütunu Verileriniz. Sonra tek bir p 'sütun ekleyebilirsiniz.Bittiğinde, verilerinizi gerekirse geniş formatta alabilirsiniz ('reshape2 :: dcast' veya' tidyr :: spread'). Bu güzel bir şekilde ölçeklenecektir - 5 hafta veya 500 haftanız varsa kod aynı olacaktır. – Gregor

+0

Hm, veriler uzun biçimdeyse aynı kimlik için önceki döneme/haftaya nasıl başvurabilirim? –

+1

En kolay 'dplyr' veya 'data.table' ve onların gruplama işlevlerini kullanmak olacaktır. 'Id' ile gruplama, 'week_num' ile sıralama, sonra' p = ifelse (week_val! = 0,' active ', NA) 'gibi bir şey başlatır, daha sonra' paste 've' rle' ile bir şeyler yapabilirsiniz. değişkenler. [R: değerlerin ardışık oluşlarını sayın] (http://stackoverflow.com/q/19998836/903061) satırları boyunca bir şey. Şimdi tam bir çözüm yazmak için zamanım yok - Kimsenin bana vurmadığı bir zaman bulmaya çalışacağım. – Gregor

cevap

12

Sonunda for döngüsünü bıraktım ve bunun yerine @Gregor tarafından gönderilen önerileri takip ettim; Burada ne yaptım:

sonunda
df_long = melt(df, id.vars = c("id", "first_active")) #transformed my wide data to the long format 
colnames(df_long) = c("id", "first_active", "week_num", "week_orders") 


df_long = 
df_long %>% 
mutate(p_var = paste("p", substr(week_num, 5, 5), sep="")) %>% #created p-columns that correspond to respective weeks arrange(id, week_num) %>% 
group_by(id) %>% 
mutate(active_var = ifelse(week_orders != 0, "active", 
        ifelse(first_active < as.numeric(substr(week_num, 5,5)), 
         "lapsed", NA))) %>% #created a column that would return either "active", "lapsed" or NA depending on user activity 
    mutate(lapsed_num = sequence(rle(active_var)[["lengths"]]), #created a column that would count the number of occurences of "lapsed" for a given id; it would start counting from 1 if after "active" appeared 
      final = ifelse(active_var == "active", active_var, 
          ifelse(active_var == "lapsed", paste(active_var, lapsed_num, sep=""), NA))) %>% #finally, the column takes "active" status or coalesces "lapsed" with the sequence number 
select(id, first_active, week_num, week_orders, p_var, final) %>% 
          data.frame() 

verilerim şuna benziyordu: Yani

head(df_final, 25) 
active_var id first_active week_num week_orders p_var final 
    <NA> 3   2 week1   0 p1 <NA> 
    active 3   2 week2   1 p2 active 
    lapsed 3   2 week3   0 p3 lapsed1 
    lapsed 3   2 week4   0 p4 lapsed2 
    lapsed 3   2 week5   0 p5 lapsed3 
    active 5   1 week1   1 p1 active 

, Yapmam gereken tüm

(iki adımda) data.frame dökme oldu
df_weeks = dcast(df_long[, 1:4], id + first_active ~ week_num, value.var = "week_orders") 

df_p = dcast(df_long[, c(1:2, 5:6)], id + first_active ~ p_var, value.var = "final") 

Ve onlara katılmak ..

df_solution = inner_join(df_weeks, df_p) 

İşte bu kadar!

df_solution 
id first_active week1 week2 week3 week4 week5  p1  p2  p3  p4  p5 
3   2  0  1  0  0  0 <NA> active lapsed1 lapsed2 lapsed3 
5   1  1  0  0  0  0 active lapsed1 lapsed2 lapsed3 lapsed4 
8   3  0  0  1  1  1 <NA> <NA> active active active 
9   1  1  1  1  1  0 active active active active lapsed1 
14   1  1  0  1  0  0 active lapsed1 active lapsed1 lapsed2