2016-12-06 7 views
11

私は、次のデータセットを持っている:分割文字列と新しい行として追加

fact value 
1 a,b,c,d 0,1,0,1 
2 f,g,h,v 0,0,1,0 

値は1あるとき、私はそれを分割したい:

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  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) 

しかし、私の試みのどれもが近づくません。

答えて

5

一つの方法は、strsplitを用い","て元のデータフレームにfactvalueの文字ベクトルを分割した後、分割value Sにおける最初の"1"の位置を決定することです。その後factvalueの両方のための分割を決定するために、この位置を使用します。

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 

この回答は分割するvalue"1"があることを前提としています。 "1"valueの末尾にある場合、その行はdfに分割されません。

6

まず、factvalueの文字列を別々の値に分割し、それぞれがデータフレーム内の値の列になるようにスタックします。今度は、valueを使用して、それぞれの0の実行の後に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 
5

別のベース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 
5

一つdata.tableアプローチは以下になります。それぞれの要素をfactvalueに分割し、splitstackshapeパッケージのcSplit()を使用します。これにより、long形式のdata.tableが作成されます。結果が得られたら、diff()cumsum()を使用してグループ変数を作成します。valueの差が0より小さい場合、Rは新しいグループを作成します。次に、paste()factvalueの両方に適用します。 lapply(.SD ...)を使用してこれを達成できます。これは、dplyrパッケージのsummarise_at()と同等です。最後に、グループ変数を削除します。

相手に少し遅れ
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 
4

が、ここではregular expressionstidyverse機能を利用してソリューションです:

#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 = "-") 

これはvalue列に右1後に置かカンマを発見し、それらカンマを置き換えダッシュ(-)。次に、value列の各行にあるこれらのダッシュのインデックスを取得し、fact列に入力して対応するカンマをダッシュ​​で置き換えます。その後、separate_rowsを使用して、factvalueの両方の列をそれらのダッシュに分割します。これは、次が得られるはず :

#  fact value 
# <chr> <chr> 
# 1 a,b 0,1 
# 2 c,d 0,1 
# 3 f,g,h 0,0,1 
# 4  v  0 
+1

separate_rowsはきちんとしています - その1つを意識していなかった – zacdav

+0

、本当に便利です。 – Abdou

3

は、この単純な1の溶液を交換しました。

パッケージは使用されていません。 dfの列は、文字または要素である可能性があります。コードはそれらを文字に変換します。入力のvalueエントリには1つも含めることができません。入力の同じ行にあるfactvalueのコンポーネントのカンマ区切りフィールドの数は同じである必要がありますが、異なる行のフィールド数は異なることがあります。

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に追加された余分な列を削除します。

最後にすべての行のdata.framesをrbindにまとめます。

関連する問題