Я хотел бы разделить строки, где x1 и x2 == 9. В моем реальном наборе более 200 столбцов, в которых имя столбца начинается с той же строки. Приведенный ниже фиктивный код создает меньшую выборку данных. В идеале я бы хотел сделать это с помощью пакета R data.table, если это возможно.
df <- data.frame('id'=c(1,2,3), 'x1'=c(9,9,4), 'x2'=c(9,9,4))
head(df)
# does not work, but thought perhaps I could have defined the columns via a paste and then subset where columns were equal to 9.
df[which(paste0("x", 1:2)==9), ]
Обновление: извините, если я не понял. Я знаю, что просто добавляю фильтр для x1
и x2
. Проблема в том, что реальные данные состоят из более чем 200 столбцов: x1:x200
. Я ищу более чистое решение, чем то, что предлагается ниже.
Пытаться:
df[df$x1 == 9 & df$x2 == 9,]
РЕДАКТИРОВАТЬ (неправильно понял, теперь это должно сработать):
for (i in 2:200) {df = df[df[,i] == 9,]}
Проблема в том, что у меня 200 столбцов с аналогичным соглашением об именах ... так что x1: x200, что сделало бы это решение беспорядочным.
Что-то вроде этого, может быть?
df[apply(df[, paste0("x", 1:200)] == 9, 1, all), ]
Это на самом деле чище, чем то, что я нашел. Считаю это лучшим ответом. Спасибо, Грегор.
Я согласен, это довольно ясно. Если вы заботитесь об эффективности, я бы рекомендовал отложить принятие этого ответа в течение нескольких часов - у некоторых специалистов по таблицам данных, вероятно, будут лучшие идеи, которые мы можем протестировать и найти самые быстрые.
Хорошо. Звучит разумно. Еще раз спасибо за ответ. Сэкономил несколько волос.
подумайте о том, чтобы принять ответ, если он решит вашу проблему @yokota (зеленая галочка слева)
@Moody_Mudskipper yokota принял мой ответ через несколько минут после того, как я его разместил. Я рекомендовал им подождать несколько часов, если у кого-то есть лучшее решение.
Хорошо, но я был бы удивлен, возможно, rowSums
будет быстрее, чем apply
, или вы могли бы использовать startsWith
, поэтому он не зависит от суффиксов строк, судя по всему, это довольно просто.
Вы также можете использовать grep
с apply
# Select all columns that have (colnames) "x"
col.names <- grep("x",colnames(df), value = TRUE)
# Select rows where row == 9
sel <- apply(df[,col.names], 1, function(row) 9 %in% row)
df[sel,]
И на выходе
id x1 x2
1 1 9 9
2 2 9 9
Спасибо, @Miha. Это решение тоже хорошо работает. Я воспользуюсь советом Грегора и к концу дня попробую протестировать различные решения, чтобы получить «ответ».
Разница между этим ответом и моим ответом заключается в том, как строятся имена столбцов - это хорошо для сопоставления с образцом, мое - для их построения. Основная часть ответа - apply
- мой ответ применяет функцию all
, здесь используется %in%
- так что мой вернет ИСТИНА, только если все столбцов равно 9, этот возвращает ИСТИНА, если любой столбцов равно 9. Вам нужно уточнить какой вы хотите.
Расплав может позволить вам не записывать каждый столбец (для вашего случая> 2 столбцов):
> aTbl = as.data.table(df)
> aTbl[, all9sP := F]
> aTbl[, .SD
][, !'all9sP'
][, melt(.SD, id.vars=c('id'))
][, NVars := uniqueN(variable)
][value == 9
][, .(N9s=.N), .(id, NVars)
][, all9sP := N9s == NVars
][, aTbl[.SD, all9sP := i.all9sP, on=.(id)]
][all9sP == T
][, all9sP := NULL
][, .SD
]
id x1 x2
1: 1 9 9
2: 2 9 9
>
Если вам нужно эффективное базовое решение R, я бы просто использовал rowSums
, например.
cols <- paste0("x", 1:2)
df[rowSums(df[cols] == 9) == length(cols), ]
# id x1 x2
# 1 1 9 9
# 2 2 9 9
Если вам нужно решение data.table, я бы использовал двоичное соединение, например
library(data.table)
setDT(df)[as.list(rep(9, length(cols))), on = cols]
# id x1 x2
# 1: 1 9 9
# 2: 2 9 9
Данные
df <- data.frame(id = 1:3, x1 = c(9, 9, 4), x2 = c(9, 9, 4))
Спасибо за решение DT и предупреждение о применении @David Arenburg
Создать набор данных
ncols <- 5
cnms <- paste0("x", 1:ncols)
X <- data.table(ID = 1:1e6)
X[, (cnms) := NA_integer_]
X[, (cnms) := lapply(X = 1:ncols, sample, size = .N, x = 1:10)]
Найдите строки, в которых сумма равна 9
X1 <- X[, s := rowSums(.SD), .SDcols = cnms][s == 9, ][, s:= NULL][]
X1
Найдите строки, в которых все столбцы равны 9
X[, s := NULL]
ind <- rowSums(X[, lapply(.SD, is.element, set = 9), .SDcols = cnms])
X2 <- X[ind == length(cnms)][]
X2
Редактировать На самом деле это намного быстрее:
X[, s := NULL]
ind <- rowSums(X[, .SD , .SDcols = cnms] == 9)
X2 <- X[ind == length(cnms)][]
X2
Edit2 См. Ответ от https://stackoverflow.com/users/3001626/david-arenburg. Намного быстрее.
В tidyverse попробуйте rowwise
и используйте filter
как обычно
df %>%
rowwise() %>%
filter(x1 %in% 9 & x2 %in% 9 )
Source: local data frame [2 x 3]
Groups: <by row>
# A tibble: 2 x 3
id x1 x2
<dbl> <dbl> <dbl>
1 1 9 9
2 2 9 9
Поскольку вы хотите использовать
data.table
, я предполагаю, что эффективность имеет значение. Я бы рекомендовал использовать более крупный пример, который подойдет для тестирования. Может что то типаset.seed(47); nc = 40; nr = 5000; dt = data.table(matrix(sample(c(0, 9), size = nc * nr, replace = TRUE, prob = c(1, 99)), nrow = nr)); dt$id = 1:nr; setkey(dt, id)