У меня есть таблица данных в формате:
myTable <- data.table(Col1 = c("A", "A", "A", "B", "B", "B"), Col2 = 1:6)
print(myTable)
Col1 Col2
1: A 1
2: A 2
3: A 3
4: B 4
5: B 5
6: B 6
Я хочу показать только наивысший результат для каждой категории в столбце 1, затем свернуть все остальные и представить их сумму в столбце 2. Должно получиться так:
print(myTable)
Col1 Col2
1: A 3
2: Others 3
3: B 6
4: Others 9
Мне удалось это сделать с помощью следующего кода:
unique <- unique(myTable$Col1) # unique values in Col1
myTable2 <- data.table() # empty data table to populate
for(each in unique){
temp <- myTable[Col1 == each, ] # filter myTable for unique Col1 values
temp <- temp[order(-Col2)] # order filtered table increasingly
sumCol2 <- sum(temp$Col2) # sum of values in filtered Col2
temp <- temp[1, ] # retain only first element
remSum <- sumCol2 - sum(temp$Col2) # remaining sum in Col2 (without first element)
temp <- rbindlist(list(temp, data.table("Others", remSum))) # rbind first element and remaining elements
myTable2 <- rbindlist(list(myTable2, temp)) # populate data table from beginning
}
Это работает, но я пытаюсь сократить очень большую таблицу данных, поэтому на это уходит вечность.
Есть ли лучший способ подойти к этому?
Спасибо.
ОБНОВИТЬ: На самом деле моя процедура немного сложнее. Я подумал, что смогу разработать его сам после того, как освоу основы, но, похоже, вместо этого мне понадобится дополнительная помощь. Я хочу отобразить 5 самых высоких значений в столбце Col1 и свернуть остальные, но некоторые записи в столбце Col1 не имеют 5 значений; в этом случае должны отображаться все записи, и не следует добавлять строку «Другие».
Собственно я имел ввиду высшее значение, поправил, спасибо.
ну .... это все меняет. Я обновил свое решение.
Это плохой формат для использования в анализе, поскольку вы больше не можете распознать первые «Другие» как связанные с A, кроме как на основе текущей сортировки. В любом случае, повторно ваше «обновление», может быть, myTable[order(-Col2), lapply(.SD, sum), by=.(Col1, r = as.character(replace(r <- rowid(Col1), r > 5, "other")))]
, хотя вам нужно будет предоставить соответствующий пример, чтобы мы могли подтвердить ... Поскольку было опубликовано так много ответов, вы можете опубликовать новый вопрос, если не можете его понять.
Здесь данные разбиты на группы в соответствии со значением Col1
(by = Col1
). .N
- это индекс последней строки в данной группе, поэтому c(Col2[.N], sum(Col2) - Col2[.N]))
дает последнее значение Col2
и сумму Col2
за вычетом последнего значения. Вновь созданные переменные окружены .()
, потому что .()
является псевдонимом для функции list()
при использовании data.table, а созданные столбцы должны быть в списке.
library(data.table)
setDT(df)
df[, .(Col1 = c(Col1, 'Others'),
Col2 = c(Col2[.N], sum(Col2) - Col2[.N]))
, by = Col1][, -1]
# Col1 Col2
# 1: A 3
# 2: Others 3
# 3: B 6
# 4: Others 9
Превосходно! Не могли бы вы пояснить синтаксис? Хотя мне это нравится, я все еще новичок в пакете data.table. На самом деле моя процедура немного сложнее: я хочу сохранить 5 верхних записей, но некоторые значения в Col1 не имеют 5 записей; в этих случаях следует сохранить все записи и не включать строку «Прочие».
Если бы это просто вопрос отображения вещей, вы могли бы пакеты 'table':
others <- function(x) sum(x)-last(x)
df %>% tabular(Col1*(last+others) ~ Col2, .)
# Col1 Col2
# A last 3
# others 3
# B last 6
# others 9
do.call(
rbind, lapply(split(myTable, factor(myTable$Col1)), function(x) rbind(x[which.max(x$Col2),], list("Other", sum(x$Col2[-which.max(x$Col2)]))))
)
# Col1 Col2
#1: A 3
#2: Other 3
#3: B 6
#4: Other 9
Я это сделал! Я сделал новый myTable для иллюстрации. Я хочу сохранить только 4 самых высоких значения по категориям и свернуть остальные.
set.seeed(123)
myTable <- data.table(Col1 = c(rep("A", 3), rep("B", 5), rep("C", 4)), Col2 = sample(1:12, 12))
print(myTable)
Col1 Col2
1: A 8
2: A 5
3: A 2
4: B 7
5: B 10
6: B 9
7: B 12
8: B 11
9: C 4
10: C 6
11: C 3
12: C 1
# set key to Col2, it will sort it increasingly
setkey(myTable, Col2)
# if there are more than 4 entries by Col1 category, will return all information, otherwise will return 4 entries completing with NA
myTable <- myTable[,.(Col2 = Col2[1:max(c(4, .N))]) , by = Col1]
# will print in Col1: 4 entries of Col1 category, then "Other"
# will print in Col2: 4 last entries of Col2 in that category, then the remaining sum
myTable <- myTable[, .(Col1 = c(rep(Col1, 4), "Other"), Col2 = c(Col2[.N-3:0], sum(Col2) - sum(Col2[.N-3:0]))), by = Col1]
# removes rows with NA inserted in first step
myTable <- na.omit(myTable)
# removes rows where Col2 = 0, inserted because that Col1 category had exactly 4 entries
myTable <- myTable[Col2 != 0]
Ооооооо!
Вот базовое решение R и эквивалент dplyr
:
res <- aggregate(Col2 ~.,transform(
myTable, Col0 = replace(Col1,duplicated(Col1,fromLast = TRUE), "Other")), sum)
res[order(res$Col1),-1]
# Col0 Col2
# 1 A 3
# 3 Other 3
# 2 B 6
# 4 Other 9
myTable %>%
group_by(Col0= Col1, Col1= replace(Col1,duplicated(Col1,fromLast = TRUE),"Other")) %>%
summarize_at("Col2",sum) %>%
ungroup %>%
select(-1)
# # A tibble: 4 x 2
# Col1 Col2
# <chr> <int>
# 1 A 3
# 2 Other 3
# 3 B 6
# 4 Other 9
"I want show only the first result for each category in Col1"
похоже вы хотите показать последнее.