Разделите строки и добавьте их как новую строку

У меня есть следующий набор данных:

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 для всех строк вместе.