Выберите не-значения NA и назначьте переменные на основе имен столбцов - PullRequest
0 голосов
/ 22 мая 2018

Мне дали набор данных, в котором участники проводят семь испытаний в одном из 16 возможных состояний.16 условий вытекают из схемы 2x2x2x2 (то есть есть четыре управляемые переменные, каждая из которых имеет два уровня).Допустим, у Var1 есть уровни «Пот» и «Пан».Var2 имеет уровни «Привет» и «Низкий».Вар 3 имеет уровни «Вверх» и «Вниз».Вар 4 имеет уровни «Один» и «Два».

Набор данных включает в себя столбцы для каждого наблюдения в каждом условии для каждого участника, то есть для каждой строки имеется 112 (16 * 7) столбцов (вместе с некоторыми столбцами, содержащими демографические данные и т. Д.), 105 (15* 7) из которых пусты.Условия кодируются в метках столбцов, поэтому столбцы варьируются от «PotHiUp1» до «PanLowDown2».

Таким образом, данные выглядят так:

Var1 <- c('Pot', 'Pan')
Var2 <- c('Hi', 'Low')
Var3 <- c('Up', 'Down')
Var4 <- c('One','Two')
Obs <- seq(1,7,1)

df <- expand.grid(Var1,Var2,Var3,Var4,Obs)
df <- df %>% 
  arrange(Var1,Var2,Var3,Var4)

x <- apply(df,1,paste,collapse="")

id <- seq(1,16,1)
age <- rep(20,16)
df <- as.data.frame(cbind(id, age))

for (i in 1:length(x)) {
  df[,ncol(df)+1] <- NA
  names(df)[ncol(df)] <- paste0(x[i])
}

j <- seq(3,ncol(df),7)

for (i in 1:nrow(df)) {
    df[i,c(j[i]:(j[i]+6))] <- 10
}

Я хочу привести в порядок эти данныетак, чтобы в каждой строке было 4 столбца (по одному для каждой переменной), определяющих условие, и 7 столбцов с наблюдениями.

Мое решение состоит в том, чтобы фильтровать данные с помощью dplyr, например:

Df1 <- df %>% 
  filter(!is.na(PotHiUpOne1)) %>% 
  mutate(Var1 = 'pot', Var2 = 'hi', Var3 = 'up', Var4 = 'one')

затем удалите столбцы NA следующим образом:

Df1 <- Filter(function(x)!all(is.na(x)), Df1)

Я делаю это 16 раз (по одному разу для каждого условия) и затем, наконец, связываю 16 фреймов данных, которые я создал, после переименования семи оставшихся наблюдений.столбцы, чтобы они соответствовали.

Мне интересно, может ли кто-нибудь предложить более эффективный подход, предпочтительно с использованием dplyr.

Редактировать: я должен добавить, что когда я говорю "эффективный", я действительно имею в видуболее элегантный подход с точки зрения кода, а не что-то, что будет работать быстро (набор данных не большой) - то есть то, что не включает в себя запись более или менее одного и того же блкод 16 раз.

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

Хорошо, это не так чисто, как решение mt1022, но оно не требует data.table.Требуется dplyr для функции case_when и base для всего остального.

Определение двух новых функций, find_conditions и transform.

find_conditions немного громоздконо может быть полезно, так как вы можете легко добавить новые определения, если это необходимо.

find_conditions <- function(x){
  x1 <- x
  x1 <- case_when(
    x1 == "PotHiUpOne" ~ c("pot", "hi", "up", "one"),
    x1 == "PotHiUpTwo" ~ c("pot", "hi", "up", "two"),
    x1 == "PotHiDownOne" ~ c("pot", "hi", "down", "one"),
    x1 == "PotHiDownTwo" ~ c("pot", "hi", "down", "two"),
    x1 == "PotLowUpOne" ~ c("pot", "low", "up", "one"),
    x1 == "PotLowUpTwo" ~ c("pot", "low", "up", "two"),
    x1 == "PotLowDownOne" ~ c("pot", "low", "down", "one"),
    x1 == "PotLowDownTwo" ~ c("pot", "low", "down", "two"),
    x1 == "PanHiUpOne" ~ c("pan", "hi", "up", "one"),
    x1 == "PanHiUpTwo" ~ c("pan", "hi", "up", "two"),
    x1 == "PanHiDownOne" ~ c("pan", "hi", "down", "one"),
    x1 == "PanHiDownTwo" ~ c("pan", "hi", "down", "two"),
    x1 == "PanLowUpOne" ~ c("pan", "low", "up", "one"),
    x1 == "PanLowUpTwo" ~ c("pan", "low", "up", "two"),
    x1 == "PanLowDownOne" ~ c("pan", "low", "down", "one"),
    x1 == "PanLowDownTwo" ~ c("pan", "low", "down", "two")
  )
  if(NA %in% x1){
    cat("Error: Input not recognized")
  }
  else{
    return(x1)
  }
}

transform берет строку из df и преобразует ее в нужную форму.Это зависит от функции find_conditions, которую мы уже определили.

transform <- function(row){
  row1 <- row[3:length(row)] # Forget about id and age columns, will put them back at the end

  cols <- colnames(row1)[!is.na(row1)] # Get names of the columns which are not NA
  cols <- substr(cols,1,nchar(cols)-1) # Slice off the last character (The number)
  cols <- cols[!duplicated(cols)] # Columns should all have the same name now - find it by removing duplicates
  vars <- find_conditions(cols) # Use our new find_conditions function to break it up into individual conditions

  row1 <- row1[!is.na(row1)] # Keep only non-NA values

  new_row <- c(row[1:2],row1,vars) # put id, age, row1, vars together
  as.vector(unlist(new_row)) # Return as an unnamed vector
}

Теперь использовать эти две функции довольно просто:

l1 <- list() # Initialize empty list
for (i in 1:nrow(df)){
  l1[[i]] <- transform(df[i,]) # Fill list with transformed rows
}

DF1 <- data.frame(do.call("rbind",l1)) # Bind the transformed rows together

Оставьте это в цикле, как вы сказали,не большой набор данных.Удачи!

0 голосов
/ 22 мая 2018

Надеюсь, это то, что вы хотите:

library(data.table)

dtt <- as.data.table(df)
dtt2 <-  melt(dtt, id.vars = c('id', 'age'))[!is.na(value)]
dtt2[, c('var1', 'var2', 'var3', 'var4', 'cond') := tstrsplit(variable, '(?!^)(?=[A-Z0-9])', perl = T)]
dtt2[, variable := NULL]
dcast(dtt2, ... ~ cond, value.var = 'value')
#     id age var1 var2 var3 var4  1  2  3  4  5  6  7
#  1:  1  20  Pot   Hi   Up  One 10 10 10 10 10 10 10
#  2:  2  20  Pot   Hi   Up  Two 10 10 10 10 10 10 10
#  3:  3  20  Pot   Hi Down  One 10 10 10 10 10 10 10
#  4:  4  20  Pot   Hi Down  Two 10 10 10 10 10 10 10
#  5:  5  20  Pot  Low   Up  One 10 10 10 10 10 10 10
#  6:  6  20  Pot  Low   Up  Two 10 10 10 10 10 10 10
#  7:  7  20  Pot  Low Down  One 10 10 10 10 10 10 10
#  8:  8  20  Pot  Low Down  Two 10 10 10 10 10 10 10
#  9:  9  20  Pan   Hi   Up  One 10 10 10 10 10 10 10
# 10: 10  20  Pan   Hi   Up  Two 10 10 10 10 10 10 10
# 11: 11  20  Pan   Hi Down  One 10 10 10 10 10 10 10
# 12: 12  20  Pan   Hi Down  Two 10 10 10 10 10 10 10
# 13: 13  20  Pan  Low   Up  One 10 10 10 10 10 10 10
# 14: 14  20  Pan  Low   Up  Two 10 10 10 10 10 10 10
# 15: 15  20  Pan  Low Down  One 10 10 10 10 10 10 10
# 16: 16  20  Pan  Low Down  Two 10 10 10 10 10 10 10
...