Р; Для каждого уровня в столбце A замените значения в столбце B, следуя условию в столбце C - PullRequest
3 голосов
/ 01 апреля 2019

У меня есть список наблюдений видов в N местах. Наблюдения - это присутствие, отсутствие или неизвестность (1, 0, 'na'). Что мне нужно сделать, это для каждого вида выполнить условие:

для каждого САЙТА, ​​если в% SITE нет 1%, заменить все 0 на 'na'

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

Вот несколько фиктивных данных:

x <- c(1,2,3,4,5,6,7,8,9,10)
site <- c(1,1,1,2,2,2,3,3,3,1)
sp1 <- factor(c(1,1,'na','na',0,0,'na','na','na',0))
sp2 <- factor(c(0,0,1,1,'na','na',0,1,'na','na'))
table <- cbind.data.frame(x,site,sp1,sp2)

И что я сделал:

for (j in c(3:4)){
  site.present <- unique(table$site[which(table[,j]==1)])
  for (i in (1:length(table[,j]))) {
    ifelse(!(table[i,2]%in%site.present), 
           ifelse(table[i,j]==0,table[i,j]<-'na',T),T)
  }
}

В этом примере [5,3] и [6,3] должны стать «na» вместо 0 (потому что для sp1 нет присутствия на сайте 2). Приведенный выше код работает, но он кажется глупым для обработки миллионов записей ...

Очень ценится!

Ответы [ 3 ]

1 голос
/ 01 апреля 2019

Использование dplyr и base::replace. Мы можем заменить любой ноль на NA, если в этом месте нет видов, равных 1.

library(dplyr)
df <- table

df %>% mutate_all(~as.numeric(as.character(.))) %>% 
       group_by(site) %>% 
       #mutate(sp1_mod=replace(sp1,all(sp1!=1, na.rm = TRUE) & sp1==0,NA)) #for one column
       mutate_at(vars('sp1','sp2'), list(~replace(.,all(.!=1, na.rm = TRUE) & .==0,NA))) 

Кроме того, вместо именования переменных внутри vars одна за другой мы можем использовать один из Выбрать помощников см. ?dplyr::select, например. мы можем использовать matches для сопоставления любых имен столбцов, начинающихся с sp и с цифрой или более

mutate_at(vars(matches('sp\\d+')), list(~replace(.,any(.==1, na.rm = TRUE) & .==0,NA)))
1 голос
/ 01 апреля 2019

Это то, что вы ищете?

library(dplyr)

table %>%
  group_by(site) %>%
  mutate(sp1 = if_else(
    !any(sp1 == 1) & sp1 == 0,
    "na",
    as.character(sp1)
  ))
0 голосов
/ 01 апреля 2019

Если я вас правильно понимаю, вам нужно компактное и быстрое решение, которое можно применять сразу ко всему диапазону от 1 до n видов.

Сначала я бы reshape данные в длинном формате, а затем установите NA, используя by sp*, если это элемент c(0, NA) для каждого site.В-третьих, мы можем дополнительно reshape вернуться к исходному большому формату.

tmp <- reshape(dat, varying=list(3:ncol(dat)), v.names="sp", idvar=1:2, direction="long")
tmp <- do.call(rbind, by(tmp, tmp[c("site", "time")], function(x)
  if (all(x$sp %in% c(0, NA))) cbind(x[-4], sp=NA) else x))
dat <- reshape(tmp, timevar="time", idvar=c("x", "site"), direction="wide", sep="")
dat
#         x site  sp1  sp2
# 1.1.1   1    1    1    0
# 2.1.1   2    1    1    0
# 3.1.1   3    1 <NA>    1
# 10.1.1 10    1    0 <NA>
# 4.2.1   4    2 <NA>    1
# 5.2.1   5    2 <NA> <NA>
# 6.2.1   6    2 <NA> <NA>
# 7.3.1   7    3 <NA>    0
# 8.3.1   8    3 <NA>    1
# 9.3.1   9    3 <NA> <NA>

Если мы хотим большей скорости, мы могли бы использовать melt и dcast для процесса преобразования из data.table пакет, который почти удваивает скорость.Код немного меняется:

library(data.table)
tmp <- melt(dat, id.vars=c("x", "site"), variable.name="time", value.name="sp")
tmp <- do.call(rbind, by(tmp, tmp[c("site", "time")], function(x)
  if (all(x$sp %in% c(0, NA))) cbind(x[-4], sp=NA) else x))
dcast(tmp, x + site ~ time, value.var="sp")

Чтобы проверить, работают ли оба, увеличьте набор данных до количества видов Zoraptera, равного 28, и снова запустите код:

set.seed(42)
n <- 28 - 2
add <- setNames(as.data.frame(
  replicate(n, factor(sample(c(1, 0, NA), nrow(dat), replace=TRUE)))), 
  paste0("sp", 3:(n + 2)))
dat <- cbind(dat, add)

Данные

# I'd rather use a neutral name for the data, since `table` is a function name, see `?table`
dat <- structure(list(x = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), site = c(1, 
1, 1, 2, 2, 2, 3, 3, 3, 1), sp1 = structure(c(2L, 2L, 3L, 3L, 
1L, 1L, 3L, 3L, 3L, 1L), .Label = c("0", "1", "na"), class = "factor"), 
    sp2 = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 1L, 2L, 3L, 3L), .Label = c("0", 
    "1", "na"), class = "factor")), class = "data.frame", row.names = c(NA, 
-10L))

# first thing to do is make proper NAs!
levels(dat$sp1) <- levels(dat$sp2) <- c(0, 1, NA)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...