

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# AWS Glue Python 程式碼範例
<a name="aws-glue-programming-python-samples"></a>
+ [程式碼範例：加入和關聯化資料](aws-glue-programming-python-samples-legislators.md)
+ [程式碼範例：使用 ResolveChoice、Lambda 和 ApplyMapping 的資料準備](aws-glue-programming-python-samples-medicaid.md)

# 程式碼範例：加入和關聯化資料
<a name="aws-glue-programming-python-samples-legislators"></a>

本範例使用從 [http://everypolitician.org/](http://everypolitician.org/) 下載至 Amazon Simple Storage Service (Amazon S3) 的 `sample-dataset` 儲存貯體的資料集：`s3://awsglue-datasets/examples/us-legislators/all`。資料集包含美國國會議員和他們在美國眾議院和參議院內座位的 JSON 格式的資料，已針對教學用途稍作修改，並透過公有 Amazon S3 儲存貯體提供。

您可在 GitHub 網站 [AWS Glue 儲存庫範例](https://github.com/awslabs/aws-glue-samples)的 `join_and_relationalize.py` 檔案中找到此範例的原始程式碼。

本指南將利用這項資料告訴您如何執行下列動作：
+ 使用AWS Glue爬蟲程式來分類存放在公有 Amazon S3 儲存貯體中的物件，並將其結構描述儲存至 AWS Glue Data Catalog。
+ 檢查爬蟲程式所產生的資料表中繼資料和結構描述。
+ 編寫 Python 擷取、傳輸和載入 (ETL) 指令碼，使用 Data Catalog 中的中繼資料執行下列動作：
  + 將來自不同原始檔案的資料加入到單一資料表 (也就是將資料去正規化)。
  + 篩選加入的資料表，依國會議員類型放入不同的資料表。
  + 將產生的資料寫入到單獨的 Apache Parquet 檔案中，供以後分析之用。

在 上執行時偵錯 Python 或 PySpark 指令碼的偏好方法是 AWS 在 [Glue Studio AWS 上使用筆記本](https://docs.aws.amazon.com/glue/latest/ug/notebooks-chapter.html)。

## 步驟 1：在 Amazon S3 儲存貯體中網路爬取資料
<a name="aws-glue-programming-python-samples-legislators-crawling"></a>

1. 登入 AWS 管理主控台，並在 https：//[https://console.aws.amazon.com/glue/](https://console.aws.amazon.com/glue/) 開啟 AWS Glue主控台。

1. 遵循 中的步驟[設定編目程式](define-crawler.md)，建立新的爬蟲程式，可將`s3://awsglue-datasets/examples/us-legislators/all`資料集編目到 Glue Data Catalog `legislators`中名為 AWS 的資料庫。範例資料已放在這個公有 Amazon S3 儲存貯體中。

1. 執行新的爬蟲程式，接著查看 `legislators` 資料庫。

   爬蟲程式建立下列中繼資料資料表：
   + `persons_json`
   + `memberships_json`
   + `organizations_json`
   + `events_json`
   + `areas_json`
   + `countries_r_json`

   這是一個包含國會議員和其歷史的半標準化資料表集合。

## 步驟 2：新增樣板指令碼至開發端點筆記本
<a name="aws-glue-programming-python-samples-legislators-boilerplate"></a>

將以下樣板指令碼貼至開發端點以匯入您需要的 AWS Glue 程式庫，並且設定單一的 `GlueContext`：

```
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

glueContext = GlueContext(SparkContext.getOrCreate())
```

## 步驟 3：從資料目錄中的資料檢查結構描述
<a name="aws-glue-programming-python-samples-legislators-schemas"></a>

接下來，您可以從 Glue Data Catalog AWS 輕鬆建立檢查 DynamicFrame，並檢查資料的結構描述。例如，若要查看 `persons_json` 資料表的結構描述，請將下列內容新增到筆記本：

```
persons = glueContext.create_dynamic_frame.from_catalog(
             database="legislators",
             table_name="persons_json")
print "Count: ", persons.count()
persons.printSchema()
```

以下為列印呼叫的輸出：

```
Count:  1961
root
|-- family_name: string
|-- name: string
|-- links: array
|    |-- element: struct
|    |    |-- note: string
|    |    |-- url: string
|-- gender: string
|-- image: string
|-- identifiers: array
|    |-- element: struct
|    |    |-- scheme: string
|    |    |-- identifier: string
|-- other_names: array
|    |-- element: struct
|    |    |-- note: string
|    |    |-- name: string
|    |    |-- lang: string
|-- sort_name: string
|-- images: array
|    |-- element: struct
|    |    |-- url: string
|-- given_name: string
|-- birth_date: string
|-- id: string
|-- contact_details: array
|    |-- element: struct
|    |    |-- type: string
|    |    |-- value: string
|-- death_date: string
```

資料表中的每個人都是美國國會的成員。

若要檢視 `memberships_json` 資料表的結構描述，請輸入如下命令：

```
memberships = glueContext.create_dynamic_frame.from_catalog(
                 database="legislators",
                 table_name="memberships_json")
print "Count: ", memberships.count()
memberships.printSchema()
```

其輸出如下：

```
Count:  10439
root
|-- area_id: string
|-- on_behalf_of_id: string
|-- organization_id: string
|-- role: string
|-- person_id: string
|-- legislative_period_id: string
|-- start_date: string
|-- end_date: string
```

`organizations` 為政黨和參議院與眾議院這兩個議會殿堂。若要檢視 `organizations_json` 資料表的結構描述，請輸入如下命令：

```
orgs = glueContext.create_dynamic_frame.from_catalog(
           database="legislators",
           table_name="organizations_json")
print "Count: ", orgs.count()
orgs.printSchema()
```

其輸出如下：

```
Count:  13
root
|-- classification: string
|-- links: array
|    |-- element: struct
|    |    |-- note: string
|    |    |-- url: string
|-- image: string
|-- identifiers: array
|    |-- element: struct
|    |    |-- scheme: string
|    |    |-- identifier: string
|-- other_names: array
|    |-- element: struct
|    |    |-- lang: string
|    |    |-- note: string
|    |    |-- name: string
|-- id: string
|-- name: string
|-- seats: int
|-- type: string
```

## 步驟 4：篩選資料
<a name="aws-glue-programming-python-samples-legislators-filtering"></a>

接著，保留需要的欄位，將 `id` 重新命名為 `org_id`。資料集很小，可以從整體來檢視。

`toDF()` 會將 `DynamicFrame` 轉換為 Apache Spark `DataFrame`，因此您可套用 Apache Spark SQL 中現有的轉換：

```
orgs = orgs.drop_fields(['other_names',
                        'identifiers']).rename_field(
                            'id', 'org_id').rename_field(
                               'name', 'org_name')
orgs.toDF().show()
```

以下將顯示輸出：

```
+--------------+--------------------+--------------------+--------------------+-----+-----------+--------------------+
|classification|              org_id|            org_name|               links|seats|       type|               image|
+--------------+--------------------+--------------------+--------------------+-----+-----------+--------------------+
|         party|            party/al|                  AL|                null| null|       null|                null|
|         party|      party/democrat|            Democrat|[[website,http://...| null|       null|https://upload.wi...|
|         party|party/democrat-li...|    Democrat-Liberal|[[website,http://...| null|       null|                null|
|   legislature|d56acebe-8fdc-47b...|House of Represen...|                null|  435|lower house|                null|
|         party|   party/independent|         Independent|                null| null|       null|                null|
|         party|party/new_progres...|     New Progressive|[[website,http://...| null|       null|https://upload.wi...|
|         party|party/popular_dem...|    Popular Democrat|[[website,http://...| null|       null|                null|
|         party|    party/republican|          Republican|[[website,http://...| null|       null|https://upload.wi...|
|         party|party/republican-...|Republican-Conser...|[[website,http://...| null|       null|                null|
|         party|      party/democrat|            Democrat|[[website,http://...| null|       null|https://upload.wi...|
|         party|   party/independent|         Independent|                null| null|       null|                null|
|         party|    party/republican|          Republican|[[website,http://...| null|       null|https://upload.wi...|
|   legislature|8fa6c3d2-71dc-478...|              Senate|                null|  100|upper house|                null|
+--------------+--------------------+--------------------+--------------------+-----+-----------+--------------------+
```

輸入以下以檢視出現在 `memberships` 的 `organizations`：

```
memberships.select_fields(['organization_id']).toDF().distinct().show()
```

以下將顯示輸出：

```
+--------------------+
|     organization_id|
+--------------------+
|d56acebe-8fdc-47b...|
|8fa6c3d2-71dc-478...|
+--------------------+
```

## 步驟 5：全部整合為一
<a name="aws-glue-programming-python-samples-legislators-joining"></a>

現在，使用 AWS Glue 加入這些關聯式表格，並建立一份關於國會議員 `memberships` 及其對應的 `organizations` 的完整歷史記錄資料表。

1. 首先，加入 `persons` 和 `memberships` 的 `id` 和 `person_id`。

1. 接著，將結果加入到 `orgs` 的 `org_id` 和 `organization_id`。

1. 然後，捨棄冗餘欄位 `person_id` 和 `org_id`。

您可以在同一 (延伸) 指令碼行執行所有這些操作：

```
l_history = Join.apply(orgs,
                       Join.apply(persons, memberships, 'id', 'person_id'),
                       'org_id', 'organization_id').drop_fields(['person_id', 'org_id'])
print "Count: ", l_history.count()
l_history.printSchema()
```

其輸出如下：

```
Count:  10439
root
|-- role: string
|-- seats: int
|-- org_name: string
|-- links: array
|    |-- element: struct
|    |    |-- note: string
|    |    |-- url: string
|-- type: string
|-- sort_name: string
|-- area_id: string
|-- images: array
|    |-- element: struct
|    |    |-- url: string
|-- on_behalf_of_id: string
|-- other_names: array
|    |-- element: struct
|    |    |-- note: string
|    |    |-- name: string
|    |    |-- lang: string
|-- contact_details: array
|    |-- element: struct
|    |    |-- type: string
|    |    |-- value: string
|-- name: string
|-- birth_date: string
|-- organization_id: string
|-- gender: string
|-- classification: string
|-- death_date: string
|-- legislative_period_id: string
|-- identifiers: array
|    |-- element: struct
|    |    |-- scheme: string
|    |    |-- identifier: string
|-- image: string
|-- given_name: string
|-- family_name: string
|-- id: string
|-- start_date: string
|-- end_date: string
```

您現在取得最終的資料表，可用於分析。您可以用精巧、有效率的格式編寫，以用於分析 (也就是 Parquet)，在 AWS Glue、Amazon Athena 或 Amazon Redshift Spectrum 上執行 SQL。

以下呼叫將資料表編寫到多個檔案，在稍後執行分析時支援快速平行讀取：

```
glueContext.write_dynamic_frame.from_options(frame = l_history,
          connection_type = "s3",
          connection_options = {"path": "s3://glue-sample-target/output-dir/legislator_history"},
          format = "parquet")
```

若要將所有歷史記錄資料合併成單一檔案，您必須將其轉換為資料框架、分割並編寫：

```
s_history = l_history.toDF().repartition(1)
s_history.write.parquet('s3://glue-sample-target/output-dir/legislator_single')
```

或者，如果您希望將其分為參議院和眾議院：

```
l_history.toDF().write.parquet('s3://glue-sample-target/output-dir/legislator_part',
                               partitionBy=['org_name'])
```

## 步驟 6：轉換關聯式資料庫的資料
<a name="aws-glue-programming-python-samples-legislators-writing"></a>

AWS Glue 可讓您輕鬆地將資料寫入關聯式資料庫，例如 Amazon Redshift，即使是半結構化資料。其提供轉換 `relationalize`，可將 `DynamicFrames` 扁平化，無論框架中的物件多複雜。

使用本範例中的 `l_history` `DynamicFrame`，以根資料表 (`hist_root`) 的名稱和暫時任務路徑傳送至 `relationalize`。將傳回 `DynamicFrameCollection`。然後，您可以將 `DynamicFrames` 的名稱列在該集合中：

```
dfc = l_history.relationalize("hist_root", "s3://glue-sample-target/temp-dir/")
dfc.keys()
```

以下為 `keys` 呼叫的輸出：

```
[u'hist_root', u'hist_root_contact_details', u'hist_root_links',
 u'hist_root_other_names', u'hist_root_images', u'hist_root_identifiers']
```

`Relationalize` 將歷史記錄資料表分成六個新資料表：根資料表包含在 `DynamicFrame` 中的各物件記錄，和陣列的輔助資料表。關聯式資料庫中的陣列處理通常為次最佳化，尤其在這些陣列變得龐大時。將陣列分成不同的資料表，可加快查詢速度。

接著，檢查 `contact_details` 以查看分隔：

```
l_history.select_fields('contact_details').printSchema()
dfc.select('hist_root_contact_details').toDF().where("id = 10 or id = 75").orderBy(['id','index']).show()
```

以下為 `show` 呼叫的輸出：

```
root
|-- contact_details: array
|    |-- element: struct
|    |    |-- type: string
|    |    |-- value: string
+---+-----+------------------------+-------------------------+
| id|index|contact_details.val.type|contact_details.val.value|
+---+-----+------------------------+-------------------------+
| 10|    0|                     fax|                         |
| 10|    1|                        |             202-225-1314|
| 10|    2|                   phone|                         |
| 10|    3|                        |             202-225-3772|
| 10|    4|                 twitter|                         |
| 10|    5|                        |          MikeRossUpdates|
| 75|    0|                     fax|                         |
| 75|    1|                        |             202-225-7856|
| 75|    2|                   phone|                         |
| 75|    3|                        |             202-225-2711|
| 75|    4|                 twitter|                         |
| 75|    5|                        |                SenCapito|
+---+-----+------------------------+-------------------------+
```

`contact_details` 欄位為原始 `DynamicFrame` 中結構的陣列。這些陣列的每個元素都是輔助資料表中的單獨資料列，以 `index` 編製索引。此處的 `id` 是 `hist_root` 資料表的外部金鑰，金鑰為 `contact_details`：

```
dfc.select('hist_root').toDF().where(
    "contact_details = 10 or contact_details = 75").select(
       ['id', 'given_name', 'family_name', 'contact_details']).show()
```

以下為其輸出：

```
+--------------------+----------+-----------+---------------+
|                  id|given_name|family_name|contact_details|
+--------------------+----------+-----------+---------------+
|f4fc30ee-7b42-432...|      Mike|       Ross|             10|
|e3c60f34-7d1b-4c0...|   Shelley|     Capito|             75|
+--------------------+----------+-----------+---------------+
```

請注意，這些命令將使用 `toDF()`，然後是 `where` 表達式，來篩選您想要查看的資料列。

因此，加入 `hist_root` 資料表與輔助資料表可執行下列動作：
+ 無需陣列支援便能將資料載入到資料庫。
+ 使用 SQL 查詢陣列中的每個個別項目。

使用 AWS Glue 連線以安全存放和存取您的 Amazon Redshift 憑證。有關如何建立自己的連線的詳細資訊，請參閱 [連線至資料](glue-connections.md)。

您現已準備好透過一次循環一個 `DynamicFrames` 來將資料寫入連接器：

```
for df_name in dfc.keys():
  m_df = dfc.select(df_name)
  print "Writing to table: ", df_name
  glueContext.write_dynamic_frame.from_jdbc_conf(frame = m_df, connection settings here)
```

您的連接器設定將因您的關聯式資料庫類型而異：
+ 如需有關寫入 Amazon Redshift 的指示，請參閱[Redshift 連線](aws-glue-programming-etl-connect-redshift-home.md)。
+ 若為其他資料庫，請參閱 [AWS Glue for Spark 中 ETL 的連線類型和選項](aws-glue-programming-etl-connect.md)。

## 結論
<a name="aws-glue-programming-python-samples-legislators-conclusion"></a>

整體而言，AWS Glue 極具彈性。它讓您用幾行程式碼便能完成通常要好幾天才能完成撰寫的任務。您可在 GitHub [AWS Glue 範例](https://github.com/awslabs/aws-glue-samples)的 Python 檔案 `join_and_relationalize.py` 中找到完整從來源到目標的 ETL 指令碼。

# 程式碼範例：使用 ResolveChoice、Lambda 和 ApplyMapping 的資料準備
<a name="aws-glue-programming-python-samples-medicaid"></a>

此範例使用的資料集，包含從兩個 [Data.CMS.gov](https://data.cms.gov) 資料集下載的美國聯邦醫療保險 (Medicare) 供應商付款資料：「住院患者預期付款系統供應商前 100 大診斷相關群組摘要 - FY2011」和「住院患者費用資料 FY 2011」。下載資料之後，我們修改了資料集，以在檔案結尾處引入幾個錯誤記錄。上述經修改的檔案位於公有 Amazon S3 儲存貯體，位置在 `s3://awsglue-datasets/examples/medicare/Medicare_Hospital_Provider.csv`。

您可以在 `data_cleaning_and_lambda.py`[ examplesAWS Glue GitHub 儲存庫的 ](https://github.com/awslabs/aws-glue-samples) 檔案中找到此範例的原始碼。

在 上執行時偵錯 Python 或 PySpark 指令碼的偏好方法是 AWS 在 [Glue Studio AWS 上使用筆記本](https://docs.aws.amazon.com/glue/latest/ug/notebooks-chapter.html)。

## 步驟 1：在 Amazon S3 儲存貯體中網路爬取資料
<a name="aws-glue-programming-python-samples-medicaid-crawling"></a>

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/glue/](https://console.aws.amazon.com/glue/) 開啟 AWS Glue主控台。

1. 遵循 中所述的程序[設定編目程式](define-crawler.md)，建立新的爬蟲程式，該爬蟲程式可以編目`s3://awsglue-datasets/examples/medicare/Medicare_Hospital_Provider.csv`檔案，並將產生的中繼資料放入 Glue Data Catalog `payments`中名為 AWS 的資料庫。

1. 執行新的爬蟲程式，接著查看 `payments` 資料庫。在讀取檔案開頭，確定其格式和分隔符號後，爬蟲程式應會於資料庫建立一個命名為 `medicare` 的中繼資料資料表。

   新 `medicare` 資料表的結構描述如下：

   ```
   Column  name                            Data type
   ==================================================
   drg definition                             string
   provider id                                bigint
   provider name                              string
   provider street address                    string
   provider city                              string
   provider state                             string
   provider zip code                          bigint
   hospital referral region description       string
   total discharges                           bigint
   average covered charges                    string
   average total payments                     string
   average medicare payments                  string
   ```

## 步驟 2：新增樣板指令碼至開發端點筆記本
<a name="aws-glue-programming-python-samples-medicaid-boilerplate"></a>

將以下樣板指令碼貼至開發端點以匯入您需要的 AWS Glue 程式庫，並且設定單一的 `GlueContext`：

```
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

glueContext = GlueContext(SparkContext.getOrCreate())
```

## 步驟 3：比較不同的結構描述剖析
<a name="aws-glue-programming-python-samples-medicaid-schemas"></a>

接著，您可以查看 Apache Spark `DataFrame` 辨識出的結構描述是否跟 AWS Glue 爬蟲程式所記錄的相同。執行此程式碼：

```
medicare = spark.read.format(
   "com.databricks.spark.csv").option(
   "header", "true").option(
   "inferSchema", "true").load(
   's3://awsglue-datasets/examples/medicare/Medicare_Hospital_Provider.csv')
medicare.printSchema()
```

以下為 `printSchema` 呼叫的輸出：

```
root
 |-- DRG Definition: string (nullable = true)
 |-- Provider Id: string (nullable = true)
 |-- Provider Name: string (nullable = true)
 |-- Provider Street Address: string (nullable = true)
 |-- Provider City: string (nullable = true)
 |-- Provider State: string (nullable = true)
 |-- Provider Zip Code: integer (nullable = true)
 |-- Hospital Referral Region Description: string (nullable = true)
 |--  Total Discharges : integer (nullable = true)
 |--  Average Covered Charges : string (nullable = true)
 |--  Average Total Payments : string (nullable = true)
 |-- Average Medicare Payments: string (nullable = true)
```

接著，查看 AWS Glue `DynamicFrame` 產生的結構描述：

```
medicare_dynamicframe = glueContext.create_dynamic_frame.from_catalog(
       database = "payments",
       table_name = "medicare")
medicare_dynamicframe.printSchema()
```

`printSchema` 的輸出如下：

```
root
 |-- drg definition: string
 |-- provider id: choice
 |    |-- long
 |    |-- string
 |-- provider name: string
 |-- provider street address: string
 |-- provider city: string
 |-- provider state: string
 |-- provider zip code: long
 |-- hospital referral region description: string
 |-- total discharges: long
 |-- average covered charges: string
 |-- average total payments: string
 |-- average medicare payments: string
```

在 `DynamicFrame` 產生的結構描述中，`provider id` 可以是 `long` 或 `string` 類型。`DataFrame` 結構描述將 `Provider Id` 列為 `string` 類型， Data Catalog 則是將 `provider id` 列為 `bigint` 類型。

何者正確？ 在檔案的最後有兩筆記錄 (總共 160,000 筆記錄)，且該欄位中有 `string` 值。這些就是之前引入以示範產生問題的錯誤記錄。

為了解決這種問題，AWS Glue`DynamicFrame` 採用 *choice (選擇)* 類型的概念。在此例中，`DynamicFrame` 展示了 `long` 和 `string` 值都會在該欄出現。AWS Glue 爬蟲程式遺漏了 `string` 值，原因是只考量到資料的前 2 MB。Apache Spark `DataFrame` 會考量整個資料集，但被強制將最普遍的類型指派給該欄位，亦即 `string`。事實上，Spark 在遇到複雜類型或不熟悉的變化時，通常都會採取最普遍的作法。

要查詢 `provider id` 欄，請先解決選擇類型。您可以使用 `DynamicFrame` 中的 `resolveChoice` 轉換方法，藉由以下的 `cast:long` 選項將 `string` 值轉換為 `long` 值。

```
medicare_res = medicare_dynamicframe.resolveChoice(specs = [('provider id','cast:long')])
medicare_res.printSchema()
```

`printSchema` 輸出就會是：

```
root
 |-- drg definition: string
 |-- provider id: long
 |-- provider name: string
 |-- provider street address: string
 |-- provider city: string
 |-- provider state: string
 |-- provider zip code: long
 |-- hospital referral region description: string
 |-- total discharges: long
 |-- average covered charges: string
 |-- average total payments: string
 |-- average medicare payments: string
```

如果有無法轉換的 `string` 值，AWS Glue 會插入 `null`。

另一個選項是將選擇類型轉換為 `struct`，這會保留兩種類型的值。

接著，查看異常的資料列。

```
medicare_res.toDF().where("'provider id' is NULL").show()
```

您會見到以下情況：

```
+--------------------+-----------+---------------+-----------------------+-------------+--------------+-----------------+------------------------------------+----------------+-----------------------+----------------------+-------------------------+
|      drg definition|provider id|  provider name|provider street address|provider city|provider state|provider zip code|hospital referral region description|total discharges|average covered charges|average total payments|average medicare payments|
+--------------------+-----------+---------------+-----------------------+-------------+--------------+-----------------+------------------------------------+----------------+-----------------------+----------------------+-------------------------+
|948 - SIGNS & SYM...|       null|            INC|       1050 DIVISION ST|      MAUSTON|            WI|            53948|                        WI - Madison|              12|              $11961.41|              $4619.00|                 $3775.33|
|948 - SIGNS & SYM...|       null| INC- ST JOSEPH|     5000 W CHAMBERS ST|    MILWAUKEE|            WI|            53210|                      WI - Milwaukee|              14|              $10514.28|              $5562.50|                 $4522.78|
+--------------------+-----------+---------------+-----------------------+-------------+--------------+-----------------+------------------------------------+----------------+-----------------------+----------------------+-------------------------+
```

現在移除兩筆不正確的記錄，如下所示：

```
medicare_dataframe = medicare_res.toDF()
medicare_dataframe = medicare_dataframe.where("'provider id' is NOT NULL")
```

## 步驟 4：映射資料並使用 Apache Spark Lambda 函數
<a name="aws-glue-programming-python-samples-medicaid-lambda-mapping"></a>

AWS Glue 尚未直接支援 Lambda 函式，亦即使用者定義的函式。但是您隨時可以從 Apache Spark `DataFrame` 來回轉換 `DynamicFrame`，以利用除了 `DynamicFrames` 特殊功能之外的 Spark 功能。

接著，將付款資訊轉為數字，讓 Amazon Redshift 或 Amazon Athena 等分析引擎可以更快處理。

```
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType

chop_f = udf(lambda x: x[1:], StringType())
medicare_dataframe = medicare_dataframe.withColumn(
        "ACC", chop_f(
            medicare_dataframe["average covered charges"])).withColumn(
                "ATP", chop_f(
                    medicare_dataframe["average total payments"])).withColumn(
                        "AMP", chop_f(
                            medicare_dataframe["average medicare payments"]))
medicare_dataframe.select(['ACC', 'ATP', 'AMP']).show()
```

`show` 呼叫的輸出如下：

```
+--------+-------+-------+
|     ACC|    ATP|    AMP|
+--------+-------+-------+
|32963.07|5777.24|4763.73|
|15131.85|5787.57|4976.71|
|37560.37|5434.95|4453.79|
|13998.28|5417.56|4129.16|
|31633.27|5658.33|4851.44|
|16920.79|6653.80|5374.14|
|11977.13|5834.74|4761.41|
|35841.09|8031.12|5858.50|
|28523.39|6113.38|5228.40|
|75233.38|5541.05|4386.94|
|67327.92|5461.57|4493.57|
|39607.28|5356.28|4408.20|
|22862.23|5374.65|4186.02|
|31110.85|5366.23|4376.23|
|25411.33|5282.93|4383.73|
| 9234.51|5676.55|4509.11|
|15895.85|5930.11|3972.85|
|19721.16|6192.54|5179.38|
|10710.88|4968.00|3898.88|
|51343.75|5996.00|4962.45|
+--------+-------+-------+
only showing top 20 rows
```

資料仍然全是字串。我們可以使用強大的 `apply_mapping` 轉換方法來捨棄、重新命名、轉換、巢套資料，讓其他資料程式設計語言和系統能夠輕易存取：

```
from awsglue.dynamicframe import DynamicFrame
medicare_tmp_dyf = DynamicFrame.fromDF(medicare_dataframe, glueContext, "nested")
medicare_nest_dyf = medicare_tmp_dyf.apply_mapping([('drg definition', 'string', 'drg', 'string'),
                 ('provider id', 'long', 'provider.id', 'long'),
                 ('provider name', 'string', 'provider.name', 'string'),
                 ('provider city', 'string', 'provider.city', 'string'),
                 ('provider state', 'string', 'provider.state', 'string'),
                 ('provider zip code', 'long', 'provider.zip', 'long'),
                 ('hospital referral region description', 'string','rr', 'string'),
                 ('ACC', 'string', 'charges.covered', 'double'),
                 ('ATP', 'string', 'charges.total_pay', 'double'),
                 ('AMP', 'string', 'charges.medicare_pay', 'double')])
medicare_nest_dyf.printSchema()
```

`printSchema` 輸出如下：

```
root
 |-- drg: string
 |-- provider: struct
 |    |-- id: long
 |    |-- name: string
 |    |-- city: string
 |    |-- state: string
 |    |-- zip: long
 |-- rr: string
 |-- charges: struct
 |    |-- covered: double
 |    |-- total_pay: double
 |    |-- medicare_pay: double
```

將資料轉回 Spark `DataFrame` 後，您就可以顯示其樣貌：

```
medicare_nest_dyf.toDF().show()
```

其輸出如下：

```
+--------------------+--------------------+---------------+--------------------+
|                 drg|            provider|             rr|             charges|
+--------------------+--------------------+---------------+--------------------+
|039 - EXTRACRANIA...|[10001,SOUTHEAST ...|    AL - Dothan|[32963.07,5777.24...|
|039 - EXTRACRANIA...|[10005,MARSHALL M...|AL - Birmingham|[15131.85,5787.57...|
|039 - EXTRACRANIA...|[10006,ELIZA COFF...|AL - Birmingham|[37560.37,5434.95...|
|039 - EXTRACRANIA...|[10011,ST VINCENT...|AL - Birmingham|[13998.28,5417.56...|
|039 - EXTRACRANIA...|[10016,SHELBY BAP...|AL - Birmingham|[31633.27,5658.33...|
|039 - EXTRACRANIA...|[10023,BAPTIST ME...|AL - Montgomery|[16920.79,6653.8,...|
|039 - EXTRACRANIA...|[10029,EAST ALABA...|AL - Birmingham|[11977.13,5834.74...|
|039 - EXTRACRANIA...|[10033,UNIVERSITY...|AL - Birmingham|[35841.09,8031.12...|
|039 - EXTRACRANIA...|[10039,HUNTSVILLE...|AL - Huntsville|[28523.39,6113.38...|
|039 - EXTRACRANIA...|[10040,GADSDEN RE...|AL - Birmingham|[75233.38,5541.05...|
|039 - EXTRACRANIA...|[10046,RIVERVIEW ...|AL - Birmingham|[67327.92,5461.57...|
|039 - EXTRACRANIA...|[10055,FLOWERS HO...|    AL - Dothan|[39607.28,5356.28...|
|039 - EXTRACRANIA...|[10056,ST VINCENT...|AL - Birmingham|[22862.23,5374.65...|
|039 - EXTRACRANIA...|[10078,NORTHEAST ...|AL - Birmingham|[31110.85,5366.23...|
|039 - EXTRACRANIA...|[10083,SOUTH BALD...|    AL - Mobile|[25411.33,5282.93...|
|039 - EXTRACRANIA...|[10085,DECATUR GE...|AL - Huntsville|[9234.51,5676.55,...|
|039 - EXTRACRANIA...|[10090,PROVIDENCE...|    AL - Mobile|[15895.85,5930.11...|
|039 - EXTRACRANIA...|[10092,D C H REGI...|AL - Tuscaloosa|[19721.16,6192.54...|
|039 - EXTRACRANIA...|[10100,THOMAS HOS...|    AL - Mobile|[10710.88,4968.0,...|
|039 - EXTRACRANIA...|[10103,BAPTIST ME...|AL - Birmingham|[51343.75,5996.0,...|
+--------------------+--------------------+---------------+--------------------+
only showing top 20 rows
```

## 步驟 5：寫入資料至 Apache Parquet
<a name="aws-glue-programming-python-samples-medicaid-writing"></a>

AWS Glue 可讓您輕鬆以關聯式資料庫能有效取用的格式 (例如 Apache Parquet) 撰寫資料：

```
glueContext.write_dynamic_frame.from_options(
       frame = medicare_nest_dyf,
       connection_type = "s3",
       connection_options = {"path": "s3://glue-sample-target/output-dir/medicare_parquet"},
       format = "parquet")
```