2016-01-23 42 views
3

我在R中工作,其中包含一個由數字變量和字符變量組成的數據框。我的數據框DF看起來像這樣(我加dput版本中最後部分):R中的函數cut和ifelse之間的奇怪行爲

a1 b1 
1 a 10.15 
2 a 25.10 
3 a 32.40 
4 a 56.70 
5 a 89.02 
6 b 90.50 
7 b 78.53 
8 b 98.12 
9 b 34.30 
10 b 99.75 

DF變量a1是一組變量,b1是數值變量。然後出現困境。我想通過使用cut函數並考慮保存在a1中的組創建一個名爲c1的新變量。爲此,我在下一行代碼結合兩種功能ifelse()cut()

DF$c1=ifelse(DF$a1=="a", 
       cut(DF$b1,breaks = c(0,25,50,70,max(DF$b1)),right = TRUE,include.lowest = TRUE), 
       ifelse(DF$a1=="b", 
         cut(DF$b1,breaks = c(0,50,max(DF$b1)),right = TRUE,include.lowest = TRUE),NA)) 

的代碼行工作得很好,但在c1創建新的,他值的混亂結果。 cut()不是顯示一個因子,而是返回整數。然後,我得到了這樣的結果:

table(DF$c1,exclude=NULL) 

    1 2 3 4 <NA> 
    2 6 1 1 0 

儘管創造了休息,整數在c1變化的結果進行分配。這在我沒有問題的情況下工作時不會發生,但在這種情況下,我不同意組內的條件。例如下一行代碼返回這樣的結果:

DF$c1=cut(DF$b1,breaks = c(0,25,50,70,max(DF$b1)),right = TRUE,include.lowest = TRUE) 

table(DF$c1,exclude=NULL) 

    [0,25] (25,50] (50,70] (70,99.8]  <NA> 
     1   3   1   5   0 

我想知道如何解決ifelse()cut()功能之間的這種行爲,因爲返回的整數產生最終結果的差異。在這個例子中,我只爲a1變量工作,但我有一個包含很多組的大型數據庫。這就是我將這些功能組合起來以便爲每個組獲得不同裁減的原因。此外,休息時間的值可能會發生變化,因此以手動方式包括標籤可能會很長。這兩個函數的組合是否有可能爲每個組(因子)返回正確的標籤而不是整數。該dput()版本我的數據框DF的是下一個:

DF<-structure(list(a1 = c("a", "a", "a", "a", "a", "b", "b", "b", 
"b", "b"), b1 = c(10.15, 25.1, 32.4, 56.7, 89.02, 90.5, 78.53, 
98.12, 34.3, 99.75)), .Names = c("a1", "b1"), row.names = c(NA, 
-10L), class = "data.frame") 

感謝您的幫助!

回答

3

問題是cut()輸出一個因子,但由於它們有不同的級別,它們被強制爲整數。一個解決方案可能是圍繞你的cut()as.character(),從而保持了脅迫水平,然後factor()整個輸出:

DF$c1=factor(ifelse(DF$a1=="a", 
      as.character(cut(DF$b1,breaks = c(0,25,50,70,max(DF$b1)),right = TRUE,include.lowest = TRUE)), 
      ifelse(DF$a1=="b", 
        as.character(cut(DF$b1,breaks = c(0,50,max(DF$b1)),right = TRUE,include.lowest = TRUE)),NA))) 

DF 

    a1 b1  c1 
1 a 10.15 [0,25] 
2 a 25.10 (25,50] 
3 a 32.40 (25,50] 
4 a 56.70 (50,70] 
5 a 89.02 (70,99.8] 
6 b 90.50 (50,99.8] 
7 b 78.53 (50,99.8] 
8 b 98.12 (50,99.8] 
9 b 34.30 [0,50] 
10 b 99.75 (50,99.8] 
+0

是的,但是如何排列因子? –

+0

你是什麼意思?對數據框中的行進行排序?或水平? – scoa

+0

當您轉換爲字符時,因素中斷不能正確排列,因此,當您執行c1的操作時,係數的排序順序不正確。或者當您使用該因子作爲x變量時,順序都是錯誤的。 –

2

@scoa是正確的;你試圖將不同層次的兩個因素結合起來,所以你的結果被強制轉化爲整數,並且你失去了關卡。這是另一種更小尺寸的方法,它將更具可擴展性。

首先,讓所有的休息命名列表:

breaks <- list('a' = c(0, 25, 50, 70, max(DF$b1)), 'b' = c(0, 50, max(DF$b1))) 
breaks 

> $a 
>  0 25 50 70 99.75 
> $b 
>  0 50 99.75 

然後使用unlist(list(some, factors))(或在這種情況下,lapply),整齊地合併因素,保持各個層面。 (這很神奇,它是其中一種內置功能,其實並不明顯。)

DF$c1 <- unlist(lapply(1:length(breaks), 
        function(x){cut(DF[DF$a1 == names(breaks[x]), 'b1'], 
            breaks = breaks[[x]], 
            right = TRUE, 
            include.lowest = TRUE)} 
        )) 
DF 

> a1 b1  c1 
> 1 a 10.15 [0,25] 
> 2 a 25.10 (25,50] 
> 3 a 32.40 (25,50] 
> 4 a 56.70 (50,70] 
> 5 a 89.02 (70,99.8] 
> 6 b 90.50 (50,99.8] 
> 7 b 78.53 (50,99.8] 
> 8 b 98.12 (50,99.8] 
> 9 b 34.30 [0,50] 
> 10 b 99.75 (50,99.8] 

它最終是2行代碼,對於更大,更復雜的數據集應該是健壯的。

2

這不是對您的問題的直接回答,而是對整體任務的替代方法。

因爲你有「一個大型的數據庫,每組」有很多組[具有不同的削減],所以我認爲很快嵌套ifelse的代碼可能會變得非常混亂。也許是品味的問題,但我認爲如果您將每個組的breaks存儲在單獨的表中,代碼將更易於閱讀和維護。

這裏是你會如何使用data.table做到這一點:

library(data.table) 
dt_brk <- data.table(grp = c("a", "a", "a", "a", "a", "b", "b", "b"), 
        brk = c(0, 25, 50, 70, Inf, 0, 50, Inf)) 

請注意,我用Inf作爲休息的上限,而不是max(your-values)

我們將您的數據幀「DF」轉換成data.table使用setDT。然後,對於「a1」(by = a1)的每個級別,我們使用來自「dt_brk」的breaks,其中「grp」等於「a1」(dt_brk[grp == a1, brk])的cut「b1」。

setDT(DF)[, c1 := as.character(cut(b1, breaks = dt_brk[grp == a1, brk])), by = a1] 

DF 
#  a1 b1  c1 
# 1: a 10.15 (0,25] 
# 2: a 25.10 (25,50] 
# 3: a 32.40 (25,50] 
# 4: a 56.70 (50,70] 
# 5: a 89.02 (70,Inf] 
# 6: b 90.50 (50,Inf] 
# 7: b 78.53 (50,Inf] 
# 8: b 98.12 (50,Inf] 
# 9: b 34.30 (0,50] 
# 10: b 99.75 (50,Inf]