2014-02-27 109 views
2

下面的代碼可以做得更像「R like」嗎?該代碼如何壓縮?

鑑於data.frame INDF:

V1   V2  V3  V4 
1 a   ha  1;2;3  A 
2 c   hb  4   B 
3 d   hc  5;6  C 
4 f   hd  7   D 

裏面DF我想

  1. 找到所有行這爲 「V3」 列有由分隔的多個值 「;」
  2. 然後複製各行的次數相等的單獨的值中的「V3」列中的數字,
  3. 然後每個複製的行中的「V3」列僅接收一個初始值

不久,輸出data.frame(= outDF)看起來像:

V1   V2  V3  V4 
1 a   ha  1   A 
1 a   ha  2   A 
1 a   ha  3   A 
2 c   hb  4   B 
3 d   hc  5   C 
3 d   hc  6   C 
4 f   hd  7   D 

所以,如果從INDF我想要去outDF,我會寫下面的代碼:

#load inDF from csv file 
inDF <- read.csv(file='example.csv', header=FALSE, sep=",", fill=TRUE) 

#search in inDF, on the V3 column, all the cells with multiple values 
rowlist <- grep(";", inDF[,3]) 

# create empty data.frame and add headers from "headDF" 
xDF <- data.frame(matrix(0, nrow=0, ncol=4)) 
colnames(xDF)=colnames(inDF) 

#take every row from the inDF data.frame which has multiple values in col3 and break it in several rows with only one value 

for(i in rowlist[]) 
{ 
    #count the number of individual values in one cell 
    value_nr <- str_count(inDF[i,3], ";"); value_nr <- value_nr+1 

    # replicate each row a number of times equal with its value number, and transform it to character 
    extracted_inDF <- inDF[rep(i, times=value_nr[]),] 
    extracted_inDF <- data.frame(lapply(extracted_inDF, as.character), stringsAsFactors=FALSE) 

    # split the values in V3 cell in individual values, place them in a list 
    value_ls <- str_split(inDF[i, 3], ";") 

    #initialize f, to use it later to increment both row number and element in the list of values 
    f = 1 

    # replace the multiple values with individual values 
    for(j in extracted_inDF[,3]) 

    { 
    extracted_inDF[f,3] <- value_ls[[1]][as.integer(f)] 
    f <- f+1 
    } 

    #put all the "demultiplied" rows in xDF 
    xDF <- merge(extracted_inDF[], xDF[], all=TRUE) 
} 

# delete the rows with multiple values from the inDF 
inDF <- inDF[-rowlist[],] 

#create outDF 
outDF <- merge(inDF, xDF, all=TRUE) 

您能否請

+1

從正確編碼規則:永遠不要重新發明輪子。浪費時間,你很可能犯錯誤,或至少拿出一個非最佳解決方案。 –

+1

我同意不重新發明輪子。我不確定我是否確切知道如何使用車輪。對於上面的例子,你有任何關於正確使用R的建議嗎? – CLM

+0

看看str_split和strsplit。一般來說,基R有一些有用的字符串函數,'stringr'包有更多。 –

回答

3

我不知道,我是一個談論是否要使用的R「正確」或「錯誤」方式...我主要只是用它來回答Stack Overflow的問題。 :-)

但是,有很多方法可以改善您的代碼。對於初學者來說,是的,你應該嘗試熟悉預定義的功能。它們通常會更有效率,並且會使您的代碼對同一種語言的其他用戶更加透明。儘管你簡要地描述了你想要達到的目標,並且我幾乎馬上知道答案,但是我發現你的代碼讓人望而生畏。我會把你的問題分成兩個主要部分:(1)分解數據和(2)將它與原始數據集重新組合。

對於部分1:你明明知道一些你需要的功能 - 或者至少主要的一個,你需要:strsplit。如果您使用strsplit,則會看到它返回list,但您需要一個簡單的vector。你怎麼到那的?尋找unlist。你問題的第一部分現在已經解決了。

對於部分2:你首先需要確定你需要多少次,以複製原始數據集的每一行。爲此,您可以鑽取list(例如,l/s/v-apply)並計算每個項目的length。我選擇了sapply,因爲我知道它會創建一個我可以用於rep的矢量。

然後,如果您已經使用data.frame就足夠了,特別是在提取數據時,您會意識到mydf[c(1, 1, 1, 2), ]將導致data.frame,其中第一行重複兩次。知道這一點,我們可以使用我們剛剛做出的length計算來「擴大」我們原來的data.frame

最後,擴展data.frame後,我們只需要將相關列替換爲未列出的值。


這裏是上述行動。我命名你的數據集「是myDF」:

V3 <- strsplit(mydf$V3, ";", fixed=TRUE) 
sapply(V3, length) ## How many times to repeat each row? 
# [1] 3 1 2 1 
## ^^ Use that along with `[` to "expand" your data.frame 
mydf2 <- mydf[rep(seq_along(V3), sapply(V3, length)), ] 
mydf2$V3 <- unlist(V3) 
mydf2 
#  V1 V2 V3 V4 
# 1 a ha 1 A 
# 1.1 a ha 2 A 
# 1.2 a ha 3 A 
# 2 c hb 4 B 
# 3 d hc 5 C 
# 3.1 d hc 6 C 
# 4 f hd 7 D 

分享一些更多的選擇...

的「data.table」包居然能這樣的事情非常有用的。

library(data.table) 
DT <- data.table(mydf) 
DT2 <- DT[, list(new = unlist(strsplit(as.character(V3), ";", fixed = TRUE))), by = V1] 
merge(DT, DT2, by = "V1") 

另外,concat.split.multiple從我的「splitstackshape」包幾乎做它一步到位,但如果你想你的精確的輸出,你需要刪除NA值,並重新安排行。

library(splitstackshape) 
df2 <- concat.split.multiple(mydf, split.cols="V3", seps=";", direction="long") 
df2 <- df2[complete.cases(df2), ] ## Optional, perhaps 
df2[order(df2$V1), ]    ## Optional, perhaps 
+1

我已經通過你的代碼,我開始明白什麼是R(在我短暫的RI經歷中一直在寫「for」循環,不知何故它不像R;感覺我可以在任何情況下完成其他編程語言,那是當我意識到我做錯了什麼,因爲R應該有許多現成功能)。對於與基準測試的聯繫 - 我想知道如何測量R中的CPU使用情況。任何有關測量RAM使用情況的軟件包的建議? – CLM

2

在這種情況下,您可以使用split-apply-combine範例來重新整形數據。

由於要分別對每一行進行操作,所以您希望將行分割爲inDF。我已經使用了split功能這裏由排它分裂:

spl = split(inDF, 1:nrow(inDF)) 

spl是包含在inDF各行的1行數據幀的列表。

接下來,您需要應用一個函數將拆分數據轉換爲您需要的最終格式。在這裏,我將使用lapply函數變換的1行的數據幀,使用strsplit打破了可變V3成其相應的部分:

transformed = lapply(spl, function(x) { 
    data.frame(V1=x$V1, V2=x$V2, V3=strsplit(x$V3, ";")[[1]], V4=x$V4) 
}) 

tranformed現在是其中第一元件具有3列表行數據幀,第三個元素具有2行數據幀,第二個和第四個元素具有1行數據幀。

最後一步是將此列表合併到outDF中,使用do.callrbind函數。這與使用transformed列表的所有元素調用rbind具有相同的效果。

outDF = do.call(rbind, transformed) 

這就產生所需的最終數據幀:

outDF 
#  V1 V2 V3 V4 
# 1.1 a ha 1 A 
# 1.2 a ha 2 A 
# 1.3 a ha 3 A 
# 2 c hb 4 B 
# 3.1 d hc 5 C 
# 3.2 d hc 6 C 
# 4 f hd 7 D 
+0

有趣的方法(+1),但它似乎像這樣按行進行,尤其是對所有對'data.frame'的調用,在時間上會非常昂貴。 – A5C1D2H2I1M1N2O1R2T1

+0

當你說時間昂貴,你的意思是CPU時間? – CLM

+0

@AnandaMahto同意,這是一個很慢的交易(4毫秒比我的電腦0.2毫秒)。我認爲split/apply/combine是編程R時學到的最重要的範例,所以我認爲展示這種方法非常重要。 – josliber