Как заменить определенное значение фрейма данных неизвестным именем столбца? - PullRequest
9 голосов
/ 20 сентября 2019

У меня большой фрейм данных с неизвестными именами столбцов и числовыми значениями 1, 2, 3 или 4. Теперь я хочу заменить все 4 значения на его имя столбца, а все 1, 2 и 3 - на пустые значения.

Конечно, я могу сделать цикл некоторого вида, например:

df <- data.frame(id=1:8,unknownvarname1=c(1:4,1:4),unknownvarname2=c(4:1,4:1))
for (i in 2:length(df)){
  df[,i] <- as.character(df[,i])
  df[,i] <- mgsub::mgsub(df[,i],c(1,2,3,4),c("","","",names(df)[i]))  
}

Это будет результат:

  id unknownvarname1 unknownvarname2
1  1                 unknownvarname2
2  2                                
3  3                                
4  4 unknownvarname1                
5  5                 unknownvarname2
6  6                                
7  7                                
8  8 unknownvarname1 unknownvarname2

Для фрейма данных такого размера этосовершенно никаких проблем.Но когда я пытаюсь выполнить этот цикл на больших фреймах данных с размером до 30 000 и до 40 неизвестных переменных, цикл завершается годами.

Кто-нибудь знает о более быстром способе сделать это?Я пробовал такие функции, как mutate() из dplyr package, но мне не удалось заставить его работать.

Заранее большое спасибо!

Ответы [ 6 ]

7 голосов
/ 20 сентября 2019

Еще одна базовая опция R, использующая ifelse внутри lapply (все еще циклично работающая по столбцам, но векторизованная запись по столбцам):

df <- data.frame(id=1:8,unknownvarname1=c(1:4,1:4),unknownvarname2=c(4:1,4:1))
df[,2:3] <- lapply(2:3, function(x) { ifelse(df[,x] < 4, "", colnames(df)[x]) })

дает

  id unknownvarname1 unknownvarname2
1  1                 unknownvarname2
2  2                                
3  3                                
4  4 unknownvarname1                
5  5                 unknownvarname2
6  6                                
7  7                                
8  8 unknownvarname1         
7 голосов
/ 20 сентября 2019

Просто чтобы дать другой вариант с switch (хотя, поскольку эта функция не векторизована, ей нужно вложенное sapply в lapply, что не делает его "красивым" и эффективным ...):

По сути, switch работает с numeric как switch(myNumberToTest, caseIfOne, caseIfTwo, ...).

Так что вам нужно:

df[, 2:3] <- lapply(2:3, function(x) sapply(df[, x], switch, "", "", "", names(df)[x]))

df
#  id unknownvarname1 unknownvarname2
#1  1                 unknownvarname2
#2  2                                
#3  3                                
#4  4 unknownvarname1                
#5  5                 unknownvarname2
#6  6                                
#7  7                                
#8  8 unknownvarname1                
7 голосов
/ 20 сентября 2019

В одну сторону, используя базу R

#Replace all the values with 1:3 with blank
df[-1][sapply(df[-1], `%in%`, 1:3)] <- ""
#Get the row/column indices where value is 4
mat <- which(df == 4, arr.ind = TRUE)
#Exclude values from first column
mat <- mat[mat[, 2] != 1, ]
#Replace remaining entries with it's corresponding column names
df[mat] <- names(df)[mat[, 2]]
df

#  id unknownvarname1 unknownvarname2
#1  1                 unknownvarname2
#2  2                                
#3  3                                
#4  4 unknownvarname1                
#5  5                 unknownvarname2
#6  6                                
#7  7                                
#8  8 unknownvarname1                
6 голосов
/ 20 сентября 2019

Другая базовая возможность R с использованием sweep:

idx <- df[, -1] == 4
sw <- sweep(idx, 2, 1:2, FUN = '*') + 1
df[, -1] <- c("", colnames(df[, -1]))[sw]

, которая дает:

> df
  id unknownvarname1 unknownvarname2
1  1                 unknownvarname2
2  2                                
3  3                                
4  4 unknownvarname1                
5  5                 unknownvarname2
6  6                                
7  7                                
8  8 unknownvarname1

Это можно сократить до:

sw <- sweep(df[, -1] == 4, 2, 1:2, FUN = '*') + 1
df[, -1] <- c("", colnames(df[, -1]))[sw]
5 голосов
/ 21 сентября 2019

Еще одна опция, использующая col для выравнивания имен и значений:

sel <- df[-1]==4
df[-1] <- ""
df[-1][sel] <- names(df[-1])[col(df[-1])[sel]]

#  id unknownvarname1 unknownvarname2
#1  1                 unknownvarname2
#2  2                                
#3  3                                
#4  4 unknownvarname1                
#5  5                 unknownvarname2
#6  6                                
#7  7                                
#8  8 unknownvarname1
5 голосов
/ 20 сентября 2019

Несколько неэффективный вариант tidyverse.Это неэффективно, потому что мы должны вручную выбрать столбцы позже:

to_use <- names(df)[-1]
df %>% 
  mutate_at(vars(contains("unknown")),list(~ifelse(.==4,
                                             NA,
                                             ""))) -> new_df

new_df[-1] <-map2(new_df[-1], to_use,function(x,y) replace(x,is.na(x),y))

Менее ручной подход, который также имеет недостаток, заключающийся в неспецифичности:

 df %>% 
   map2(.,names(.), function(x, y) ifelse( x==4, y,"")) %>% 
   as.data.frame() %>% 
   mutate(id=row.names(.)) # might be a way around  with `.id`
  id unknownvarname1 unknownvarname2
1  1                 unknownvarname2
2  2                                
3  3                                
4  4 unknownvarname1                
5  5                 unknownvarname2
6  6                                
7  7                                
8  8 unknownvarname1 

Результат для подхода 1:

new_df
     id unknownvarname1 unknownvarname2
    1  1                 unknownvarname2
    2  2                                
    3  3                                
    4  4 unknownvarname1                
    5  5                 unknownvarname2
    6  6                                
    7  7                                
    8  8 unknownvarname1 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...