Làm thế nào để tham gia một bảng vào một shapefile với ID và tên không khớp (các chuỗi tương tự)?


8

Tôi đang gặp một vấn đề khó chịu mà tôi đang cố gắng tìm một giải pháp tự động. Phiên bản tốc ký là tôi có một shapefile và một bảng dữ liệu được tạo cho các vùng trong các quốc gia. Bảng dữ liệu đã tạo KHÔNG có bất kỳ loại mã GID / quản trị viên được tiêu chuẩn hóa nào khớp với shapefiles và tên vùng cũng không khớp chính xác. Hãy xem xét kỹ hơn; đây là khung dữ liệu giả của tôi + shapefile.

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)

Vì vậy, những gì tôi có là một bảng với ID dường như ngẫu nhiên, tên khu vực và một giá trị được vẽ với bản đồ choropleth. Trông như thế này:

> 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

Nhìn vào các thuộc tính quan tâm của shapefile, chúng tôi đã nhận được điều này:

> 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

Lý tưởng nhất, dfsẽ bao gồm một số loại ID quản trị phù hợp để tham gia vào shapefile. Thật không may, bất cứ ai tạo ra dữ liệu mà tôi đang sử dụng đều không tuân theo các quy ước này. Ngoài ra, thật tuyệt vời khi kết hợp các tên khu vực ... nhưng như bạn có thể thấy, có mỗi biến thể nhỏ trong mỗi tên.

Kết hợp bằng tay luôn là một giải pháp dự phòng, nhưng ai muốn dành thời gian để làm điều đó? ;) Nhưng thực sự, ngay cả khi lười biếng, dự án mà tôi đang thực hiện sẽ lập bản đồ hàng chục và hàng chục quốc gia khác nhau, vì vậy tôi đang tìm kiếm một giải pháp tự động có thể làm mọi thứ mà không phải làm gì bằng tay. Điều này có thể không? Tôi bằng cách nào đó có thể ghép các tên khu vực cực đại này với các shapefiles không?

Sidenote: Tôi đang tìm kiếm greplcác chuỗi khớp một phần cho mỗi bài đăng này , nhưng tôi không chắc liệu đây có phải là một giải pháp tiềm năng không vì tôi cần rút ra từ các tên cột thay vì nhập từng tên khu vực.

EDIT: Khi tôi khớp ID bằng tay, những gì tôi đã làm là tạo một cột mới trong khung dữ liệu của mình và thêm các thuật ngữ khớp chính xác từ shapefile. Thật không may, vì tính đặc thù của dữ liệu, thứ tự của các tên không khớp với nhau, vì vậy điều này vẫn yêu cầu một số nhập thủ công. Tôi hy vọng một số loại giải pháp hoàn toàn tự động (nếu thậm chí có thể).


Nếu bạn may mắn và có cùng số lượng bản ghi theo cùng một thứ tự trong cả shapefile và bảng, bạn có thể sao chép và dán tên vào các cột liền kề trong một bảng mới, nối nó với shapefile bằng tên của nó và nối nó với bảng sử dụng tên của nó. (Hoặc sử dụng một bản sao của shapefile của bạn, dán trực tiếp tên bảng vào dbf của nó trong bảng tính Excel hoặc Libre / Open Office trước năm 2007.) Nếu bạn không có số lượng chính xác từ một đến một bản ghi nhưng nhiều "kéo dài" trong số đó bạn có thể trộn một chút công việc thủ công với bản sao và bột nhão.
johns

Đây là những gì tôi đã làm bằng tay, nhưng tiếc là chúng không theo đúng thứ tự. Ngay cả khi được liệt kê theo thứ tự abc, nó vẫn có thể không hoạt động mọi lúc (trong ví dụ này, Erevan = Thành phố Yerevan, nơi ném phần còn lại của danh sách ra khỏi trật tự).
Lauren

Câu trả lời:


6

Tôi sẽ dùng stringdistgói đã triển khai nhiều thuật toán để tính độ tương tự một phần (khoảng cách) của các chuỗi bao gồm Jaro-winkler. Đây là một giải pháp nhanh chóng cho bạn:

  #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

nó xuất ra:

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

Bạn có thể chơi với tham số maxDist của phương thức amatch. Mặc dù 3 hoạt động tốt nhất với dữ liệu mẫu của bạn!


Vâng, điều này làm việc cho ví dụ của tôi! Bây giờ để kiểm tra một vài nữa! Câu hỏi liên quan: làm thế nào tôi có thể đạt được sự tham gia tương tự này trong khi vẫn giữ không gian shapefile? Có vẻ như đoạn mã này vừa tạo một khung dữ liệu với dữ liệu được nối, nhưng tôi vẫn sẽ cần có thể ánh xạ nó.
Lauren

Tôi đã tạo khung dữ liệu theo cách thủ công để vấn đề của bạn có thể được tái tạo. Khi bạn đọc một shapefile qua readOGR, lớp đầu ra sẽ là một trong các lớp dẫn xuất "sp", chẳng hạn như "SpatialPointsDataFrame". Và tất cả chúng đều có thuộc tính "data" chứa tất cả dữ liệu thuộc tính thuộc kiểu dataframe. Trong ví dụ của tôi, tôi đang tham gia vào khung dữ liệu và thông tin hình học không bị ảnh hưởng. Vì vậy, đối với ví dụ của bạn, chỉ cần thay đổi arm.datathành arm@datavà nó sẽ hoạt động tốt.
Farid Cheraghi

Không sử dụng arm@data, điều đó sẽ tạo ra một mớ hỗn độn lớn (các bản ghi không khớp với hình học chính xác của chúng)
Robert Hijmans

6

Tôi muốn thêm một số chi tiết vào câu trả lời của Farid Cher's vì đây là một vấn đề rất phổ biến. Sử dụng amatchcó thể làm điều kỳ diệu, nhưng với những Spatialđối tượng này, bạn không nên sử dụng base::mergekhông truy cập vào @datavị trí. Điều đó chắc chắn sẽ dẫn đến một mớ hỗn độn khủng khiếp ( base::mergethay đổi thứ tự của các bản ghi và chúng sẽ không còn phù hợp với hình học).

Thay vào đó, sử dụng sp::mergephương thức, bằng cách sử dụng SpatialPolygonsDataFrameđối số đầu tiên trong merge. Cũng lưu ý vấn đề tiềm năng của việc có hồ sơ trùng lặp. Và tôi đã thêm dữ liệu để ví dụ này được khép kín và có thể tái tạo.

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')]

Điều này

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

thất bại với tin nhắn

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

Bởi vì có hai bản ghi cho "Aragatsotn" trong df. Bạn có thể làm

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

Nhưng thông thường, cách tiếp cận lành mạnh là sử dụng đôi khi như

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

Bây giờ, hợp nhất không hoạt động tốt trong trường hợp này vì tên không khớp. Vì vậy, bạn có thể sử dụng

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>

Gần như ở đó, nhưng "Thành phố Yerevan" không khớp với "Erevan". Trong trường hợp này bạn có thể tăngmaxDist

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

Nhưng việc tăng maxDistkhông phải lúc nào cũng hoạt động hoặc đưa ra các kết quả khớp sai vì tên biến thể của becuase có thể rất khác biệt. Vì vậy, trong nhiều trường hợp, bạn sẽ thực hiện một số thay thế thủ công như:

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

Trong cả hai trường hợp theo sau

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

Trong mọi trường hợp, bạn sẽ muốn kiểm tra nếu sum(table(i) > 1) == 0; mặc dù mergekhông nên thất bại nếu có trùng lặp


Chi tiết đẹp! Đây là lý do tại sao tôi gọi câu trả lời của tôi nhanh chóng . Tuy nhiên, khung dữ liệu phù hợp (df) sẽ không chứa dữ liệu hình học. phải không OP muốn lập bản đồ df đã tham gia. Tổng hợp không gian thay vì tổng hợp thuộc tính sẽ là một lựa chọn khác cho nhiều trường hợp tham gia.
Farid Cheraghi

df không có hình học, do đó bước cuối cùng với merge. Tổng hợp không gian rất hữu ích cho các trường hợp khác nhau (nếu, trong ví dụ này, NAME_1có các bản sao.)
Robert Hijmans
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.