R でネストされた for ループに apply() を使用する

okwaves2024-01-25  7

この質問にはすでに答えがあります: R での複雑な非等価マージ

(答えは3つ)

3 年前

に閉店しました。

R でネストされた for ループを作成しましたが、ループの実行に時間がかかりすぎます。 2 つの大きなデータセットがあります。 dfA のすべての行と dfB のすべての行について、ループは dfA の日付が dfB の日付間隔内にあるかどうかを確認する必要があります。これならが true の場合、2 つのデータセットはその行の特定の列でマージされます。ループがまだ実行されているため、私が書いたコードがエラーなしで動作するかどうかはわかりません。

何か洞察があれば幸いです。

dfA:


       Common    a       Date 
1 20141331123    1        2005-01-01 
2 20141331123    2        2005-01-02 
3 20141331123    3        2005-01-03 
4 20141331123    4        2005-01-04 
5 20141331123    5        2005-01-05 
6 20141331123    6        2005-01-06 

dfB:


  cDate       bDate      common                      
1 2005-01-01 2005-06-13  20141331123 

dfB$Interval <- interval(ymd(dfB$cDate), ymd(dfB$bDate)) 

library(lubridate) 

for (i in 1:nrow(dfA)) {
  
  for (i in 1:nrow(dfB)) {
  
      if (dfA$Date[i] %within% dfB$Interval[i] == TRUE) {
        
        merged <- merge(dfA, dfB, by.x = c("common"), by.y = c("Common"))
        
      }
    
  }
  
  return(merged)

}

(1) コードはすべての結果を破棄しています。マージしたい場合は、その戻り値をどこかにキャプチャする必要があります。それがなければ、何の成果も示さないまま、すべての仕事をこなしていることになります。 (2) 範囲ベースの結合操作を検討している場合は、非等価結合を実行することを強くお勧めします。ループを試みるのをやめてください。 data.table と SQL はこれをネイティブに実行します (残念ながら、base-R と dplyr は実行しません)。

– r2evans

2020 年 9 月 4 日 20:54

...では、apply() はここでどこに登場するのでしょうか?

– r2evans

2020 年 9 月 4 日 20:55

dput を使用して、小さく代表的なサンプル データを提供していただければ非常に役立ちます。つまり、それぞれから少数の行があり、一部の行はマージされ、一部の行はマージされません。

– r2evans

2020 年 9 月 4 日 20:58

データを追加しました。 dfA の日付が dfB の日付間隔内にあるかどうかを確認したいため、lubridate を使用しました。 apply() が for ループを置き換えるのに役立つのではないかと思いました。私の理解は間違っている可能性があります。私はまだリーですR の使用方法について説明します。

– user12310746

2020 年 9 月 4 日 21:08



------------------------

非等価結合は、SQL と R 内の data.table でネイティブにサポートされています。基本 R 関数も Tidyverse 関数もローカルではサポートしていません [1]。

library(data.table)
setDT(dfA)
setDT(dfB)
dfB[dfA, on = .(common == Common, cDate <= Date, bDate >= Date)]
#         cDate      bDate      common a
# 1: 2005-01-01 2005-01-01 20141331123 1
# 2: 2005-01-02 2005-01-02 20141331123 2
# 3: 2005-01-03 2005-01-03 20141331123 3
# 4: 2005-01-04 2005-01-04 20141331123 4
# 5: 2005-01-05 2005-01-05 20141331123 5
# 6: 2005-01-06 2005-01-06 20141331123 6

サンプル データは、すべてが 1 つの間隔に収まるという点で少し面白くありませんが、おそらくこれは、より多様なデータで機能するでしょう。

[1]: SQL がサポートしているため、dbplyr では sql_on を使用してサポートされています。

データ:

dfA <- structure(list(Common = c("20141331123", "20141331123", "20141331123", "20141331123", "20141331123", "20141331123"), a = 1:6, Date = structure(c(12784, 12785, 12786, 12787, 12788, 12789), class = "Date")), row.names = c(NA, -6L), class = "data.frame")
dfB <- structure(list(cDate = structure(12784, class = "Date"), bDate = structure(12947, class = "Date"), common = "20141331123"), row.names = c(NA, -1L), class = "data.frame")

4

ありがとうございます。これは便利で非常に高速です。データは正しくマージされましたが、dfA の日付列も含めながらこのマージを行う方法はありますか?

– user12310746

2020 年 9 月 9 日 1:28

現時点では、Date 列の複製を作成して、最終的なデータテーブルに含めるようにしました。

– user12310746

2020 年 9 月 9 日 1:35

1

はい、非等価マージでは 3 つのフィールドのうち 1 つが失われる傾向があります (これは私も同じ不満です)。私もフィールドをコピーすることで対処しています。

– r2evans

2020 年 9 月 9 日 1:49

わかりました、わかりました。このマージの速さには本当に驚かされます。

– user12310746

2020 年 9 月 9 日 1:52



------------------------

データ サイズが許せば、単純なマージとサブセットを検討してください。

final_df <- subset(merge(dfA, dfB, by.x="Common", by.y="common"),
                   Date >= cDate & Date <= bDate)

final_df
#        Common a       Date      cDate      bDate
# 1 20141331123 1 2005-01-01 2005-01-01 2005-06-13
# 2 20141331123 2 2005-01-02 2005-01-01 2005-06-13
# 3 20141331123 3 2005-01-03 2005-01-01 2005-06-13
# 4 20141331123 4 2005-01-04 2005-01-01 2005-06-13
# 5 20141331123 5 2005-01-05 2005-01-01 2005-06-13
# 6 20141331123 6 2005-01-06 2005-01-01 2005-06-13

オンライン デモ

2

1

これは、ほぼデカルト展開 (コモンごと) から始まりますね?

– r2evans

2020 年 9 月 4 日 21:28

1

サンプルが示唆しているように、共通の値が両方のデータセットですべて同じであれば、はい。データ サイズが許せば、このソリューションは機能すると判断します。

– パフェ

2020 年 9 月 4 日 21:30

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。