一致しないIDと名前(類似した文字列)を持つシェープファイルにテーブルを結合する方法は?


8

自動化された解決策を見つけようとしているという迷惑な問題があります。簡略版は、シェープファイルと、国内の地域用に作成されたデータのテーブルがあることです。作成されたデータテーブルには、シェープファイルと照合するための標準化されたGID /管理コードは含まれておらず、リージョン名も完全には一致していません。よく見てみましょう。これが私のダミーデータフレーム+シェープファイルです。

library(rgdal)

#load in shapefile
arm <- readOGR("D:/Country-Shapefiles/ARM_adm_shp", layer = "ARM_adm1")

#create dummy data frame
id <- c(100:110)
name <- c("Aragatsotn", "Ararat", "Armavir", "Gaghark'unik'", "Kotayk", "Lorri", 
          "Shirak", "Syunik'", "Tavush", "Vayots' Dzor", "Yerevan City")
value <- runif(11, 0.0, 1.0)
df <- data.frame(id, name, value)

だから私が持っているのは、一見ランダムなID、地域名、およびコロプレスマップでプロットされる値のテーブルです。このように見えます:

> df
    id          name     value
1  100    Aragatsotn 0.6923852
2  101        Ararat 0.5762024
3  102       Armavir 0.4688358
4  103 Gaghark'unik' 0.4702253
5  104        Kotayk 0.9347992
6  105         Lorri 0.1937813
7  106        Shirak 0.5162604
8  107       Syunik' 0.4332389
9  108        Tavush 0.9889513
10 109  Vayots' Dzor 0.2182024
11 110  Yerevan City 0.5791886

関心のあるシェープファイル属性を見ると、次のことがわかります。

> arm@data[c("ID_1", "NAME_1")]

       ID_1      NAME_1
    0     1  Aragatsotn
    1     2      Ararat
    2     3     Armavir
    3     4      Erevan
    4     5 Gegharkunik
    5     6      Kotayk
    6     7        Lori
    7     8      Shirak
    8     9      Syunik
    9    10      Tavush
    10   11 Vayots Dzor

理想的にdfは、シェープファイルに結合するために、ある種の一致する管理IDを含めることになります。残念ながら、私が使用しているデータを作成した人は、これらの規則に従っていませんでした。あるいは、リージョン名自体を一致させるのは素晴らしいことですが、ご覧のように、名前ごとにわずかな違いがあります。

手作業での照合は常にバックアップソリューションですが、それを行うために時間をかけたい人はいますか?;)しかし、実際には、怠惰を除けば、私が取り組んでいるプロジェクトは数十および数十の国のマッピングになるため、手動で何もすることなくすべてを実行できる自動化されたソリューションを探しています。これは可能ですか?これらのほぼリージョン名をシェープファイルに一致させることはできますか?

補足:この投稿grepl文字列の部分一致を探していますが、各地域名を手動で入力するのではなく、列名から描画する必要があるため、これが潜在的な解決策かどうかはわかりません。

編集:手作業でIDを照合する場合、データフレームに新しい列を作成し、シェープファイルから完全に一致する用語を追加します。残念ながら、データの特殊性のため、名前の順序も一致していません。そのため、これにはまだ手動での入力が必要です。私はある種の完全に自動化されたソリューションを望んでいます(可能であれば)


幸運で、シェープファイルとテーブルの両方に同じ順序で同じ数のレコードがある場合、名前をコピーして新しいテーブルの隣接する列に貼り付け、その名前を使用してシェープファイルに結合し、それを結合することができます。名前を使用したテーブル。(または、シェープファイルのコピーを使用して、2007年より前のExcelまたはLibre / Open Officeシートのdbfにテーブル名を直接貼り付けます。)1対1のレコードの正確な数がなく、長い「ストレッチ」が多数ある場合そのうち、コピーと貼り付けを少し手作業で行うことができます。
ジョンズ

これは手動で手動で行うことになったものですが、残念ながらそれらは正しい順序ではありません。アルファベット順にリストされている場合でも、常に機能するとは限りません(この例では、エレバン=エレバンシティ。リストの残りが順不同になります)。
Lauren

回答:


6

stringdist含む文字列の部分的な類似性(距離)を計算するための多くのアルゴリズムを実装したパッケージを選びますJaro-winkler。ここにあなたのための速い解決策があります:

  #df to be joined
  id <- c(100:111)
  name <- c("Aragatsotn", "Ararat", "Armavir", "Gaghark'unik'", "Kotayk", "Lorri", 
            "Shirak", "Syunik'", "Tavush", "Vayots' Dzor", "Yerevan City","Aragatsotn")
  value <- runif(12, 0.0, 1.0)
  df <- data.frame(id, name, value)

  #create shape data df
  shpNames <- c("Aragatsotn",
               "Ararat",
               "Armavir",
               "Erevan",
               "Gegharkunik",
               "Kotayk",
               "Lori",
               "Shirak",
               "Syunik",
               "Tavush",
               "VayotsDzor")
  arm.data  <- data.frame(ID_1=1:11,NAME_1=shpNames)

  #simple match (only testing)
  match(df$name,arm.data$NAME_1)
  #simple merge (testing)
  merge(arm.data,df,by.x="NAME_1",by.y="name",all.x=TRUE)

  #partial match using stringdist package
  library("stringdist")
  am<-amatch(arm.data$NAME_1,df$name,maxDist = 3)
  b<-data.frame()
  for (i in 1:dim(arm.data)[1]) {
      b<-rbind(b,data.frame(arm.data[i,],df[am[i],]))
  }
  b

それは出力します:

ID_1      NAME_1  id          name     value
1     1  Aragatsotn 100    Aragatsotn 0.8510984
2     2      Ararat 101        Ararat 0.3004329
3     3     Armavir 102       Armavir 0.9258740
4     4      Erevan  NA          <NA>        NA
5     5 Gegharkunik 103 Gaghark'unik' 0.9935353
6     6      Kotayk 104        Kotayk 0.6025050
7     7        Lori 105         Lorri 0.9577662
8     8      Shirak 106        Shirak 0.6346550
9     9      Syunik 107       Syunik' 0.6531175
10   10      Tavush 108        Tavush 0.9726032
11   11  VayotsDzor 109  Vayots' Dzor 0.3457315

amatchメソッドのmaxDistパラメーターで遊ぶことができます。3はサンプルデータで最適に機能しますが!


はい、これは私の例ではうまくいきました!さらにいくつかテストしてみましょう!関連質問:シェープファイルを空間に保ちながら、これと同じ結合を実現するにはどうすればよいですか?このコードは、結合されたデータを含むデータフレームを作成したように見えますが、それでもマッピングできるようにする必要があります。
Lauren

データフレームを手動で作成したので、問題を再現できます。readOGRを介してシェープファイルを読み取る場合、出力クラスは「SpatialPointsDataFrame」などの「sp」派生クラスの1つになります。そして、それらはすべて、データフレームタイプのすべての属性データを含む「データ」属性を持っています。私の例では、データフレームに参加しており、幾何学的情報はそのままです。だからあなた例えば、単に変更arm.dataするarm@dataと、それだけで正常に動作します。
Farid Cheraghi

使用しないでください。arm@data大きな混乱が
生じ

6

これは非常に一般的な問題なので、Farid Cherの回答にいくつかの詳細を追加したいと思います。を使用amatchすると不思議に思うSpatialかもしれませが、これらのオブジェクトではスロットを使用しbase::mergeたりアクセスしたりしないでください@data。これは必然的にひどい混乱につながりbase::mergeます(レコードの順序を変更し、ジオメトリと一致しなくなります)。

代わりに、の最初の引数としてas sp::mergeを使用して、メソッドを使用SpatialPolygonsDataFramemergeます。また、レコードが重複している潜在的な問題にも注意してください。そして、例が自己完結型で再現可能なようにデータを追加しました。

library(raster)
#example data.frame
name <- c("Aragatsotn", "Ararat", "Armavir", "Gaghark'unik'", "Kotayk", "Lorri", "Shirak", "Syunik'", "Tavush", "Vayots' Dzor", "Yerevan City","Aragatsotn")
value <- runif(12, 0.0, 1.0)
df <- data.frame(name, value)

# example SpatialPolygonsDataFrame
arm <- getData('GADM', country='ARM', level=1)[, c('NAME_1')]

この

merge(arm, df, by.x='NAME_1', by.y='name')

メッセージで失敗する

#Error in .local(x, y, ...) : non-unique matches detected

には「Aragatsotn」の2つのレコードがあるためdfです。あなたができる

merge(arm, df, by.x='NAME_1', by.y='name', duplicateGeoms=TRUE)

しかし、通常、健全なアプローチは次のようなものを使用することです

df <- aggregate(df[, 'value', drop=FALSE], df[, 'name', drop=FALSE], mean)
m <- merge(arm, df, by.x='NAME_1', by.y='name')
data.frame(m)

data.frame(m)
#        NAME_1       value
#1   Aragatsotn 0.421576186
#2       Ararat 0.003138734
#3      Armavir 0.703402672
#4       Erevan          NA
#5  Gegharkunik          NA
#6       Kotayk 0.926883799
#7         Lori          NA
#8       Shirak 0.430585540
#9       Syunik          NA
#10      Tavush 0.121784395
#11 Vayots Dzor          NA

この場合、名前が一致しないため、マージはうまく機能しません。だからあなたは使うことができます

i <- amatch(df$name, arm$NAME_1, maxDist = 3)
df$match[!is.na(i)] <- arm$NAME_1[i[!is.na(i)]]
df
#            name       value       match
#1     Aragatsotn 0.421576186  Aragatsotn
#2         Ararat 0.003138734      Ararat
#3        Armavir 0.703402672     Armavir
#4  Gaghark'unik' 0.682169824 Gegharkunik
#5         Kotayk 0.926883799      Kotayk
#6          Lorri 0.128894086        Lori
#7         Shirak 0.430585540      Shirak
#8        Syunik' 0.163562936      Syunik
#9         Tavush 0.121784395      Tavush
#10  Vayots' Dzor 0.383439033 Vayots Dzor
#11  Yerevan City 0.168033419        <NA>

ほとんどありますが、「エレバンシティ」と「エレバン」は一致しませんでした。この場合、あなたは増やすことができますmaxDist

i <- amatch(df$name, arm$NAME_1, maxDist = 10)
df$match[!is.na(i)] <- arm$NAME_1[i[!is.na(i)]]

ただしmaxDist、バリアント名が非常に明確になる可能性があるため、増加しても常に機能するわけではなく、誤った一致を示すわけでもありません。そのため、多くの場合、次のような手動での置換を行うことになります。

df[df$name=="Yerevan City", 'match'] <- "Erevan"

どちらの場合も、

m <- merge(arm, df, by.x='NAME_1', by.y='match')

いずれにせよ、あなたは以下のことを確認したいと思うでしょうsum(table(i) > 1) == 0。ただし、 merge重複する一致がある場合はとにかく失敗するはずです。


素敵な詳細!これが私が私の答えを速く呼んだ理由です。ただし、一致したデータフレーム(df)にはジオメトリデータが含まれません。それでしょうか?OPは、結合されたdfをマップしたいと考えています。属性集約の代わりに空間集約は、複数の結合の場合の別の選択肢になります。
Farid Cheraghi 2016年

dfにはジオメトリがないため、の最後のステップmergeです。空間集計が異なる場合に便利です(この例では、場合NAME_1重複していた。)
ロバートHijmans
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.