2016-09-18 20 views
7

考慮以下幾點:查找以上比在DF列中的值的值,並且較少的值的數量,而無需使用環

df <- data.frame(X = c(5000, 6000, 5500, 5000, 5300)) 

count_above <- function(vector) 
{ 
    counts <- vector() 
    counts[1] <- 0 
    for (i in 2:length(vector)) 
    { 
    temp <- vector[1:i] 
    counts <- c(counts, sum(temp < vector[i])) 
    } 
    return(counts) 
} 

這使我正確的輸出:

count_above(df$X) 
[1] 0 1 1 0 2 

對於例如,(列)向量這裏是

5000 
6000 
5500 
5000 
5300 

在最高層5000,沒有值ABO對了。所以這給出值0

6000,有一個值高於它並且小於60005000。所以這給出了價值1

5500上面有兩個值,其中一個小於5500,所以這給出值1等等。

有什麼辦法可以在不使用循環的情況下寫出來嗎?

+0

通常,您希望避免for循環並使用矢量化函數。但是在這種情況下,對於每一行,您需要比較i-1值,而且我不認爲這可以向量化很多。有時只是保持循環是可以的。用適用的家庭包裝它可能會傷害到可讀性。 – dracodoc

+1

儘管可以改進函數中的幾個點:1.創建具有固定大小的計數向量,而不是在每一步中增加計數向量。 2.不需要創建temp,只需使用vector [1:i]就可以了。 3.不要使用保留名稱作爲變量,如參數'vector'。 4.它應該是向量[1:(i-1)] – dracodoc

回答

12

另一種方法,神似aichao的解決方案(但有點短)

X <- c(5000, 6000, 5500, 5000, 5300) 
indices <- 1:length(X) 
count_above <- colSums(outer(X, X, "<") & outer(indices, indices, "<")) 
## [1] 0 1 1 0 2 

編輯(性能):也許我的想法被選定爲接受的答案,因爲它是短期和自我解釋的代碼 - 但要小心使用它在大型載體上!這是所有解決方案中最慢的方法!與Dracodoc所做的一樣,我也做了一個微基準。但我用3000個值的隨機生成的載體,以獲得更多顯著的運行時間:

count_above_loop <- function(v) 
{ 
    counts <- integer(length = length(v)) 
    counts[1] <- 0 
    for (i in 2:length(v)) 
    { 
    counts[i] <- sum(v[1:(i-1)] < v[i]) 
    } 
    return(counts) 
} 

count_above_outer <- function(X) { 
    indices <- 1:length(X) 
    colSums(outer(X, X, "<") & outer(indices, indices, "<")) 
} 

count_above_apply <- function(X) { 
    sapply(seq_len(length(X)), function(i) sum(X[i:1] < X[i])) 
} 

X <- runif(3000) 

microbenchmark::microbenchmark(count_above_loop(X), 
           count_above_apply(X), 
           count_above_outer(X), times = 10) 

Unit: milliseconds 
       expr  min  lq  mean median  uq  max neval cld 
    count_above_loop(X) 56.27923 58.17195 62.07571 60.08123 63.92010 77.31658 10 a 
count_above_apply(X) 54.41776 55.07511 57.12006 57.22372 58.61982 59.95037 10 a 
count_above_outer(X) 121.12352 125.56072 132.45728 130.08141 137.08873 154.28419 10 b 

我們看到,在一個大的載體,沒有一個數據幀的開銷中的應用方法比for循環稍微快一點。

我的外部產品方法花費的時間增加了一倍以上。

所以我會建議使用for循環 - 它也可讀和更快。我的方法可能會被考慮,如果你想有可證明的正確的代碼(因爲這一個班輪是非常接近的問題規範)

+0

請參閱下面的microbenchmark。 – dracodoc

+0

感謝您的更新。我也應該使用更大的隨機數據集。我使用的這個微小的示例大小使得我的基準信息量更少。 – dracodoc

6

考慮運行條件計數與sapply()。雖然這仍然是一個循環,它是一個矢量方法:

count_above <- sapply(seq_len(nrow(df)), 
         function(i) sum(df[i:1, c("X")] < df$X[i])) 
count_above 
# [1] 0 1 1 0 2 
+0

它應該是df [(i-1):1,c(「X」)] – dracodoc

+1

不一定就是它上面的行。查看OP的第三行5500返回1,因爲它看起來在5000和6000,它上面的兩個(所有上面的值)。 – Parfait

+0

我的意思是你應該比較上面的所有行與我,這意味着v [1:(i-1)]和v [i]。包括我沒有錯誤只是因爲比較v [i] dracodoc

3

另一種方法(還是因爲colSums環路):

xg <- expand.grid(df$X,df$X) 
o <- matrix(xg$Var1 < xg$Var2, nrow=length(x)) 
o[lower.tri(o)] <- FALSE 
count_above <- colSums(o) 
##[1] 0 1 1 0 2 

這將極有可能不會像凍糕的回答一樣高效,但它是一種選擇。

4

編輯:我應該使用更大的數據集的基準,小數據集使基準測試結果有點誤導。請參閱PatrickRoocks的更新。

我剛剛評論說,for循環不一定比應用家庭差,然後我看到了這一點。

我做了一個比較優化的for循環和sapply方法的microbenchmark。 for循環速度快6倍。 sapply方法不是一個適當的函數,將它修改爲一個採用向量的函數,而不是假設數據幀列可以稍微改進一點。

df <- data.frame(X = c(5000, 6000, 5500, 5000, 5300)) 

count_above <- function(v) 
{ 
    counts <- integer(length = length(v)) 
    counts[1] <- 0 
    for (i in 2:length(v)) 
    { 
    counts[i] <- sum(v[1:(i-1)] < v[i]) 
    } 
    return(counts) 
} 
count_above(df$X) 

microbenchmark::microbenchmark(count_above(df$X), sapply(seq_len(nrow(df)), function(i) sum(df[i:1, c("X")] < df$X[i])), times = 10) 

Unit: microseconds 
                    expr 
                count_above(df$X) 
sapply(seq_len(nrow(df)), function(i) sum(df[i:1, c("X")] < df$X[i])) 
    min  lq  mean median  uq  max neval cld 
    38.623 41.068 65.0722 55.0010 65.512 142.757 10 a 
262.045 269.379 368.6231 339.2905 415.067 640.934 10 b 

更新:

# modify Parfait's answer into a function, taking vector instead of data frame 
count_above_2 <- function(v){ 
    counts <- sapply(seq_len(length(v)), 
    function(i) sum(v[i:1] < v[i])) 
    return(counts) 
} 

X <- df$X 

microbenchmark::microbenchmark(count_above(X), count_above_2(X), {indices <- 1:length(X); colSums(outer(X, X, "<") & outer(indices, indices, "<"))}, times = 100) 

Unit: microseconds 
                         expr 
                       count_above(X) 
                      count_above_2(X) 
{  indices <- 1:length(X)  colSums(outer(X, X, "<") & outer(indices, indices, "<")) } 
    min  lq  mean median  uq  max neval cld 
21.023 23.4680 39.02878 26.1565 35.4450 144.224 100 a 
41.067 49.3785 67.06162 53.2900 70.1565 166.712 100 b 
37.646 40.0900 66.45059 53.0450 72.8455 258.623 100 b 

For循環仍然獲勝。 傳遞一個矢量而不是所有的節省時間,所以我給3個解決方案相同的向量是可比較的。 Parfait的答案與PatrickRoocks的答案相當。

除了表現,還有一個微妙的正確點。

因爲v [i] < v [i]是FALSE,所以OP的函數和Parfait的和(v [i:1] < v [i])給出正確的答案。根據定義,它應該使用v [1:(i-1)] < v [i]。

我的功能可以寫在一個更簡潔的版本是這樣的:

count_above <- function(v) 
{ 
    counts <- integer(length = length(v)) 
    for (i in 1:length(v)) 
    { 
    counts[i] <- sum(v[1:(i-1)] < v[i]) 
    } 
    return(counts) 
} 

它看起來更好,並給予正確的結果。這也取決於v [1] < v [1]是否爲FALSE。這不一定是錯誤的,因爲它只是第一行,儘管我仍然更喜歡更長,但更明顯的版本。