2014-01-14 35 views
2

我想用數千列分割數據幀。數據幀看起來像這樣:在多行上按'/'一次拆分數千列,對新行中的值進行排序並添加'NA'值

# sample data of four columns 
sample <-read.table(stdin(),header=TRUE,sep="") 
POS v1 v2 v3 v4 
152 0 0/1 0/2 0/1/2 
73 1 0 0/1 0/1 
185 0 1 0/3 0 

輸出應爲:

POS v1 v2 v3 v4 
152 0 0 0 0 
152 NA 1 NA 1 
152 NA NA 2 2 
73 NA 0 0 0 
73 1 NA 1 1 
185 0 NA 0 0 
185 NA 1 NA NA 
185 NA NA NA NA 
185 NA NA 3 NA 

的值0,1,2和3應該如此獲得的新行進行排序,而值在現場POS應該重複。然後插入NA值以獲得相同長度的柱子。

+0

您是否主要需要*工作*或主要工作*快速*?你到目前爲止嘗試過什麼嗎? –

+0

我假設0/1/2總是按順序。 –

+0

另外,爲什麼你需要這個 - 例如,轉換成位域不是一樣有效? (也要快得多) –

回答

3

下面是使用data.table溶液:

library("data.table") 
dt <- data.table(df) 
fun <- function(DT) { 
    split <- strsplit(vapply(DT, as.character, character(1L)), "/") 
    lapply(split, 
    function(x, max.len) as.numeric(x)[match(0:max.len, as.numeric(x))], 
    max.len=max(as.numeric(unlist(split))) 
) } 
dt[, fun(.SD), by=POS] 
# POS v1 v2 v3 v4 
# 1: 152 0 0 0 0 
# 2: 152 NA 1 NA 1 
# 3: 152 NA NA 2 2 
# 4: 73 NA 0 0 0 
# 5: 73 1 NA 1 1 
# 6: 185 0 NA 0 0 
# 7: 185 NA 1 NA NA 
# 8: 185 NA NA NA NA 
# 9: 185 NA NA 3 NA 

的想法是使用data.table執行我們的函數fun針對每一行的數據元素(即,不包括POS)。 data.table將針對我們的修改結果拼接回POS

通過/每個數據行分割轉換​​爲字符矢量,然後在此fun開始時,因爲有/這將產生一個列表與每個項目,字符向量與儘可能多的元素,+ 1

最後,lapply循環遍歷這些列表項中的每一個,將它們全部轉換爲相同長度的向量,填入NA並進行排序。

data.table將結果列表識別爲表示我們結果集的列,並且如前所述添加了POS列。


編輯:下面的地址在評論一個問題:

val <- "0/2/3:25:0.008,0.85,0.002:0.004,0.013,0.345" 
first.colon <- strsplit(val, ":")[[1]][[1]] 
strsplit(first.colon, "/")[[1]] 
// [1] "0" "2" "3" 

最關鍵的事情要明白的是strsplit返回與因爲有您的輸入向量的項目,如多元素的列表。在這個玩具例子中,向量中只有一個項目,所以列表中只有一個項目,儘管每個項目都是一個可以具有多個值的字符向量(在這種情況下,我們通過/分割後的值爲3)。因此,像這樣,就可以(但我沒有測試調試):

dt <- data.table(df) 
fun <- function(DT) { 
    split <- strsplit(vapply(DT, as.character, character(1L)), ":") 
    split.2 <- vapply(split, `[[`, character(1L), 1) # get just first value from `:` split 
    split.2 <- strsplit(split.2, "/") 
    lapply(split.2, 
    function(x, max.len) as.numeric(x)[match(0:max.len, as.numeric(x))], 
    max.len=max(as.numeric(unlist(split))) 
) } 
+0

如果在v1:v4中沒有數字,而是一個字符串as.character'0/2/3:25:0.008,0.85,0.002:0.004,0.013,0.345',我首先要用':'分割它。 ,然後選擇第一個元素並用'/'分割。如何在分割後選擇列表元素? – user3184877

+0

@ user3184877,請參閱編輯。 – BrodieG

3
tmp <- apply(sample[-1], 1, function(x) { 
    s <- strsplit(x, "\\/") 
    num <- lapply(s, as.integer) 
    ma <- max(unlist(num)) 
    vec <- rep(NA_integer_, ma + 1) 
    sapply(num, function(y) replace(vec, y + 1, y)) 
}) 

res <- data.frame(POS = rep(sample[[1]], sapply(tmp, nrow)), 
        do.call(rbind, tmp)) 

# POS v1 v2 v3 v4 
# 1 152 0 0 0 0 
# 2 152 NA 1 NA 1 
# 3 152 NA NA 2 2 
# 4 73 NA 0 0 0 
# 5 73 1 NA 1 1 
# 6 185 0 NA 0 0 
# 7 185 NA 1 NA NA 
# 8 185 NA NA NA NA 
# 9 185 NA NA 3 NA 
1

這裏是指示存在或不存在一種替代的解決方案,而不是生成多個行,生成用於每個值的位掩碼「0」「1」「2」等位。

> sample <-read.table(stdin(),header=TRUE,sep="", 
          row.names=1,colClasses="character") 
0: POS v1 v2 v3 v4 
1: 152 0 0/1 0/2 0/1/2 
2: 73 1 0 0/1 0/1 
3: 185 0 1 0/3 0 
4: 
> # transform the strings into bit masks 
> B<-function(X)lapply(strsplit(X,"/"), 
       function(n)Reduce(bitOr,bitwShiftL(1,as.numeric(n)),0)) 
> B("0/1") 
[[1]] 
[1] 3 
> # apply it everywhere 
> s<-colwise(B)(sample) 
> rownames(s)<-rownames(sample) 
> s 
    v1 v2 v3 v4 
152 1 3 5 7 
73 2 1 3 3 
185 1 2 9 1 

雖然這不是你問什麼,假設一套枚舉值小(0,1,2),它是非常多的存儲空間更高效,可以很容易地進行處理:

哪元素都V1 「0」 和v3 「0」 和 「1」

> subset(s, bitAnd(v1,B("0")) & bitAnd(v4,B("0/1"))) 
    v1 v2 v3 v4 
152 1 3 5 7 
185 1 2 9 1 
+0

我希望找到一種方法來將2維數據框(作爲矩陣)投影到T/F值的三維形式中,但是我無法輕易弄清楚。這將是他所要求的稀疏表示的密集等價物。 –

2

我猜有趣的數據是一個真正的矩陣

m = as.matrix(sample[,-1]) 

底層數據是一個具有相對較少的唯一值的向量;我們唯一值映射到它們的整數表示,使用地圖,可以最小化是必要

s = as.character(m) 
map = lapply(strsplit(setNames(unique(s), unique(s)), "/"), as.integer) 

這裏的每一行需要複製的次數任意循環迭代次數

row.len = apply(matrix(sapply(map, max)[s], ncol=ncol(m)), 1, max) + 1 

和偏移到每個行

offset = head(c(1, cumsum(rep(row.len, ncol(m))) + 1), -1) 

計算每個映射元素的值的s,並且在012的值的索引

v = unlist(unname(map)[match(s, names(map))]) 
idx = rep(offset, sapply(map, length)[s]) + v 

最後,分配NA的結果矩陣,並更新非NA值

ans = matrix(NA_integer_, sum(row.len), ncol(m)) 
ans[idx] = v 

作爲功能:

flatten <- function(sample) { 
    m = as.matrix(sample[,-1]) 
    s = as.character(m) 
    map = lapply(strsplit(setNames(unique(s), unique(s)), "/"), as.integer) 
    row.len = apply(matrix(sapply(map, max)[s], ncol=ncol(m)), 1, max) + 1 
    offset = head(c(1, cumsum(rep(row.len, ncol(m))) + 1), -1) 
    v = unlist(unname(map)[match(s, names(map))]) 
    idx = rep(offset, sapply(map, length)[s]) + v 
    ans = matrix(NA_integer_, sum(row.len), ncol(m), 
     dimnames=list(NULL, colnames(sample)[-1])) 
    ans[idx] = v 
    cbind(POS=rep(sample[,1], row.len), as.data.frame(ans)) 
} 

這樣做的最慢的部分將是apply函數計算row.len。一些時間(我猜這個尺寸對於這個問題是不正確的......)

xx = do.call(rbind, replicate(10000, sample, simplify=FALSE)) 
dim(xx) 
## [1] 30000  5 
system.time(flatten(xx)) 
## user system elapsed 
## 0.192 0.000 0.194 

對比上面的data.table解決方案大約5s。

相關問題