Я ищу быстрый способ преобразования данных из длинного в широкий формат. Прямо сейчас я попробовал код с гнездом для циклов, хотя работа выполнена, для создания вывода требуется много времени.
SN NN EE Service_tier
A B C economy
B C C economy
P Q R regular
Q S R regular
S R R regular
H I L economy
I J L economy
J K L economy
K L L economy
Ожидаемый результат, как показано ниже
SN hop1 hop2 hop3 hop4 service_tier
A B C economy
P Q S R regular
H I J K L economy
в настоящее время приведенный ниже код выполняет свою работу. Я уверен, что есть эффективный и чистый способ сделать это.
for (i in 1:lasrow){
sn <- raw_d[i,1]
nn <- raw_d[i,2]
en <- raw_d[i,3]
lc <- 1
if (nn == en){
d[lr,lc]<-sn
d[lr,lc+1]<-nn
d[lr,lc+2]<-en
lr <- lr+1
}
else{
while(nn!=en){
d[lr,lc]<-sn
lc <- lc+1
next_d <- filter(raw_d,raw_d$SN==sn,raw_d$EN==en)
if (dim(next_d)[1]==0){
d[lr,lc]<-"broken bf"
lc <- lc+1
break
}else{
sn <- next_d$NN
nn <- next_d$NN
}
}
d[lr,lc]<-en
lr<-lr+1
}
}
экономика = {P, Q, R, S} может появиться и в экономике, но для заданных P и S у нас будет только один путь, а не несколько путей.
Один из вариантов — создать уникальную последовательность, используя rleid
из data.table
, gather
фрейм данных в длинном формате, удалить дубликаты из каждой группы, назначить имена столбцов и spread
вернуть их в широкий формат.
library(dplyr)
library(tidyr)
df %>%
mutate(row = data.table::rleid(Service_tier)) %>%
gather(key, value, -Service_tier, -row) %>%
group_by(row) %>%
filter(!duplicated(value)) %>%
mutate(key = c("SN", paste0("hop", 1:(n() - 1)))) %>%
spread(key, value) %>%
ungroup() %>%
select(-row) %>%
select(SN, starts_with("hop"), Service_tier)
# A tibble: 3 x 6
# SN hop1 hop2 hop3 hop4 Service_tier
# <chr> <chr> <chr> <chr> <chr> <fct>
#1 A B C NA NA economy
#2 H I J K L economy
#3 P Q S R NA regular
Мы можем использовать data.table
. Преобразуйте «data.frame» в «dat.table» (setDT(df1)
, сгруппированный по rleid
на «Service_tier», измените значение «SN» на first
элемент, сгруппированный по «grp», затем сгруппированный по «Service_tier», «SN ', получить элемент unique
Subset of Data.table и dcast
из «длинного» в «широкий» формат
library(data.table)
dcast(setDT(df1)[, SN := first(SN), rleid(Service_tier)][,
unique(unlist(.SD)), .(SN, Service_tier)],
SN + Service_tier ~ paste0("hop", rowid(SN)), value.var = "V1", fill = "")
# SN Service_tier hop1 hop2 hop3 hop4
#1: A economy B C
#2: H economy I J K L
#3: P regular Q S R
df1 <- structure(list(SN = c("A", "B", "P", "Q", "S", "H", "I", "J",
"K"), NN = c("B", "C", "Q", "S", "R", "I", "J", "K", "L"), EE = c("C",
"C", "R", "R", "R", "L", "L", "L", "L"), Service_tier = c("economy",
"economy", "regular", "regular", "regular", "economy", "economy",
"economy", "economy")), class = "data.frame", row.names = c(NA,
-9L))
Важным моментом здесь является определение того, какие строки принадлежат к какой группе. В ответах Ронак и Акрун используется rleid(Service_tier)
, предполагая, что изменение Service_tier
указывает на начало новой группы.
Это может быть предложено образцом набора данных, но не может считаться гарантированным. ИМХО, Service_tier
скорее атрибут, чем ключ. На самом деле, ОП проверяет NN == EE
в своем фрагменте кода, чтобы переключиться на новую группу.
В приведенных ниже решениях data.table группировка определяется cumsum(shift(NN == EE, fill = TRUE))
, который проверяет равенство fo NN
и EE
, откладывает результат до следующей строки, где начинается следующая группа, и перечисляет группы путем подсчета TRUE
с использованием cumsum()
.
В упрощенном варианте (без изменения формы) прыжки агрегируются функцией toString()
:
library(data.table)
setDT(d)[, .(SN = first(SN), hops = toString(NN), Service_tier = first(Service_tier)),
by = .(grp = cumsum(shift(NN == EE, fill = TRUE)))][]
grp SN hops Service_tier 1: 1 A B, C economy 2: 2 P Q, S, R regular 3: 3 H I, J, K, L economy
Для преобразования длинного формата в широкий используется dcast()
:
library(data.table)
library(magrittr) # piping used to improve readability
w <- setDT(d)[, .(SN = first(SN), hops = NN, Service_tier = first(Service_tier)),
by = .(grp = cumsum(shift(NN == EE, fill = TRUE)))] %>%
dcast(grp + ... ~ rowid(grp, prefix = "hop"), value.var = "hops", fill = "") %>%
setcolorder(c(1:2, 4:ncol(.), 3))
w
grp SN hop1 hop2 hop3 hop4 Service_tier 1: 1 A B C economy 2: 2 P Q S R regular 3: 3 H I J K L economy
setcolorder()
используется для перестановки столбцов в порядке, ожидаемом OP. Это делается на месте, т.е. без копирования всего объекта данных.
library(data.table)
d <- fread("SN NN EE Service_tier
A B C economy
B C C economy
P Q R regular
Q S R regular
S R R regular
H I L economy
I J L economy
J K L economy
K L L economy")
Я получаю сообщение об ошибке... Ошибка в [.data.frame
(x, , .(SN = first(SN), hops = toString(NN), Service_tier = first(Service_tier)), : неиспользуемый аргумент (by = .(grp = cumsum (сдвиг (NN == EE, заполнение = ИСТИНА))))
@VasukiRao Кажется, я мог забыть принудить d
к data.table с помощью setDT(d)
. Я изменил код. Пожалуйста, поднос еще раз. И спасибо за отчет.
когда я запускаю код, ошибка в ncol(w): объект 'w' не найден
Виноват. Еще одна ошибка копирования и вставки. Пожалуйста, смотрите измененный код.
отлично, работает безупречно, каждое интересное решение по сравнению с циклом for
Кажется, что ваши наборы являются взаимоисключающими, например, регулярный = {P, Q, R, S}, но P, Q, R и S не являются частью никакого другого «набора». Всегда ли это будет правдой? Может ли «А» когда-либо быть частью обычного и эконом-класса? Правильный метод расширения этих данных будет зависеть от вашего ответа.