2011-10-17 12 views
6

私はクロージャの周りを頭で囲んでいるのですが、私はと思っています。私は彼らが役に立つかもしれないケースを見つけました。データマージ慣用の解決策としてのクロージャ

私はで動作するように、次の部分があります。

  • 状態名と機能
  • data.frameに収納された状態名をきれいにするために設計された正規表現のセット、(標準化された形式のものを(上記の関数は作成されます)と状態IDコードは、2つをリンクするために( "マージマップ")

考えて、いくつかのdata.frameは、 "、"ワシントンDC "、"コロンビア特別区 "など)、罪を犯すgle関数は、状態名列が削除され、状態IDコードのみが残っている同じdata.frameを返します。その後、その後のマージは一貫して発生する可能性があります。

私はこれをいくつでも行うことができますが、特に優雅に思える方法の1つは、マージマップと正規表現とコード処理のすべてをクロージャ内に格納することです(クロージャがデータ付き関数)。

質問1:これは合理的な考えですか?

質問2:そうであれば、どのようにRで行うのですか?ここで

は、例えば、データ上で動作愚かな簡単なきれいな状態名機能です:

dat <- structure(list(state = c("Alabama", "Alaska", "Arizona", "Arkansas", 
"California", "Colorado", "Connecticut", "Delaware", "District of Columbia", 
"Florida"), pop08 = structure(c(29L, 44L, 40L, 18L, 25L, 30L, 
22L, 48L, 36L, 13L), .Label = c("1,050,788", "1,288,198", "1,315,809", 
"1,316,456", "1,523,816", "1,783,432", "1,814,468", "1,984,356", 
"10,003,422", "11,485,910", "12,448,279", "12,901,563", "18,328,340", 
"19,490,297", "2,600,167", "2,736,424", "2,802,134", "2,855,390", 
"2,938,618", "24,326,974", "3,002,555", "3,501,252", "3,642,361", 
"3,790,060", "36,756,666", "4,269,245", "4,410,796", "4,479,800", 
"4,661,900", "4,939,456", "5,220,393", "5,627,967", "5,633,597", 
"5,911,605", "532,668", "591,833", "6,214,888", "6,376,792", 
"6,497,967", "6,500,180", "6,549,224", "621,270", "641,481", 
"686,293", "7,769,089", "8,682,661", "804,194", "873,092", "9,222,414", 
"9,685,744", "967,440"), class = "factor")), .Names = c("state", 
"pop08"), row.names = c(NA, 10L), class = "data.frame") 

とサンプルマージ:

cleanStateNames <- function(x) { 
    x <- tolower(x) 
    x[grepl("columbia",x)] <- "DC" 
    x 
} 

ここではいくつかの例のデータは、最終的な機能は、上で実行されることですmap(実際のものはFIPSコードを状態にリンクするので、簡単に生成することはできません):

merge_map <- data.frame(state=dat$state, id=seq(10)) 

EDIT以下crippledlambdaの答えのオフビルは、ここに機能の試みです:私はこの質問に解決を検討したいの前に

prepForMerge <- local({ 
    merge_map <- structure(list(state = c("alabama", "alaska", "arizona", "arkansas", "california", "colorado", "connecticut", "delaware", "DC", "florida"), id = 1:10), .Names = c("state", "id"), row.names = c(NA, -10L), class = "data.frame") 
    list(
    replace_merge_map=function(new_merge_map) { 
     merge_map <<- new_merge_map 
    }, 
    show_merge_map=function() { 
     merge_map 
    }, 
    return_prepped_data.frame=function(dat) { 
     dat$state <- cleanStateNames(dat$state) 
     dat <- merge(dat,merge_map) 
     dat <- subset(dat,select=c(-state)) 
     dat 
    } 
) 
}) 

> prepForMerge$return_prepped_data.frame(dat) 
     pop08 id 
1 4,661,900 1 
2  686,293 2 
3 6,500,180 3 
4 2,855,390 4 
5 36,756,666 5 
6 4,939,456 6 
7 3,501,252 7 
8  591,833 9 
9  873,092 8 
10 18,328,340 10 

2つの問題が残っている:prepForMerge$return_prepped_data.frame(dat)を呼び出す

  1. は痛い毎回です。どのように私はちょうどprepForMerge(dat)を呼び出すことができるようなデフォルトの機能を持っている?私はそれがどのように実装されているかはわかりませんが、おそらくデフォルトfxnのための規約があります。

  2. merge_map定義にデータとコードを混ぜて使用しないでください。理想的には、別の場所でmerge_mapをクリーンアップしてから、クロージャーの中に入れて保存してください。

+0

これは私にクロージャを使用する自然な場所のようではありません。 – hadley

+0

@hadleyこれは質問1の範囲内であるため、答えとして投稿することはできますか?おそらく、これをクロージャの理想的な状況よりも少なくするようなことについての精緻化が必要です(実際にデータを更新する必要はありませんか? ?私はクロージャの「方法」については大丈夫ですが、「なぜ/いつ」の部分を使用するのに苦労しています。 –

答えて

4

私はあなたの質問のポイントを逃してもよいが、これはあなたが閉鎖に使用できる1つの方法です:

> replaceStateNames <- local({ 
+ statenames <- c("Alabama", "Alaska", "Arizona", "Arkansas", 
+     "California", "Colorado", "Connecticut", "Delaware", 
+     "District of Columbia", "Florida") 
+ function(patt,newtext) { 
+  statenames <- tolower(statenames) 
+  statenames[grepl(patt,statenames)] <- newtext 
+  statenames 
+ } 
+ }) 
> 
> replaceStateNames("columbia","DC") 
[1] "alabama"  "alaska"  "arizona"  "arkansas" "california" 
[6] "colorado" "connecticut" "delaware" "DC"   "florida"  
> replaceStateNames("alaska","palincountry") 
[1] "alabama"    "palincountry"   "arizona"    
[4] "arkansas"    "california"   "colorado"    
[7] "connecticut"   "delaware"    "district of columbia" 
[10] "florida"    
> replaceStateNames("florida","jebbushland") 
[1] "alabama"    "alaska"    "arizona"    
[4] "arkansas"    "california"   "colorado"    
[7] "connecticut"   "delaware"    "district of columbia" 
[10] "jebbushland"  
> 

をしかし、一般化するために、あなたは、データフレームの定義とstatenamesを置き換えることができます関数呼び出しに引数として渡すことなくこのデータフレームを使用する関数(または関数のリスト)を返します。例(私はgreplignore.case=TRUE引数を使用しました。注):

> replaceStateNames <- local({ 
+ statenames <- c("Alabama", "Alaska", "Arizona", "Arkansas", 
+     "California", "Colorado", "Connecticut", "Delaware", 
+     "District of Columbia", "Florida") 
+ list(justreturn=function(patt,newtext) { 
+  statenames[grepl(patt,statenames,ignore.case=TRUE)] <- newtext 
+  statenames 
+ },reassign=function(patt,newtext) { 
+  statenames <<- replace(statenames,grepl(patt,statenames,ignore.case=TRUE),newtext) 
+  statenames 
+ }) 
+ }) 

ただ、最初の例のように:

> replaceStateNames$justreturn("columbia","DC") 
[1] "Alabama"  "Alaska"  "Arizona"  "Arkansas" "California" 
[6] "Colorado" "Connecticut" "Delaware" "DC"   "Florida"  

ちょうど元の値ことを確認するためにstatenamesの字句スコープの値を返します変更されていません:

> replaceStateNames$justreturn("shouldnotmatch","anythinghere") 
[1] "Alabama"    "Alaska"    "Arizona"    
[4] "Arkansas"    "California"   "Colorado"    
[7] "Connecticut"   "Delaware"    "District of Columbia" 
[10] "Florida"    

は同じことを行いますが、変更は "永久" を作る:

> replaceStateNames$reassign("columbia","DC") 
[1] "Alabama"  "Alaska"  "Arizona"  "Arkansas" "California" 
[6] "Colorado" "Connecticut" "Delaware" "DC"   "Florida"  

これらの機能に付加されているstatenamesの値が変更されています。いずれの場合においても

> replaceStateNames$justreturn("shouldnotmatch","anythinghere") 
[1] "Alabama"  "Alaska"  "Arizona"  "Arkansas" "California" 
[6] "Colorado" "Connecticut" "Delaware" "DC"   "Florida"  

、あなたは「マージマップ」またはあなたが望む任意の他のマッピングとデータフレーム、およびこれらの単純な関数でstatenamesを置き換えることができます。 "マージ" の

編集

いえば、これはあなたが探しているものでしょうか?クロージャを使用して、最初?merge例の実装:

> authors <- data.frame(surname = I(c("Tukey", "Venables", "Tierney", "Ripley", "McNeil")), 
+      nationality = c("US", "Australia", "US", "UK", "Australia"), 
+      deceased = c("yes", rep("no", 4))) 
> books <- data.frame(name = I(c("Tukey", "Venables", "Tierney", 
+      "Ripley", "Ripley", "McNeil", "R Core")), 
+      title = c("Exploratory Data Analysis", 
+      "Modern Applied Statistics ...", 
+      "LISP-STAT", 
+      "Spatial Statistics", "Stochastic Simulation", 
+      "Interactive Data Analysis", 
+      "An Introduction to R"), 
+      other.author = c(NA, "Ripley", NA, NA, NA, NA, 
+      "Venables & Smith")) 
> 
> mergewithauthors <- with(list(authors=authors),function(books) 
+ merge(authors, books, by.x = "surname", by.y = "name")) 
> 
> mergewithauthors(books) 
    surname nationality deceased       title other.author 
1 McNeil Australia  no  Interactive Data Analysis   <NA> 
2 Ripley   UK  no   Spatial Statistics   <NA> 
3 Ripley   UK  no   Stochastic Simulation   <NA> 
4 Tierney   US  no      LISP-STAT   <NA> 
5 Tukey   US  yes  Exploratory Data Analysis   <NA> 
6 Venables Australia  no Modern Applied Statistics ...  Ripley 

編集2

辞書的にバインドされるオブジェクトにファイルを読み取るには、

fn <- local({ 
    data <- read.csv("filename.csv") 
    function(...) { 
    ... 
    } 
}) 

または

を行うことができますいずれか
fn <- with(list(data=read.csv("filename.csv")), 
    function(...) { 
     ... 
    } 
    }) 

または

fn <- with(local(data <- read.csv("filename.csv")), 
    function(...) { 
     ... 
    } 
    }) 

などです。 (私は、関数(...)があなたの "merge_map"と関係していると仮定します)。 localの代わりにevalqを使用することもできます。オブジェクトがグローバル空間に存在する(または環境を囲む)「をもたらす」ために、あなただけ行うことができ、次の

globalobj <- value  ## could be from read.csv() 
fn <- local({ 
    localobj <- globalobj ## if globalobj is not locally defined, 
         ## R will look in enclosing environment 
         ## in this case, the globalenv() 
    function(...) { 
    ... 
    } 
}) 

その後、globalobjが後でほぼ(?)すべて以降(機能に添付localobjを変更しません修正Rは値渡しセマンティクスに従う)。上記の例に示すようにlocalの代わりにwithを使用することもできます。

+1

恐ろしい答え。ありがとうございました。データをコードとしてロードしています。私はあなたが単にread.csvのファイルかクロージャの中のファイルからそれを行うと仮定しますが、オブジェクトのコピーをその関数に格納したい場合はどうでしょうか?例えば。 merge_mapがコードの前半に作成されていて、クロージャにそのコードを持ってきたいのですか? –

+0

うれしい私は助けることができます。 – hatmatrix

関連する問題