22. 更多的空間連接

在上一個章節中,我們看過 ST_Centroid(geometry)ST_Union([geometry]) 函式,以及一些簡單範例。在本章節中,我們將對它們進行更精細的操作。

22.1. 建立人口普查區塊資料表

在工作坊 \data\ 目錄中,有一個檔案包含屬性資料,但沒有幾何, nyc_census_sociodata.sql。該資料表包含紐約市的有趣社會經濟資料:上下班時間、所得、和教育程度。只有一個問題。資料是根據「人口普查區塊」進行摘要,我們沒有人口普查區塊的空間資料!

在本章節中,我們將

  • 載入 nyc_census_sociodata.sql 資料表

  • 為人口普查區塊建立一個空間資料表

  • 將屬性資料與空間資料結合

  • 使用我們的最新資料進行一些分析

22.1.1. 載入 nyc_census_sociodata.sql

  1. 在 PgAdmin 中開啟 SQL 查詢視窗

  2. 從功能表中選擇 檔案->開啟,並瀏覽至 nyc_census_sociodata.sql 檔案

  3. 按下「執行查詢」按鈕

  4. 如果您按下 PgAdmin 中的「重整」按鈕,資料表清單中現在應該會包含一個 nyc_census_sociodata 資料表

22.1.2. 建立人口普查區塊資料表

正如我們在上一個章節中所見,我們可以透過 blkid 金鑰子字串摘要來建立更高級的幾何,從普查區塊。為了取得人口普查區塊,我們需要根據 blkid 的前 11 個字元來進行群組摘要。

360610001001001 = 36 061 000100 1 001

36     = State of New York
061    = New York County (Manhattan)
000100 = Census Tract
1      = Census Block Group
001    = Census Block

使用 ST_Union 彙總來建立新的資料表

-- Make the tracts table
CREATE TABLE nyc_census_tract_geoms AS
SELECT
  ST_Union(geom) AS geom,
  SubStr(blkid,1,11) AS tractid
FROM nyc_census_blocks
GROUP BY tractid;

-- Index the tractid
CREATE INDEX nyc_census_tract_geoms_tractid_idx
  ON nyc_census_tract_geoms (tractid);

22.1.3. 將屬性與空間資料結合

使用標準屬性連接,將區塊幾何資料表與區塊屬性資料表結合

-- Make the tracts table
CREATE TABLE nyc_census_tracts AS
SELECT
  g.geom,
  a.*
FROM nyc_census_tract_geoms g
JOIN nyc_census_sociodata a
ON g.tractid = a.tractid;

-- Index the geometries
CREATE INDEX nyc_census_tract_gidx
  ON nyc_census_tracts USING GIST (geom);

22.1.4. 回答一個有趣的問題

回答一個有趣的問題!「列出前 10 名依擁有碩士學位的人口比例排列的紐約社區」。

SELECT
  100.0 * Sum(t.edu_graduate_dipl) / Sum(t.edu_total) AS graduate_pct,
  n.name, n.boroname
FROM nyc_neighborhoods n
JOIN nyc_census_tracts t
ON ST_Intersects(n.geom, t.geom)
WHERE t.edu_total > 0
GROUP BY n.name, n.boroname
ORDER BY graduate_pct DESC
LIMIT 10;

我們加總我們感興趣的統計資料,然後在最後將它們除以總計。為了避免除以零的錯誤,我們不麻煩引進人口數為零的區域。

 graduate_pct |       name        | boroname
--------------+-------------------+-----------
         47.6 | Carnegie Hill     | Manhattan
         42.2 | Upper West Side   | Manhattan
         41.1 | Battery Park      | Manhattan
         39.6 | Flatbush          | Brooklyn
         39.3 | Tribeca           | Manhattan
         39.2 | North Sutton Area | Manhattan
         38.7 | Greenwich Village | Manhattan
         38.6 | Upper East Side   | Manhattan
         37.9 | Murray Hill       | Manhattan
         37.4 | Central Park      | Manhattan

備註

紐約的地理學家一定會對「Flatbush」出現在這個受教育程度過高的社區清單上感到疑惑。答案將在下一章中討論。

22.2. 多邊形/多邊形連線

我們使用 ST_Intersects(geometry_a, geometry_b) 函式在我們的有趣查詢(在 回答一個有趣的問題 中)確定每個社區摘要中要包含哪些人口普查區的多邊形。這會導致一個問題:如果某個區剛好位於兩個社區的邊界上會怎樣?它會和兩個區相交,因此會同時包含在兩個彙總統計資料中。

_images/centroid_neighborhood.png

要避免這種重複計數,有兩種方法

  • 簡單的方法是確保每個區僅計入一個彙總區域中(使用 ST_Centroid(geometry)

  • 複雜的方法是將交會的區劃分在邊界上(使用 ST_Intersection(geometry,geometry)

以下是使用簡單方法避免在我們的大學教育查詢中重複計數的範例

SELECT
  100.0 * Sum(t.edu_graduate_dipl) / Sum(t.edu_total) AS graduate_pct,
  n.name, n.boroname
FROM nyc_neighborhoods n
JOIN nyc_census_tracts t
ON ST_Contains(n.geom, ST_Centroid(t.geom))
WHERE t.edu_total > 0
GROUP BY n.name, n.boroname
ORDER BY graduate_pct DESC
LIMIT 10;

請注意,查詢現在執行需要較長的時間,因為必須對每個人口普查區執行 ST_Centroid 函式。

 graduate_pct |        name         | boroname
--------------+---------------------+-----------
         48.0 | Carnegie Hill       | Manhattan
         44.2 | Morningside Heights | Manhattan
         42.1 | Greenwich Village   | Manhattan
         42.0 | Upper West Side     | Manhattan
         41.4 | Tribeca             | Manhattan
         40.7 | Battery Park        | Manhattan
         39.5 | Upper East Side     | Manhattan
         39.3 | North Sutton Area   | Manhattan
         37.4 | Cobble Hill         | Brooklyn
         37.4 | Murray Hill         | Manhattan

避免重複計數會改變結果!

22.2.1. Flatbush 呢?

特別是,Flatbush 社區已從清單中刪除。原因可以從我們表格中 Flatbush 社區的地圖中更仔細地觀察中得知。

_images/nyc_tracts_flatbush.jpg

根據我們的資料來源定義,Flatbush 實際上並非傳統意義上的社區,因為它僅涵蓋了展望公園的面積。該區域的人口普查區自然只記錄了零位居民。不過,社區邊界確實刮到了公園北側(在士紳化的 Park Slope 社區)邊界上的其中一個昂貴的人口普查區。在使用多邊形/多邊形測試時,這個單獨的區域被加入到否則空無一物的 Flatbush 中,導致該查詢的分數非常高。

22.3. 大範圍半徑距離連線

一個很好玩的查詢是:「接近(500 公尺以內)地鐵站的人員通勤時間如何不同於遠離地鐵站的人員?」

然而,這個問題會遇到重複計算的問題:很多人會同時接近多個地鐵站,500 公尺以內。比較紐約的人口

SELECT Sum(popn_total)
FROM nyc_census_blocks;
8175032

與紐約 500 公尺以內地鐵站人口

SELECT Sum(popn_total)
FROM nyc_census_blocks census
JOIN nyc_subway_stations subway
ON ST_DWithin(census.geom, subway.geom, 500);
10855873

靠近地鐵的人比一般人多!很明顯地,我們的簡單 SQL 會產生一個很大的重複計算錯誤。您可以查看緩衝地鐵的圖片,了解問題。

_images/subways_buffered.png

解決方法是確保我們在將資料區塊傳遞到查詢的摘要部分之前,僅有明確資料區塊。我們可以透過將查詢區分為一個子查詢(尋找明確的區塊),並包裹在摘要查詢中(傳回我們的答案),來達成目標

WITH distinct_blocks AS (
  SELECT DISTINCT ON (blkid) popn_total
  FROM nyc_census_blocks census
  JOIN nyc_subway_stations subway
  ON ST_DWithin(census.geom, subway.geom, 500)
)
SELECT Sum(popn_total)
FROM distinct_blocks;
5005743

這樣就好了!因此超過一半的紐約人口都在距離地鐵 500 公尺(約步行 5-7 分鐘)的範圍內。