返回文章列表

Python 餐廳資料整合與分析技術

本文探討如何使用 Python 整合多個網路服務的餐廳資料,包含詳細資料解析、地理編碼、衛生檢查資料整合與分析,並示範如何運用 BeautifulSoup 解析 HTML、處理 JSON 資料、計算距離以及篩選高品質餐廳,同時也介紹了芝加哥市開放資料平台的應用與資料清理技巧。

Web 開發 資料分析

網路爬蟲技術結合地理編碼與資料分析,能有效整合多個來源的餐廳資訊。本文示範如何使用 Python 的 BeautifulSoup 解析餐廳網頁,擷取名稱、地址、電話等詳細資料,並利用地理編碼函式計算餐廳距離。此外,文章也說明如何整合芝加哥市提供的餐廳衛生檢查 JSON 資料,進行資料清理、轉換與篩選,最終產出高品質餐廳清單。程式碼範例中使用 SimpleNamespace 簡化資料結構,並示範如何運用 haversine 函式計算距離,以及如何根據衛生檢查分數等條件進行篩選,提供實用的餐廳資料整合與分析方法。

第四章:高品質餐廳搜尋與資料整合

餐廳詳細資料解析

在前面的章節中,我們已經建立了基本的餐廳資料抓取與地理編碼功能。本章節將探討如何解析餐廳的詳細資料,並結合多個網路服務的資料進行整合分析。

解析餐廳詳細資料頁面

inspection_detail 函式負責解析特定餐廳的詳細資料頁面。首先,它透過 BeautifulSoup 物件導航至 <body> 標籤內的首個 <h2> 標籤,以取得餐廳名稱。接著,它會定位到 <body> 內的首個 <table> 標籤,並逐一解析表格中的每一行。

def inspection_detail(business):
    soup = get_business_soup(business)
    # 解析餐廳名稱
    business.name = soup.body.h2.text.strip()
    
    # 解析詳細資料表格
    table = soup.body.table
    for row in table.find_all("tr"):
        cells = row.find_all("td")
        name = cells[0].text.strip()
        value = cells[1].text.strip()
        # 使用字典翻譯標籤名稱為 Python 屬性名稱
        setattr(business, vdh_detail_translate[name], value)

資料翻譯與屬性設定

為了將 HTML 表格中的標籤名稱轉換為更易讀的 Python 屬性名稱,我們使用了一個翻譯字典 vdh_detail_translate。這個字典將原始的標籤名稱對映到更具描述性的屬性名稱。

vdh_detail_translate = {
    'Phone Number:': 'phone_number',
    'Facility Type:': 'facility_type',
    '# of Priority Foundation Items on Last Inspection:': 'priority_foundation_items',
    '# of Priority Items on Last Inspection:': 'priority_items',
    '# of Core Items on Last Inspection:': 'core_items',
    '# of Critical Violations on Last Inspection:': 'critical_items',
    '# of Non-Critical Violations on Last Inspection:': 'non_critical_items',
}

組合多個函式實作高品質餐廳搜尋

透過組合之前定義的函式,我們可以建立一個生成器函式 choice_iter,它能夠產出一系列包含餐廳詳細資料的名稱空間物件。

def choice_iter():
    base = SimpleNamespace(address='333 Waterside Drive')
    geocode_detail(base)
    soup = get_food_list_by_name()
    for row in food_row_iter(food_table_iter(soup)):
        geocode_detail(row)
        inspection_detail(row)
        row.distance = haversine((row.latitude, row.longitude), (base.latitude, base.longitude))
        yield row

篩選高品質餐廳

對於產出的餐廳資料,我們可以套用多個篩選條件,以排除距離過遠或衛生檢查不合格的餐廳。

for business in choice_iter():
    if business.distance > 0.75: continue
    if business.priority_foundation_items > 1: continue
    if business.priority_items > 1: continue
    if business.core_items > 1: continue
    print(business)

清潔資料入口的應用

芝加哥市提供了乾淨的餐廳檢查資料入口,透過簡單的 URL 即可下載 JSON 格式的檢查資料。然而,由於資料量龐大(超過 83,000 筆檢查記錄),我們需要套用篩選條件以縮小資料範圍。

https://data.cityofchicago.org/api/views/4ijn-s7e5/rows.json?accessType=DOWNLOAD

篩選條件的應用

我們可以根據檢查日期等條件來篩選資料,以取得特定時間範圍內的檢查記錄。

此圖示詳細解說:
  1. 原始資料:代表從網路服務取得的初始資料。
  2. 地理編碼:將地址轉換為經緯度座標的過程。
  3. 餐廳詳細資料解析:解析餐廳的詳細檢查資料。
  4. 篩選高品質餐廳:根據設定的條件篩選出符合標準的餐廳。
  5. 輸出結果:最終輸出的高品質餐廳清單。

內容解密:

此圖示呈現了從原始資料到輸出結果的整個處理流程,每一步驟都至關重要,共同構成了完整的資料處理和分析過程。

資料檢索與處理:芝加哥餐廳檢查報告

本章節將介紹如何使用Socrata API取得芝加哥市的餐廳檢查報告,並將資料儲存為JSON格式以便後續處理。

取得芝加哥市餐廳檢查報告JSON資料

首先,我們需要定義一個函式來取得芝加哥市的餐廳檢查報告資料:

def get_chicago_json():
    scheme_netloc_path = "https://data.cityofchicago.org/api/views/4ijn-s7e5/rows.json"
    form = {
        "accessType": "DOWNLOAD",
        "$where": "inspection_date > '2014-01-01'",
    }
    query = urllib.parse.urlencode(form)
    with urllib.request.urlopen(scheme_netloc_path + "?" + query) as data:
        with open("chicago_data.json", "w") as output:
            output.write(data.read().decode("utf-8"))

內容解密:

  • scheme_netloc_path 變數包含了Socrata API的URL,其中 4ijn-s7e5 是資料集的內部識別碼,rows.json 指定了我們想要的資料格式。
  • form 字典中包含了查詢引數,其中 $where 子句用於過濾資料,只取得檢查日期在2014年1月1日之後的報告。
  • 使用 urllib.request.urlopen() 函式傳送請求,並將回應內容寫入 chicago_data.json 檔案中。

將JSON資料轉換為Python物件

接下來,我們需要定義一個函式來將JSON資料轉換為Python的 SimpleNamespace 物件:

def food_row_iter():
    with open("chicago_data.json", encoding="UTF-8") as data_file:
        inspections = json.load(data_file)
        headings = [item['fieldName'] for item in inspections["meta"]["view"]["columns"]]
        for row in inspections["data"]:
            data = SimpleNamespace(**dict(zip(headings, row)))
            yield data

內容解密:

  • 使用 json.load() 函式載入JSON資料。
  • inspections["meta"]["view"]["columns"] 中取得欄位名稱,並建立 headings 列表。
  • 使用 zip() 函式將欄位名稱與資料配對,並建立 SimpleNamespace 物件。

資料清理與轉換

為了方便後續處理,我們需要對資料進行一些清理與轉換:

def parse_details(business):
    business.latitude = float(business.latitude)
    business.longitude = float(business.longitude)
    if business.violations is None:
        business.details = []
    else:
        business.details = [v.strip() for v in business.violations.split("|")]
    return business

內容解密:

  • 將經緯度資料轉換為浮點數。
  • 將違規事項分割為清單。

結合不同資料來源

最後,我們可以結合上述函式,定義一個新的函式來迭代處理餐廳資料:

def choice_iter():
    base = SimpleNamespace(address="3420 W GRACE ST", city="CHICAGO", state="IL", zip="60618", latitude=41.9503, longitude=-87.7138)
    for row in food_row_iter():
        try:
            parse_details(row)
            row.distance = haversine((row.latitude, row.longitude), (base.latitude, base.longitude))
            yield row
        except TypeError:
            pass

內容解密:

  • 定義了一個基準位置 base
  • 使用 haversine() 函式計算每個餐廳與基準位置之間的距離。
  • 使用 try-except 區塊處理無效或遺失的經緯度資料。

更敏感的分析:提升間諜技能

在前幾章的間諜任務中,我們主要專注於大量資料的收集和處理。然而,總公司並不總是需要詳細的資料,有時他們需要的是總結和評估。這意味著我們需要計算資料的中心趨勢、摘要、趨勢和相關性,因此需要編寫更複雜的演算法。

資料的理解:架構和後設資料

資料是由額外的資料來描述的,我們通常稱之為後設資料。一個基本的資料可能是 6371,如果沒有一些後設資料,我們根本不知道這代表什麼。最起碼,後設資料需要包括測量單位(在這個例子中是公里)以及被測量的事物(地球的平均半徑)。

對於客觀性較低的資料,可能沒有單位,而是一系列可能的值。例如,對於餐廳來說,它可能是一個 A-B-C 評分或透過-失敗的結果。追蹤後設資料對於正確解釋實際資料至關重要。

架構問題

一組資料應該由某個基本實體的多個例項組成。在我們的例子中,這個實體是某家餐廳最近的衛生檢查結果。如果每個例項都有一個一致的屬性集合,我們就可以把這組屬性稱為這組資料的架構。

在某些情況下,資料並不一致。也許有多個架構,或者架構非常複雜,有各種選項和替代方案。如果有好的後設資料,它應該能夠解釋架構。

計算中心趨勢

我們將學習如何計算我們收集的資料的平均值、中位數和眾數。這需要使用 Python 的內建函式和可能的額外函式庫如 NumPy。

使用 Python 進行資料分析

import csv
import statistics

def read_data(filename):
    with open(filename, 'r') as file:
        reader = csv.DictReader(file)
        data = [row for row in reader]
    return data

def compute_mean(data, field):
    values = [float(row[field]) for row in data]
    return statistics.mean(values)

def compute_median(data, field):
    values = [float(row[field]) for row in data]
    return statistics.median(values)

def compute_mode(data, field):
    values = [row[field] for row in data]
    return statistics.mode(values)

# 示例用法
data = read_data('health_inspections.csv')
mean_value = compute_mean(data, 'inspection_score')
median_value = compute_median(data, 'inspection_score')
mode_value = compute_mode(data, 'risk_category')

print(f"Mean: {mean_value}, Median: {median_value}, Mode: {mode_value}")

內容解密:

  1. 我們首先匯入必要的模組,包括 csv 用於讀取 CSV 檔案,以及 statistics 用於計算統計值。
  2. read_data 函式讀取 CSV 檔案並將其內容轉換為字典列表。
  3. compute_meancompute_mediancompute_mode 函式分別計算指定欄位的平均值、中位數和眾數。
  4. 這些函式透過列表推導式提取指定欄位的值,並使用 statistics 模組中的相應函式進行計算。
  5. 示例用法展示瞭如何使用這些函式來分析衛生檢查資料。

未來方向

我們可能會被拉向包括自然語言檔案分析在內的方向。報告、演講、書籍和文章有時與基本事實和數字一樣重要。如果我們想處理文字和語言,我們需要使用自然語言工具包(NLTK)。

在下一章中,我們將透過使用更深入的統計技術來執行更敏感的資料分析。我們將計算平均值、眾數和中位數,並檢視資料項之間的相關性。這需要我們掌握更多高階的話題,例如使用 Python 產生器函式,以及設計更高階別的結構,如 Python 模組、函式庫和應用程式。