Разделите строки и добавьте их как новую строку
У меня есть следующий набор данных:
df<-data.frame (fact= c("a,b,c,d","f,g,h,v"), value = c("0,1,0,1" , "0,0,1,0"))
Это данные:
fact value
1 a,b,c,d 0,1,0,1
2 f,g,h,v 0,0,1,0
Я хочу разбить его, когда значение равно 1. Итак, мой идеальный выход:
fact value
1: a,b 0,1
2: c,d 0,1
3: f,g,h 0,0,1
4: v 0
Во-первых, я подумал, что могу найти способ, используя cut
как:
cut(as.numeric(strsplit(as.character(df$value), split = ",")), breaks =1)
Но ни одна из моих попыток не приближается.
Ответы
Ответ 1
Один из способов состоит в том, чтобы разделить векторы символов для fact
и value
в исходном фрейме данных на ","
с помощью strsplit
, а затем определить положение первого "1"
в расколе value
s, Затем используйте эту позицию для определения разделения для fact
и value
:
sv <- strsplit(df$value,",")
sf <- strsplit(df$fact,",")
pos <- sapply(sv, function(sv) {j <- which(sv=="1"); if (length(j)==0) NA else j[1]})
out <- do.call(rbind,lapply(1:length(pos),function(i,sv,sf,pos) {
if (is.na(pos[i]) || pos[i] == length(sf[[i]]))
data.frame(fact=toString(sf[[i]]),value=toString(sv[[i]]))
else
data.frame(fact=c(toString(sf[[i]][1:pos[i]]),
toString(sf[[i]][(pos[i]+1):length(sf[[i]])])),
value=c(toString(sv[[i]][1:pos[i]]),
toString(sv[[i]][(pos[i]+1):length(sv[[i]])])))
},sv,sf,pos))
## fact value
##1 a, b 0, 1
##2 c, d 0, 1
##3 f, g, h 0, 0, 1
##4 v 0
В этом ответе предполагается, что существует "1"
в value
для разделения. Если этого не происходит или если "1"
находится в конце value
, тогда эта строка в df
не будет разделена на выходе.
Ответ 2
Сначала мы разбиваем строки в fact
и value
на отдельные значения и складываем их так, чтобы каждый из них становился столбцом значений в кадре данных. Теперь, используя value
, мы хотим, чтобы каждый пробег нулей сопровождался 1, чтобы стать группой. Это группы значений, которые мы хотим объединить в конце. Мы будем использовать dplyr
для работы отдельно в каждой группе, чтобы вернуть окончательный кадр данных.
library(dplyr)
library(purrr) # For map function
library(tidyr) # For separate_rows function
df %>%
separate_rows(fact, value, sep=",") %>%
mutate(group = lag(cumsum(value == 1), default=0)) %>%
group_by(group) %>%
summarise(fact = paste(fact, collapse=","),
value = paste(value, collapse=",")) %>%
select(-group)
fact value
1 a,b 0,1
2 c,d 0,1
3 f,g,h 0,0,1
4 v 0
Ответ 3
Другая попытка базы R:
sf <- strsplit(as.character(df$fact), ",")
sv <- strsplit(as.character(df$value), ",")
spl <- lapply(sv, function(x) -rev(cumsum(as.numeric(rev(x)))) )
#[[1]]
#[1] -2 -2 -1 -1
#
#[[2]]
#[1] -1 -1 -1 0
joinfun <- function(x) sapply(unlist(Map(split, x, spl), rec=FALSE), paste, collapse=",")
# to show you what is happening:
#> Map(split, sf, spl)
#[[1]]
#[[1]]$`-2`
#[1] "a" "b"
#
#[[1]]$`-1`
#[1] "c" "d"
#
#
#[[2]]
#[[2]]$`-1`
#[1] "f" "g" "h"
#
#[[2]]$`0`
#[1] "v"
data.frame(fact = joinfun(sf), value = joinfun(sv) )
# fact value
#1 a,b 0,1
#2 c,d 0,1
#3 f,g,h 0,0,1
#4 v 0
Ответ 4
Один метод data.table будет следующим. Вы разбиваете каждый элемент в fact
и value
с помощью cSplit()
в пакете splitstackshape
. Это создает таблицу данных в длинном формате. После того как вы получите результат, вы создаете групповую переменную с помощью diff()
и cumsum()
. Где разница в value
меньше 0, R создает новую группу. Затем вы хотите применить paste()
как к fact
, так и к value
. Вы можете достичь этого, используя lapply(.SD ...)
. Это эквивалентность summarise_at()
в пакете dplyr
. В конце вы удалите групповую переменную.
library(splitstackshape)
library(data.table)
cSplit(df, splitCols = c("fact", "value"),
direction = "long", sep = ",") -> temp
temp[, group := cumsum(c(FALSE, diff(value) < 0))][,
lapply(.SD, function(x){paste(x, collapse = ",")}),
.SDcols = fact:value,
by = group][, group :=NULL] -> out
# fact value
#1: a,b 0,1
#2: c,d 0,1
#3: f,g,h 0,0,1
#4: v 0
Ответ 5
Немного поздно для вечеринки, но вот решение, которое использует функции regular expressions
и tidyverse
:
#install.packages("devtools")
#devtools::install_github("hadley/tidyverse")
library(tidyverse)
dff <- data.frame(fact= c("a,b,c,d","f,g,h,v"),
value = c("0,1,0,1" , "0,0,1,0"),
stringsAsFactors = F)
dff %>%
mutate(value = gsub("(?<=1),(?=0)","-", value, perl = T)) %>%
group_by(value) %>%
mutate(indices = which(strsplit(value,split="")[[1]]=="-"),
fact = sprintf("%s-%s",
substr(fact, 0, indices - 1),
substr(fact, indices + 1, nchar(fact)))) %>%
select(fact, value) %>%
ungroup() %>%
separate_rows(fact, value, sep = "-")
Это находит запятые, расположенные сразу после 1
в столбце value
, а затем заменяет эти запятые тире (-
). Затем он получает индексы этих тире в каждой строке столбца value
и отправляет их в столбец fact
, чтобы заменить соответствующие запятые там тире. Впоследствии он использует separate_rows
для разделения столбцов fact
и value
на эти тире.
Он должен давать следующее:
# fact value
# <chr> <chr>
# 1 a,b 0,1
# 2 c,d 0,1
# 3 f,g,h 0,0,1
# 4 v 0
Ответ 6
Замените решение на более простой.
Пакеты не используются. Столбцы df
могут быть символом или фактором - код преобразует их в символ. value
записи на входе могут не содержать ни одного. Компоненты fact
и value
в одной строке ввода должны иметь одинаковое количество разделенных запятыми полей, но могут иметь разные числа полей в разных строках.
do.call("rbind", by(df, 1:nrow(df), function(x) {
long <- lapply(x, function(x) unlist(strsplit(as.character(x), ",")))
g <- -rev(cumsum(rev(long$value == 1)))
aggregate(long, list(g), paste, collapse = ",")[names(x)]
}))
даяние:
fact value
1 a,b 0,1
2 c,d 0,1
5 f,g,h 0,0,1
6 v 0
by
вызывает анонимную функцию, показанную один раз для каждой строки. Для каждой строки она разбивает каждый столбец запятой, давая длинную форму long
для этой строки. Например, для итерации, обрабатывающей первую строку df
, значение long
:
long <- list(fact = c("a", "b", "c", "d"), value = c("0", "1", "0", "1"))
Затем мы вычисляем переменную группировки g
для строки. Например, для первой итерации она равна:
g <- c(-2L, -2L, -1L, -1L)
Наконец, мы суммируем по g
, вставляя элементы из каждого столбца, имеющие одну и ту же группу вместе. Отбрасываем дополнительные столбцы, добавленные aggegate
.
В конце мы rbind
data.frames для всех строк вместе.