簡體   English   中英

在R之間沒有for循環的行之間移動值

[英]Moving values between rows without a for loop in R

我編寫了一些用於組織以不同頻率采樣的數據的代碼,但是我廣泛使用了for循環,當數據集很大時,這會大大降低代碼的操作速度。 我一直在瀏覽我的代碼,找到刪除for循環以加快速度的方法,但其中一個循環讓我感到難過。

舉個例子,假設數據是以3Hz采樣的,所以每秒數據都會得到三行。 但是,變量A,B和C各自以1Hz采樣,因此每三行我將獲得一個值。 在一秒周期內連續采樣變量,導致數據的對角性質。

更復雜的是,有時在原始數據集中會丟失一行。

我的目標是:確定了我希望保留的行后,我想將后續行中的非NA值移動到守護者行中。 如果它不是丟失的數據問題,我將始終保持行包含第一個變量的值,但如果其中一行丟失,我將保留下一行。

在下面的示例中,第六個樣本和第十個樣本丟失。

A <- c(1, NA, NA, 4, NA, 7, NA, NA, NA, NA)
B <- c(NA, 2, NA, NA, 5, NA, 8, NA, 11, NA)
C <- c(NA, NA, 3, NA, NA, NA, NA, 9, NA, 12)

test_df <- data.frame(A = A, B = B, C = C)

test_df
     A  B  C
 1   1 NA NA
 2  NA  2 NA
 3  NA NA  3
 4   4 NA NA
 5  NA  5 NA
 6   7 NA NA
 7  NA  8 NA
 8  NA NA  9
 9  NA 11 NA
10  NA NA 12

keep_rows <- c(1, 4, 6, 9)

將值移動到守護者行后,我將刪除臨時行,從而產生以下結果:

test_df <- test_df[keep_rows, ]
test_df
     A  B  C
 1   1  2  3
 2   4  5 NA
 3   7  8  9
 4  NA 11 12

最后,我只希望每秒數據有一行,而NA值只應保留原始數據行丟失的位置。

有沒有人有任何關於如何在不使用for循環的情況下移動數據的想法? 我很感激任何幫助! 對不起,如果這個問題太羅嗦了; 我想在太多信息方面犯錯,而不是不夠。

這應該這樣做:

test_df = with(test_df, cbind(A[1:(length(A)-2)], B[2:(length(B)-1)], C[3:length(C)]))
test_df = data.frame(test_df[!apply(test_df, 1, function(x) all(is.na(x))), ])
colnames(test_df) = c('A', 'B', 'C')
> test_df
   A  B  C
1  1  2  3
2  4  5 NA
3  7  8  9
4 NA 11 12

如果你想要更快的東西:

test_df = data.frame(test_df[rowSums(is.na(test_df)) != ncol(test_df), ])

在@John Colby的偉大答案的基礎上,我們可以擺脫應用步驟並加速相當多(大約20倍):

# Create a bigger test set 
A <- c(1, NA, NA, 4, NA, 7, NA, NA, NA, NA)
B <- c(NA, 2, NA, NA, 5, NA, 8, NA, 11, NA)
C <- c(NA, NA, 3, NA, NA, NA, NA, 9, NA, 12)
n=1e6; test_df = data.frame(A=rep(A, len=n), B=rep(B, len=n), C=rep(C, len=n))

# John Colby's method, 9.66 secs
system.time({
  df1 = with(test_df, cbind(A[1:(length(A)-2)], B[2:(length(B)-1)], C[3:length(C)]))
  df1 = data.frame(df1[!apply(df1, 1, function(x) all(is.na(x))), ])
  colnames(df1) = c('A', 'B', 'C')
})

# My method, 0.48 secs
system.time({
  df2 = with(test_df, data.frame(A=A[1:(length(A)-2)], B=B[2:(length(B)-1)], C=C[3:length(C)]))
  df2 = df2[is.finite(with(df2, A|B|C)),]
  row.names(df2) <- NULL
})

identical(df1, df2) # TRUE

...這里的技巧是, A|B|C是唯一的NA ,如果所有的值都是NA 這比使用apply在矩陣的每一行上調用all(is.na(x))要快得多。

編輯 @John有一個不同的方法,也加快了它。 我添加了一些代碼將結果轉換為具有正確名稱的data.frame並定時。 它似乎與我的解決方案速度幾乎相同。

# John's method, 0.50 secs
system.time({
  test_m = with(test_df, cbind(A[1:(length(A)-2)], B[2:(length(B)-1)], C[3:length(C)]))
  test_m[is.na(test_m)] <- -1
  test_m <- test_m[rowSums(test_m) > -3,]
  test_m[test_m == -1] <- NA
  df3 <- data.frame(test_m)
  colnames(df3) = c('A', 'B', 'C')
})

identical(df1, df3) # TRUE

再次編輯 ...... @John Colby的更新答案更快!

# John Colby's method, 0.39 secs
system.time({
  df4 = with(test_df, cbind(A[1:(length(A)-2)], B[2:(length(B)-1)], C[3:length(C)]))
  df4 = data.frame(df4[rowSums(is.na(df4)) != ncol(df4), ])
  colnames(df4) = c('A', 'B', 'C')
})

identical(df1, df4) # TRUE

所以你的問題只是在沒有循環的情況下向上移動。 顯然你已經解決了第一步。

> test_m <- with( test_df, cbind(A[1:(length(A)-2)], B[2:(length(B)-1)], C[3:length(C)]) )
> test_m
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]   NA   NA   NA
[3,]   NA   NA   NA
[4,]    4    5   NA
[5,]   NA   NA   NA
[6,]    7    8    9
[7,]   NA   NA   NA
[8,]   NA   11   12

現在是一個矩陣。 在沒有循環的情況下,您可以輕松地消除現在沒有數據點的行。 如果您希望它返回到data.frame,那么您可以使用不同的方法,但是這個方法對於大量數據運行速度最快。 我想讓NA成為一個不可能的價值......也許-1但你知道你的數據最好......也許-pi。

test_m[is.na(test_m)] <- -1

現在只需為那些不可能數字的屬性選擇行

test_m <- test_m[rowSums(test_m) > -3,]

並且,如果你想,你可以把NA放回去。

test_m[test_m == -1] <- NA
test_m
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5   NA
[3,]    7    8    9
[4,]   NA   11   12

沒有循環( forapply ),並且跨矩陣行應用的一個函數經過特殊優化並且運行速度非常快(rowSums)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM