2017-02-14 14 views
2

私は乱雑な文字列の列を含むデータフレームを持っています。それぞれの乱雑な文字列には、その中のどこかの国の名前が含まれています。ここではおもちゃのバージョンです:(country.name)に関連付けられている国名を持つ国名のregexs(regex)と別で1:countrycodeパッケージに文字列から正規表現への新しい文字列

df <- data.frame(string = c("Russia is cool (2015) ", 
          "I like - China", 
          "Stuff happens in North Korea"), 
       stringsAsFactors = FALSE) 

おかげで、私はまた、2つの有用な列を含む第2のデータセットを持っています。私たちはこのように、このデータセットをロードすることができます。

library(countrycode) 
data(countrycode_data) 

私はdf$stringの各行の国名を発見するcountrycode_data$regexで正規表現を使用するコードを記述したいと思います。正規表現を適切な国名と関連付けます(countrycode_data$country.name)。最後に、その名前を新しい列である関連する位置df$countryに書き込みます。このTBD操作を実行した後、dfは次のようになります。

     string        country 
1  Russia is cool (2015)      Russian Federation 
2    I like - China         China 
3 Stuff happens in North Korea Korea, Democratic People's Republic of 

私は非常にこれを行う方法の周りに私の頭をラップすることはできません。 greplwhichtolower%in%のさまざまな組み合わせを試してみましたが、方向や大きさ(またはその両方)が間違っています。

+0

私は 'countrycode_data'データフレームで' regex'列を見ていないよ... EDIT、用事、私は?私は 'country.name.en.regex'と呼ばれるそれを見つけたと思いますか? – rosscova

+0

'countrycode_data'の関連する列は単に' regex'と呼ばれるべきです。適切な名前を持つ関連する列は 'country.name'です。 – ulfelder

+0

このようなものが役に立ちます:http://stackoverflow.com/questions/21165256/r-merge-data-frames-allow-inexact-id-matching-eg-withditional-characters – Bulat

答えて

1

クロス参加(ブローアップされます、あなたのデータ)と可能な解決策である:以下の3つのテキスト文字列に何が起こるかを参照してください。現実世界の元のデータはそれ以上の大きさのオーダーであるのに対し、countrycode_data data.frameの行には特に200行しかないので、ループしている。 、

for(i in seq_along(patt)) { 
    df$country[grepl(patt[i], df$string, ignore.case=TRUE, perl=TRUE)] <- name[i] 
} 

他の人が指摘したように:その後、我々はループが新しいコラムを書くことができ

patt <- countrycode_data$country.name.en.regex[!is.na(countrycode_data$country.name.en.regex)] 
name <- countrycode_data$country.name.en[!is.na(countrycode_data$country.name.en.regex)] 

:ので、長い名前の

は、私は2つの国コードのデータの列を抽出します北朝鮮は国コードデータで指定された正規表現と一致しません。

+1

エレガント、ありがとうございます。 (そして、実際には、私は実際に「北朝鮮」の希望結果を得ています。) – ulfelder

+1

はい、いい考えです。私は '(spply(countrycode_data $ country.name.en.regex、stringi :: stri_detect_regex、str = tolower(df $ string))、arr.ind = TRUE)'のような 'stringi'を使って同じことを考えていました'(ここで 'col'は' countrycode_data $ country.name.en'内の行インデックスです) –

+0

@DavidArenburgも良い選択肢です。最後に、あなたは1つのループを何らかの方法で作る必要があります。 stringiは、正規表現のマッチングを大幅に向上させる可能性があります(もちろん私のアプローチで採用することもできます)。 –

1

これは実際の解決策ですが、countrycode_dataフレーム内の異なる列名を参照しています。これはシステム上で別々に表示されるためです。私はまた、いくつかの*applyコールに頼っていましたが、これはおそらく理想的ではありません。私はあなたがそれらのいくつかをベクトル化することができると確信しています、私はちょうど自分自身を確認していない。

matches <- sapply(df$string, function(x) { 

    # find matches by running all regex strings (maybe cound be vectorised?) 
    find.match <- lapply(countrycode_data$country.name.en.regex, grep, x = x, ignore.case = TRUE, perl = TRUE) 

    # note down which patterns came up with a match 
    matches <- which(sapply(find.match, length) > 0) 

    # now cull the matches list down to only those with a match 
    find.match <- find.match[ sapply(find.match, length) > 0 ] 

    # get rid of NA matches (not sure why these come up) 
    matches <- matches[ sapply(find.match, is.na) == FALSE ] 

    # now only return the value (reference to the match) if there is one (otherwise we get empty returns) 
    ifelse(length(matches) == 0, NA_integer_, matches) 
}) 

# now use the vector of references to match up country names 
df$country <- countrycode_data$country.name.en[ matches ] 

> df 
         string   country 
1  Russia is cool (2015) Russian Federation 
2    I like - China    China 
3 Stuff happens in North Korea    <NA> 

注:あなたの3番目の文字列「スタッフは北朝鮮に起こる」countrycode_dataセットで128行に一致する必要がありますが、それはしていません。私はその理由は、そこに正規表現(^(?=.*democrat|people|north|d.*p.*.r).*\bkorea|dprk|korea.*(d.*p.*r))は、 "北"が文字列の先頭でなければならないと指定しているようだと思う。私は正規表現で自分自身では良くないが、私はそれが^が指定していると信じている。

ここ
grepl("^(?=.*democrat|people|north|d.*p.*.r).*\\bkorea|dprk|korea.*(d.*p.*r)", 
     c("korea", "north korea", "aaa north korea"), 
     perl = TRUE, ignore.case = TRUE) 
# [1] FALSE TRUE FALSE 
0

は、私は、このループのために行くだろう

library(countrycode) 
data(countrycode_data) 

library(data.table) 
df <- data.table(string = c("Russia is cool (2015) ", 
          "I like - China", 
          "Stuff happens in North Korea"), 
       stringsAsFactors = FALSE) 

# adding dummy for full cross-join merge 
df$dummy <- 0L 
country.dt <- data.table(countrycode_data[, c("country.name.en", "country.name.en.regex")]) 
country.dt$dummy <- 0L 

# merging original data to countries to get all possible combinations 
res.dt <- merge(df, country.dt, by ="dummy", all = TRUE, allow.cartesian = TRUE) 

# there are cases with NA regex 
res.dt <- res.dt[!is.na(country.name.en.regex)] 

# find matches 
res.dt[, match := grepl(country.name.en.regex, string, perl = T, ignore.case = T), by = 1:nrow(res.dt)] 

# filter out matches 
res.dt <- res.dt[match == TRUE, .(string, country.name.en)] 
res.dt 

#     string country.name.en 
# 1: Russia is cool (2015) Russian Federation 
# 2:   I like - China    China 
+1

結局のところ、行操作でやっているだけならば、クロスジョインはなぜですか?シンプルな「サプリー」IMOをやってみることができます。 –

+0

私はこの特定のケースでは、予想される一致回数が少ないため、非常に良い解決策ではないことに同意します。しかしそれはそうでなければ同様の仕事のために有用かもしれません – Bulat

2

これはまさに国コードパッケージの目的なので、これを自分でコード化する理由はありません。ちょうどこのように使う...

library(countrycode) 
df <- data.frame(string = c("Russia is cool (2015) ", "I like - China", 
          "Stuff happens in North Korea"), stringsAsFactors = FALSE) 

df$country.name <- countrycode(df$string, 'country.name', 'country.name') 

「スタッフが北朝鮮に起こる」のために特別に、この場合には、それが明確な一致を見つけることができませんが、それは実際に北朝鮮と韓国のための正規表現に問題だ(私はここのために問題を開きましたhttps://github.com/vincentarelbundock/countrycode/issues/139)。さもなければ、あなたがしたいことはプリンシパルで動作するはずです。

(:そうcountry.nameは今country.name.enで、countrycodeの新バージョンがちょうど、CRANにリリースされましたv0.19我々は新しい言語を追加したので、列名が少し変更されている、とregexは今country.name.en.regexです。サイドノートでは、具体的@ulfelderします)

1

私はcountrycodeの管理者です。 @ cj-yetmanは正しい答えを出しました。あなたが直面した北朝鮮問題は、Githubのcountrycodeの開発版で修正されました。

あなたは国の名前やコードに文章を変換するには、直接国番号を使用することができます。

> library(devtools) 
> install_github('vincentarelbundock/countrycode') 
> library(countrycode) 
> df <- data.frame(string = c("Russia is cool (2015) ", 
+        "I like - China", 
+        "Stuff happens in North Korea"), 
+     stringsAsFactors = FALSE) 
> df$iso3c = countrycode(df$string, 'country.name', 'country.name') 
> df 
         string         iso3c 
1  Russia is cool (2015)      Russian Federation 
2    I like - China         China 
3 Stuff happens in North Korea Democratic People's Republic of Korea 
+1

ありがとう、@ Vincent!ある意味で、 'countrycode'特有のものを得る前に私はより一般的な答えを得てうれしいです。なぜなら、これは問題を解決するパッケージがない状況で私にとって再び起こり得るからです。 – ulfelder

+0

は 'countrycode'を使用して複数の国名を1つの文字列で捕捉する効率的な方法ですか?たとえば、「スーダンと南スーダンの事務総長報告」の文字列があり、「スーダン;南スーダン」のような文字列を返すとしますか?私は折り畳む方法を知っています。それは、私にぴったりの1つ以上の試合を返す。 – ulfelder

+1

国コード付きの状態ではすぐに使用できますが、内部コードを見ると、パッケージはすでに複数の一致を追跡しています。同じコードを使用して、 '' destination_list''をキャッチすることができます。こちらをご覧ください:https://github.com/vincentarelbundock/countrycode/blob/master/R/countrycode.R#L123 – Vincent