Skip to content

Instantly share code, notes, and snippets.

@dannymorris
Created May 18, 2021 18:36
Show Gist options
  • Save dannymorris/dd19a7d6a7132e7d7470df13d0da75bd to your computer and use it in GitHub Desktop.
Save dannymorris/dd19a7d6a7132e7d7470df13d0da75bd to your computer and use it in GitHub Desktop.
EDM-SparkML-Clustering.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "EDM-SparkML-Clustering.ipynb",
"provenance": [],
"toc_visible": true,
"authorship_tag": "ABX9TyPd2LB0mK3Ru2UrcbGM/t/J",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/dannymorris/dd19a7d6a7132e7d7470df13d0da75bd/edm-sparkml-clustering.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Lhme3LFAYEj_"
},
"source": [
"## Overview\n",
"\n",
"This notebook implements K-Means clustering using PySpark and MLlib, the Spark Machine Learning API. \n",
"\n",
"The steps taken in this notebook include the following:\n",
"\n",
"- Install Spark and PySpark\n",
"- Create a SparkSession\n",
"- Read a CSV file from the web and load into Spark\n",
"- Select features for clustering\n",
"- Assemble an [ML Pipeline](https://spark.apache.org/docs/latest/ml-pipeline.html) that defines the clustering workflow, including:\n",
" - Assemble the features into a vector\n",
" - Scale the features to have mean=0 and sd=1\n",
" - Initialize the K-Means algorithm\n",
"- Fit the ML Pipeline to the training data\n",
"- Generate predictions (i.e. cluster labels) for the training data\n",
"- Compute Silhouette score to evalute the fit\n",
"- Compute the cluster centers\n",
"- Compute cluster frequencies\n",
"- Define a reusable function for testing multiple values for *K*"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Uaiz0fDEL1x-"
},
"source": [
"# Install PySpark"
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "-iOTzB1gd7LE",
"outputId": "0857f0b0-e379-472d-921a-c8ede2ec8885"
},
"source": [
"!pip install pyspark"
],
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"text": [
"Collecting pyspark\n",
"\u001b[?25l Downloading https://files.pythonhosted.org/packages/45/b0/9d6860891ab14a39d4bddf80ba26ce51c2f9dc4805e5c6978ac0472c120a/pyspark-3.1.1.tar.gz (212.3MB)\n",
"\u001b[K |████████████████████████████████| 212.3MB 69kB/s \n",
"\u001b[?25hCollecting py4j==0.10.9\n",
"\u001b[?25l Downloading https://files.pythonhosted.org/packages/9e/b6/6a4fb90cd235dc8e265a6a2067f2a2c99f0d91787f06aca4bcf7c23f3f80/py4j-0.10.9-py2.py3-none-any.whl (198kB)\n",
"\u001b[K |████████████████████████████████| 204kB 17.8MB/s \n",
"\u001b[?25hBuilding wheels for collected packages: pyspark\n",
" Building wheel for pyspark (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
" Created wheel for pyspark: filename=pyspark-3.1.1-py2.py3-none-any.whl size=212767604 sha256=942148c17b0652ab8a0c9ad8a5a592fa09447a2993cad8f35ee22e3026f761d2\n",
" Stored in directory: /root/.cache/pip/wheels/0b/90/c0/01de724414ef122bd05f056541fb6a0ecf47c7ca655f8b3c0f\n",
"Successfully built pyspark\n",
"Installing collected packages: py4j, pyspark\n",
"Successfully installed py4j-0.10.9 pyspark-3.1.1\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "oeBvkC9UMyok"
},
"source": [
"## Load packages and modules"
]
},
{
"cell_type": "code",
"metadata": {
"id": "mLUKdc60jkBz"
},
"source": [
"from pyspark.sql import SparkSession\n",
"from pyspark import SparkFiles\n",
"from pyspark.ml import Pipeline\n",
"from pyspark.ml.feature import VectorAssembler\n",
"from pyspark.ml.feature import StandardScaler\n",
"from pyspark.ml.clustering import KMeans\n",
"from pyspark.ml.evaluation import ClusteringEvaluator\n",
"import pandas as pd"
],
"execution_count": 12,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "FMS5JD-kPQsI"
},
"source": [
"## Create SparkSession\n",
"\n",
"`SparkSession` provides a single point of entry to interact with underlying Spark functionality and allows programming Spark with DataFrame and Dataset APIs."
]
},
{
"cell_type": "code",
"metadata": {
"id": "9Gz1Z6ici7KB"
},
"source": [
"spark = SparkSession. \\\n",
" builder. \\\n",
" appName(\"MLlib Clustering\"). \\\n",
" getOrCreate()"
],
"execution_count": 7,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "lg_Hnxw8Pgu1"
},
"source": [
"## Read data\n",
"\n",
"In this example, the data resides on the internet in CSV format. Use `addFile` to load the file into Spark and read as a DataFrame using `read.csv`."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 222
},
"id": "M3uGR6qaieqF",
"outputId": "4cbc48a0-dc58-4ef3-dcd3-daeca27440b6"
},
"source": [
"url = \"https://gist.githubusercontent.com/dannymorris/1bd95ddda1cfe7fd518e5cda01f4ac03/raw/295636f511f0afbd544aece7cbfe771edc01182c/county_data.csv\"\n",
"\n",
"# upload file to Spark\n",
"spark.sparkContext.addFile(url)\n",
"\n",
"# Read file\n",
"df = spark.read.csv(\"file://\"+SparkFiles.get(\"county_data.csv\"), header=True, inferSchema= True)\n",
"\n",
"df.limit(5).toPandas().head()"
],
"execution_count": 74,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>fips</th>\n",
" <th>state</th>\n",
" <th>name</th>\n",
" <th>party_winner</th>\n",
" <th>trump_pct</th>\n",
" <th>margin</th>\n",
" <th>POPULATION_Total</th>\n",
" <th>AGE_18_29</th>\n",
" <th>AGE_30_44</th>\n",
" <th>AGE_45_59</th>\n",
" <th>AGE_60_Plus</th>\n",
" <th>AGE_18_Plus</th>\n",
" <th>RACE_Total__Asian_alone</th>\n",
" <th>RACE_Total__Black_or_African_American_alone</th>\n",
" <th>RACE_Total__Hispanic_or_Latino</th>\n",
" <th>RACE_Total__White_alone</th>\n",
" <th>GINI_Gini_Index</th>\n",
" <th>INCOME_PER_CAPITA_INCOME_IN_THE_PAST_12_MONTHS__IN_2018_INFLATION_ADJUSTED_DOLLARS_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__ASIAN_ALONE_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__BLACK_OR_AFRICAN_AMERICAN_ALONE_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__HISPANIC_OR_LATINO_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__WHITE_ALONE_</th>\n",
" <th>UNEMPLOY_Total_16_YEARS_AND_OVER</th>\n",
" <th>EDU_ATTAIN_Total__Bachelor_s_degree_or_higher</th>\n",
" <th>EDU_ATTAIN_Total__High_school_graduate__includes_equivalency_</th>\n",
" <th>EDU_ATTAIN_Total__Less_than_high_school_diploma</th>\n",
" <th>EDU_ATTAIN_Total__Some_college_or_associate_s_degree</th>\n",
" <th>INDUSTRY_Total__Agriculture__forestry__fishing_and_hunting__and_mining__Agriculture__forestry__fishing_and_hunting</th>\n",
" <th>INDUSTRY_Total__Agriculture__forestry__fishing_and_hunting__and_mining__Mining__quarrying__and_oil_and_gas_extraction</th>\n",
" <th>INDUSTRY_Total__Arts__entertainment__and_recreation__and_accommodation_and_food_services__Accommodation_and_food_services</th>\n",
" <th>INDUSTRY_Total__Arts__entertainment__and_recreation__and_accommodation_and_food_services__Arts__entertainment__and_recreation</th>\n",
" <th>INDUSTRY_Total__Construction</th>\n",
" <th>INDUSTRY_Total__Educational_services__and_health_care_and_social_assistance__Educational_services</th>\n",
" <th>INDUSTRY_Total__Educational_services__and_health_care_and_social_assistance__Health_care_and_social_assistance</th>\n",
" <th>INDUSTRY_Total__Information</th>\n",
" <th>INDUSTRY_Total__Manufacturing</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Administrative_and_support_and_waste_management_services</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Management_of_companies_and_enterprises</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Professional__scientific__and_technical_services</th>\n",
" <th>INDUSTRY_Total__Public_administration</th>\n",
" <th>INDUSTRY_Total__Retail_trade</th>\n",
" <th>CITIZEN_Estimate__Total__Not_a_U_S__citizen</th>\n",
" <th>CITIZEN_Estimate__Total__U_S__citizen_by_naturalization</th>\n",
" <th>CITIZEN_Estimate__Total__U_S__citizen__born_in_the_United_States</th>\n",
" <th>HEALTH_INSURANCE_No_health_insurance_coverage</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_direct_purchase_health_insurance_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_employer_based_health_insurance_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_Medicaid_means_tested_public_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_Medicare_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_TRICARE_military_health_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_VA_Health_Care_only</th>\n",
" <th>VETERANS_Estimate__Total__Veteran</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>48001</td>\n",
" <td>texas</td>\n",
" <td>Anderson</td>\n",
" <td>republican</td>\n",
" <td>0.786119</td>\n",
" <td>0.580355</td>\n",
" <td>57863</td>\n",
" <td>0.192527</td>\n",
" <td>0.308352</td>\n",
" <td>0.246227</td>\n",
" <td>0.252894</td>\n",
" <td>46648</td>\n",
" <td>0.005513</td>\n",
" <td>0.209823</td>\n",
" <td>0.175276</td>\n",
" <td>0.735340</td>\n",
" <td>0.4225</td>\n",
" <td>16868</td>\n",
" <td>0.0</td>\n",
" <td>0.004287</td>\n",
" <td>0.002272</td>\n",
" <td>0.007760</td>\n",
" <td>0.014320</td>\n",
" <td>0.105299</td>\n",
" <td>0.359608</td>\n",
" <td>0.232636</td>\n",
" <td>0.308481</td>\n",
" <td>0.007846</td>\n",
" <td>0.028490</td>\n",
" <td>0.022359</td>\n",
" <td>0.002165</td>\n",
" <td>0.022380</td>\n",
" <td>0.029905</td>\n",
" <td>0.057473</td>\n",
" <td>0.002037</td>\n",
" <td>0.025424</td>\n",
" <td>0.021373</td>\n",
" <td>0.000000</td>\n",
" <td>0.010912</td>\n",
" <td>0.038608</td>\n",
" <td>0.077860</td>\n",
" <td>0.041546</td>\n",
" <td>0.020946</td>\n",
" <td>0.928400</td>\n",
" <td>0.114324</td>\n",
" <td>0.028040</td>\n",
" <td>0.306080</td>\n",
" <td>0.028040</td>\n",
" <td>0.065833</td>\n",
" <td>0.005638</td>\n",
" <td>0.006088</td>\n",
" <td>0.086006</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>48003</td>\n",
" <td>texas</td>\n",
" <td>Andrews</td>\n",
" <td>republican</td>\n",
" <td>0.843084</td>\n",
" <td>0.698107</td>\n",
" <td>17818</td>\n",
" <td>0.245792</td>\n",
" <td>0.287747</td>\n",
" <td>0.246605</td>\n",
" <td>0.219855</td>\n",
" <td>12299</td>\n",
" <td>0.003536</td>\n",
" <td>0.019811</td>\n",
" <td>0.560052</td>\n",
" <td>0.924009</td>\n",
" <td>0.4506</td>\n",
" <td>31190</td>\n",
" <td>0.0</td>\n",
" <td>0.012196</td>\n",
" <td>0.015611</td>\n",
" <td>0.018863</td>\n",
" <td>0.046670</td>\n",
" <td>0.102529</td>\n",
" <td>0.461338</td>\n",
" <td>0.368810</td>\n",
" <td>0.319457</td>\n",
" <td>0.008537</td>\n",
" <td>0.167900</td>\n",
" <td>0.035125</td>\n",
" <td>0.003659</td>\n",
" <td>0.051955</td>\n",
" <td>0.036751</td>\n",
" <td>0.065696</td>\n",
" <td>0.009513</td>\n",
" <td>0.036100</td>\n",
" <td>0.022766</td>\n",
" <td>0.003252</td>\n",
" <td>0.014798</td>\n",
" <td>0.013172</td>\n",
" <td>0.077323</td>\n",
" <td>0.098047</td>\n",
" <td>0.047368</td>\n",
" <td>0.850208</td>\n",
" <td>0.175055</td>\n",
" <td>0.043256</td>\n",
" <td>0.511342</td>\n",
" <td>0.030084</td>\n",
" <td>0.055940</td>\n",
" <td>0.000000</td>\n",
" <td>0.003334</td>\n",
" <td>0.055696</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>48005</td>\n",
" <td>texas</td>\n",
" <td>Angelina</td>\n",
" <td>republican</td>\n",
" <td>0.723981</td>\n",
" <td>0.460148</td>\n",
" <td>87607</td>\n",
" <td>0.208537</td>\n",
" <td>0.245155</td>\n",
" <td>0.258604</td>\n",
" <td>0.287704</td>\n",
" <td>64914</td>\n",
" <td>0.011495</td>\n",
" <td>0.147956</td>\n",
" <td>0.218864</td>\n",
" <td>0.791855</td>\n",
" <td>0.4495</td>\n",
" <td>22322</td>\n",
" <td>0.0</td>\n",
" <td>0.012416</td>\n",
" <td>0.009582</td>\n",
" <td>0.032027</td>\n",
" <td>0.054025</td>\n",
" <td>0.156638</td>\n",
" <td>0.314755</td>\n",
" <td>0.215177</td>\n",
" <td>0.306251</td>\n",
" <td>0.011092</td>\n",
" <td>0.010768</td>\n",
" <td>0.044012</td>\n",
" <td>0.005099</td>\n",
" <td>0.037681</td>\n",
" <td>0.049589</td>\n",
" <td>0.092831</td>\n",
" <td>0.002927</td>\n",
" <td>0.064624</td>\n",
" <td>0.024525</td>\n",
" <td>0.000308</td>\n",
" <td>0.018578</td>\n",
" <td>0.023323</td>\n",
" <td>0.073836</td>\n",
" <td>0.058420</td>\n",
" <td>0.026197</td>\n",
" <td>0.909745</td>\n",
" <td>0.204409</td>\n",
" <td>0.058092</td>\n",
" <td>0.351111</td>\n",
" <td>0.057784</td>\n",
" <td>0.073559</td>\n",
" <td>0.001833</td>\n",
" <td>0.006085</td>\n",
" <td>0.091336</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>48007</td>\n",
" <td>texas</td>\n",
" <td>Aransas</td>\n",
" <td>republican</td>\n",
" <td>0.751811</td>\n",
" <td>0.514525</td>\n",
" <td>24763</td>\n",
" <td>0.147476</td>\n",
" <td>0.171872</td>\n",
" <td>0.252445</td>\n",
" <td>0.428208</td>\n",
" <td>20044</td>\n",
" <td>0.019707</td>\n",
" <td>0.015386</td>\n",
" <td>0.272826</td>\n",
" <td>0.892622</td>\n",
" <td>0.5351</td>\n",
" <td>30939</td>\n",
" <td>0.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.006186</td>\n",
" <td>0.031581</td>\n",
" <td>0.037767</td>\n",
" <td>0.211684</td>\n",
" <td>0.299242</td>\n",
" <td>0.185242</td>\n",
" <td>0.339453</td>\n",
" <td>0.005887</td>\n",
" <td>0.018010</td>\n",
" <td>0.057025</td>\n",
" <td>0.023249</td>\n",
" <td>0.062862</td>\n",
" <td>0.043205</td>\n",
" <td>0.052784</td>\n",
" <td>0.002644</td>\n",
" <td>0.025244</td>\n",
" <td>0.019407</td>\n",
" <td>0.000000</td>\n",
" <td>0.022051</td>\n",
" <td>0.030682</td>\n",
" <td>0.054131</td>\n",
" <td>0.039979</td>\n",
" <td>0.031579</td>\n",
" <td>0.914671</td>\n",
" <td>0.206945</td>\n",
" <td>0.079824</td>\n",
" <td>0.252744</td>\n",
" <td>0.026542</td>\n",
" <td>0.108162</td>\n",
" <td>0.007334</td>\n",
" <td>0.004690</td>\n",
" <td>0.128916</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>48009</td>\n",
" <td>texas</td>\n",
" <td>Archer</td>\n",
" <td>republican</td>\n",
" <td>0.896580</td>\n",
" <td>0.803586</td>\n",
" <td>8789</td>\n",
" <td>0.163153</td>\n",
" <td>0.208085</td>\n",
" <td>0.281809</td>\n",
" <td>0.346954</td>\n",
" <td>6877</td>\n",
" <td>0.005348</td>\n",
" <td>0.009216</td>\n",
" <td>0.082717</td>\n",
" <td>0.948231</td>\n",
" <td>0.4316</td>\n",
" <td>31806</td>\n",
" <td>0.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.002472</td>\n",
" <td>0.020503</td>\n",
" <td>0.022975</td>\n",
" <td>0.210121</td>\n",
" <td>0.305511</td>\n",
" <td>0.096990</td>\n",
" <td>0.310310</td>\n",
" <td>0.034026</td>\n",
" <td>0.028210</td>\n",
" <td>0.014977</td>\n",
" <td>0.008579</td>\n",
" <td>0.046968</td>\n",
" <td>0.042751</td>\n",
" <td>0.123164</td>\n",
" <td>0.006107</td>\n",
" <td>0.049149</td>\n",
" <td>0.021085</td>\n",
" <td>0.000000</td>\n",
" <td>0.027774</td>\n",
" <td>0.033881</td>\n",
" <td>0.080413</td>\n",
" <td>0.019229</td>\n",
" <td>0.006713</td>\n",
" <td>0.971100</td>\n",
" <td>0.127963</td>\n",
" <td>0.077359</td>\n",
" <td>0.422132</td>\n",
" <td>0.025447</td>\n",
" <td>0.073724</td>\n",
" <td>0.011778</td>\n",
" <td>0.000145</td>\n",
" <td>0.090883</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" fips ... VETERANS_Estimate__Total__Veteran\n",
"0 48001 ... 0.086006\n",
"1 48003 ... 0.055696\n",
"2 48005 ... 0.091336\n",
"3 48007 ... 0.128916\n",
"4 48009 ... 0.090883\n",
"\n",
"[5 rows x 52 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 74
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "1bcZqbGVQJYa"
},
"source": [
"## Select columns for clustering\n",
"\n",
"Use `.select` to select columns by name."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 222
},
"id": "UDMlKUGrkLjU",
"outputId": "d8aa1a25-ff02-4729-f5b5-a0957965bf96"
},
"source": [
"col_tags = ('AGE', 'RACE', 'GINI', 'INCOME', 'UNEMPLOY', 'EDU', 'INDUSTRY', 'CITIZEN', 'HEALTH', 'VETERANS')\n",
"\n",
"cols = list(filter(lambda x: x.startswith(col_tags), df.columns))\n",
"\n",
"cluster_df = df.select(cols)\n",
"\n",
"cluster_df.limit(5).toPandas().head()"
],
"execution_count": 75,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>AGE_18_29</th>\n",
" <th>AGE_30_44</th>\n",
" <th>AGE_45_59</th>\n",
" <th>AGE_60_Plus</th>\n",
" <th>AGE_18_Plus</th>\n",
" <th>RACE_Total__Asian_alone</th>\n",
" <th>RACE_Total__Black_or_African_American_alone</th>\n",
" <th>RACE_Total__Hispanic_or_Latino</th>\n",
" <th>RACE_Total__White_alone</th>\n",
" <th>GINI_Gini_Index</th>\n",
" <th>INCOME_PER_CAPITA_INCOME_IN_THE_PAST_12_MONTHS__IN_2018_INFLATION_ADJUSTED_DOLLARS_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__ASIAN_ALONE_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__BLACK_OR_AFRICAN_AMERICAN_ALONE_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__HISPANIC_OR_LATINO_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__WHITE_ALONE_</th>\n",
" <th>UNEMPLOY_Total_16_YEARS_AND_OVER</th>\n",
" <th>EDU_ATTAIN_Total__Bachelor_s_degree_or_higher</th>\n",
" <th>EDU_ATTAIN_Total__High_school_graduate__includes_equivalency_</th>\n",
" <th>EDU_ATTAIN_Total__Less_than_high_school_diploma</th>\n",
" <th>EDU_ATTAIN_Total__Some_college_or_associate_s_degree</th>\n",
" <th>INDUSTRY_Total__Agriculture__forestry__fishing_and_hunting__and_mining__Agriculture__forestry__fishing_and_hunting</th>\n",
" <th>INDUSTRY_Total__Agriculture__forestry__fishing_and_hunting__and_mining__Mining__quarrying__and_oil_and_gas_extraction</th>\n",
" <th>INDUSTRY_Total__Arts__entertainment__and_recreation__and_accommodation_and_food_services__Accommodation_and_food_services</th>\n",
" <th>INDUSTRY_Total__Arts__entertainment__and_recreation__and_accommodation_and_food_services__Arts__entertainment__and_recreation</th>\n",
" <th>INDUSTRY_Total__Construction</th>\n",
" <th>INDUSTRY_Total__Educational_services__and_health_care_and_social_assistance__Educational_services</th>\n",
" <th>INDUSTRY_Total__Educational_services__and_health_care_and_social_assistance__Health_care_and_social_assistance</th>\n",
" <th>INDUSTRY_Total__Information</th>\n",
" <th>INDUSTRY_Total__Manufacturing</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Administrative_and_support_and_waste_management_services</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Management_of_companies_and_enterprises</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Professional__scientific__and_technical_services</th>\n",
" <th>INDUSTRY_Total__Public_administration</th>\n",
" <th>INDUSTRY_Total__Retail_trade</th>\n",
" <th>CITIZEN_Estimate__Total__Not_a_U_S__citizen</th>\n",
" <th>CITIZEN_Estimate__Total__U_S__citizen_by_naturalization</th>\n",
" <th>CITIZEN_Estimate__Total__U_S__citizen__born_in_the_United_States</th>\n",
" <th>HEALTH_INSURANCE_No_health_insurance_coverage</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_direct_purchase_health_insurance_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_employer_based_health_insurance_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_Medicaid_means_tested_public_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_Medicare_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_TRICARE_military_health_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_VA_Health_Care_only</th>\n",
" <th>VETERANS_Estimate__Total__Veteran</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.192527</td>\n",
" <td>0.308352</td>\n",
" <td>0.246227</td>\n",
" <td>0.252894</td>\n",
" <td>46648</td>\n",
" <td>0.005513</td>\n",
" <td>0.209823</td>\n",
" <td>0.175276</td>\n",
" <td>0.735340</td>\n",
" <td>0.4225</td>\n",
" <td>16868</td>\n",
" <td>0.0</td>\n",
" <td>0.004287</td>\n",
" <td>0.002272</td>\n",
" <td>0.007760</td>\n",
" <td>0.014320</td>\n",
" <td>0.105299</td>\n",
" <td>0.359608</td>\n",
" <td>0.232636</td>\n",
" <td>0.308481</td>\n",
" <td>0.007846</td>\n",
" <td>0.028490</td>\n",
" <td>0.022359</td>\n",
" <td>0.002165</td>\n",
" <td>0.022380</td>\n",
" <td>0.029905</td>\n",
" <td>0.057473</td>\n",
" <td>0.002037</td>\n",
" <td>0.025424</td>\n",
" <td>0.021373</td>\n",
" <td>0.000000</td>\n",
" <td>0.010912</td>\n",
" <td>0.038608</td>\n",
" <td>0.077860</td>\n",
" <td>0.041546</td>\n",
" <td>0.020946</td>\n",
" <td>0.928400</td>\n",
" <td>0.114324</td>\n",
" <td>0.028040</td>\n",
" <td>0.306080</td>\n",
" <td>0.028040</td>\n",
" <td>0.065833</td>\n",
" <td>0.005638</td>\n",
" <td>0.006088</td>\n",
" <td>0.086006</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0.245792</td>\n",
" <td>0.287747</td>\n",
" <td>0.246605</td>\n",
" <td>0.219855</td>\n",
" <td>12299</td>\n",
" <td>0.003536</td>\n",
" <td>0.019811</td>\n",
" <td>0.560052</td>\n",
" <td>0.924009</td>\n",
" <td>0.4506</td>\n",
" <td>31190</td>\n",
" <td>0.0</td>\n",
" <td>0.012196</td>\n",
" <td>0.015611</td>\n",
" <td>0.018863</td>\n",
" <td>0.046670</td>\n",
" <td>0.102529</td>\n",
" <td>0.461338</td>\n",
" <td>0.368810</td>\n",
" <td>0.319457</td>\n",
" <td>0.008537</td>\n",
" <td>0.167900</td>\n",
" <td>0.035125</td>\n",
" <td>0.003659</td>\n",
" <td>0.051955</td>\n",
" <td>0.036751</td>\n",
" <td>0.065696</td>\n",
" <td>0.009513</td>\n",
" <td>0.036100</td>\n",
" <td>0.022766</td>\n",
" <td>0.003252</td>\n",
" <td>0.014798</td>\n",
" <td>0.013172</td>\n",
" <td>0.077323</td>\n",
" <td>0.098047</td>\n",
" <td>0.047368</td>\n",
" <td>0.850208</td>\n",
" <td>0.175055</td>\n",
" <td>0.043256</td>\n",
" <td>0.511342</td>\n",
" <td>0.030084</td>\n",
" <td>0.055940</td>\n",
" <td>0.000000</td>\n",
" <td>0.003334</td>\n",
" <td>0.055696</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0.208537</td>\n",
" <td>0.245155</td>\n",
" <td>0.258604</td>\n",
" <td>0.287704</td>\n",
" <td>64914</td>\n",
" <td>0.011495</td>\n",
" <td>0.147956</td>\n",
" <td>0.218864</td>\n",
" <td>0.791855</td>\n",
" <td>0.4495</td>\n",
" <td>22322</td>\n",
" <td>0.0</td>\n",
" <td>0.012416</td>\n",
" <td>0.009582</td>\n",
" <td>0.032027</td>\n",
" <td>0.054025</td>\n",
" <td>0.156638</td>\n",
" <td>0.314755</td>\n",
" <td>0.215177</td>\n",
" <td>0.306251</td>\n",
" <td>0.011092</td>\n",
" <td>0.010768</td>\n",
" <td>0.044012</td>\n",
" <td>0.005099</td>\n",
" <td>0.037681</td>\n",
" <td>0.049589</td>\n",
" <td>0.092831</td>\n",
" <td>0.002927</td>\n",
" <td>0.064624</td>\n",
" <td>0.024525</td>\n",
" <td>0.000308</td>\n",
" <td>0.018578</td>\n",
" <td>0.023323</td>\n",
" <td>0.073836</td>\n",
" <td>0.058420</td>\n",
" <td>0.026197</td>\n",
" <td>0.909745</td>\n",
" <td>0.204409</td>\n",
" <td>0.058092</td>\n",
" <td>0.351111</td>\n",
" <td>0.057784</td>\n",
" <td>0.073559</td>\n",
" <td>0.001833</td>\n",
" <td>0.006085</td>\n",
" <td>0.091336</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>0.147476</td>\n",
" <td>0.171872</td>\n",
" <td>0.252445</td>\n",
" <td>0.428208</td>\n",
" <td>20044</td>\n",
" <td>0.019707</td>\n",
" <td>0.015386</td>\n",
" <td>0.272826</td>\n",
" <td>0.892622</td>\n",
" <td>0.5351</td>\n",
" <td>30939</td>\n",
" <td>0.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.006186</td>\n",
" <td>0.031581</td>\n",
" <td>0.037767</td>\n",
" <td>0.211684</td>\n",
" <td>0.299242</td>\n",
" <td>0.185242</td>\n",
" <td>0.339453</td>\n",
" <td>0.005887</td>\n",
" <td>0.018010</td>\n",
" <td>0.057025</td>\n",
" <td>0.023249</td>\n",
" <td>0.062862</td>\n",
" <td>0.043205</td>\n",
" <td>0.052784</td>\n",
" <td>0.002644</td>\n",
" <td>0.025244</td>\n",
" <td>0.019407</td>\n",
" <td>0.000000</td>\n",
" <td>0.022051</td>\n",
" <td>0.030682</td>\n",
" <td>0.054131</td>\n",
" <td>0.039979</td>\n",
" <td>0.031579</td>\n",
" <td>0.914671</td>\n",
" <td>0.206945</td>\n",
" <td>0.079824</td>\n",
" <td>0.252744</td>\n",
" <td>0.026542</td>\n",
" <td>0.108162</td>\n",
" <td>0.007334</td>\n",
" <td>0.004690</td>\n",
" <td>0.128916</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>0.163153</td>\n",
" <td>0.208085</td>\n",
" <td>0.281809</td>\n",
" <td>0.346954</td>\n",
" <td>6877</td>\n",
" <td>0.005348</td>\n",
" <td>0.009216</td>\n",
" <td>0.082717</td>\n",
" <td>0.948231</td>\n",
" <td>0.4316</td>\n",
" <td>31806</td>\n",
" <td>0.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.002472</td>\n",
" <td>0.020503</td>\n",
" <td>0.022975</td>\n",
" <td>0.210121</td>\n",
" <td>0.305511</td>\n",
" <td>0.096990</td>\n",
" <td>0.310310</td>\n",
" <td>0.034026</td>\n",
" <td>0.028210</td>\n",
" <td>0.014977</td>\n",
" <td>0.008579</td>\n",
" <td>0.046968</td>\n",
" <td>0.042751</td>\n",
" <td>0.123164</td>\n",
" <td>0.006107</td>\n",
" <td>0.049149</td>\n",
" <td>0.021085</td>\n",
" <td>0.000000</td>\n",
" <td>0.027774</td>\n",
" <td>0.033881</td>\n",
" <td>0.080413</td>\n",
" <td>0.019229</td>\n",
" <td>0.006713</td>\n",
" <td>0.971100</td>\n",
" <td>0.127963</td>\n",
" <td>0.077359</td>\n",
" <td>0.422132</td>\n",
" <td>0.025447</td>\n",
" <td>0.073724</td>\n",
" <td>0.011778</td>\n",
" <td>0.000145</td>\n",
" <td>0.090883</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" AGE_18_29 ... VETERANS_Estimate__Total__Veteran\n",
"0 0.192527 ... 0.086006\n",
"1 0.245792 ... 0.055696\n",
"2 0.208537 ... 0.091336\n",
"3 0.147476 ... 0.128916\n",
"4 0.163153 ... 0.090883\n",
"\n",
"[5 rows x 45 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 75
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "rD1yc70LQcsf"
},
"source": [
"## Assemble and fit an ML Pipeline\n",
"\n",
"An [ML Pipeline](https://spark.apache.org/docs/latest/ml-pipeline.html) contains a sequence of transformations to apply to data. Each stage in the Pipeline is either a `Transformer` or an `Estimator`.\n",
"\n",
"The following Pipeline contains three stages: \n",
"\n",
"1. `VectorAssembler`: assemble features into a vector\n",
"2. `StandardScaler`: scale the features to have mean=0, sd=1\n",
"3. `KMeans`: initalize the K-Means algorithm"
]
},
{
"cell_type": "code",
"metadata": {
"id": "-jRW-GzIQfSI"
},
"source": [
"## 1. Assemble features into a vector\n",
"vecAssembler = VectorAssembler(inputCols=cols, outputCol=\"vecfeatures\")\n",
"\n",
"## 2. Scale the features to have mean 0 and standard deviation 1\n",
"scaler = StandardScaler(inputCol=\"vecfeatures\", outputCol=\"features\",\n",
" withStd=True, withMean=True)\n",
"\n",
"## 3. Initialize the K-Means algorithm\n",
"kmeans = KMeans(k=3, seed=1)\n",
"\n",
"# Assemble Pipeline\n",
"pipeline = Pipeline(stages=[vecAssembler, scaler, kmeans])"
],
"execution_count": 19,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "aVSGjTYNgJ-W"
},
"source": [
"Fit the pipeline to data using `.fit`, then use `.transform` to make predictions (i.e. predict cluster labels)."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 202
},
"id": "YudzkC7AfiX5",
"outputId": "30a9f9ee-9e39-4f1c-d0f6-ba971a9bda7b"
},
"source": [
"# Fit the pipeline \n",
"model = pipeline.fit(cluster_df) \n",
"\n",
"# Make a prediction \n",
"prediction = model.transform(cluster_df)\n",
"\n",
"prediction.select('vecFeatures', 'features', 'prediction').limit(5).toPandas().head()"
],
"execution_count": 79,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>vecFeatures</th>\n",
" <th>features</th>\n",
" <th>prediction</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>[0.19252701080432172, 0.3083519121934488, 0.24...</td>\n",
" <td>[0.04420494748177839, 2.471247594748924, -0.53...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>[0.24579234084071877, 0.28774697129847954, 0.2...</td>\n",
" <td>[1.0502649452687496, 1.8607396843027733, -0.51...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>[0.20853744954863357, 0.24515512832362818, 0.2...</td>\n",
" <td>[0.34660543869556293, 0.5987774593601314, -0.0...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>[0.1474755537816803, 0.1718718818599082, 0.252...</td>\n",
" <td>[-0.8067138156522811, -1.5725464914015652, -0....</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>[0.16315253744365277, 0.20808492075032717, 0.2...</td>\n",
" <td>[-0.5106115266494129, -0.4995831271003042, 0.8...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" vecFeatures ... prediction\n",
"0 [0.19252701080432172, 0.3083519121934488, 0.24... ... 0\n",
"1 [0.24579234084071877, 0.28774697129847954, 0.2... ... 1\n",
"2 [0.20853744954863357, 0.24515512832362818, 0.2... ... 0\n",
"3 [0.1474755537816803, 0.1718718818599082, 0.252... ... 0\n",
"4 [0.16315253744365277, 0.20808492075032717, 0.2... ... 0\n",
"\n",
"[5 rows x 3 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 79
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "sPRjlYkoTiSu"
},
"source": [
"## Silhouette score\n",
"\n",
"Evaluate the predictions using the Silhouette score."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "lwwIfUK_lwe3",
"outputId": "ad4b9b27-5164-43f1-fff8-53986de8e0ff"
},
"source": [
"# Evaluate clustering by computing Silhouette score\n",
"evaluator = ClusteringEvaluator()\n",
"silhouette = evaluator.evaluate(prediction)\n",
"print(\"Silhouette with squared euclidean distance = \" + str(silhouette))"
],
"execution_count": 22,
"outputs": [
{
"output_type": "stream",
"text": [
"Silhouette with squared euclidean distance = 0.35346767131585405\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "oXpQx2ZITlk_"
},
"source": [
"## Cluster centers\n",
"\n",
"To retreive the cluster centers, the K-Means model needs to be extracted from the fitted Pipeline. Use `model.stages` to view the Pipeline stages."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "hy1qmQ56gwEa",
"outputId": "d964d777-a73c-4558-ce15-313756badf91"
},
"source": [
"model.stages"
],
"execution_count": 61,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"[VectorAssembler_0c8c65f82cf3,\n",
" StandardScalerModel: uid=StandardScaler_77347baa1268, numFeatures=45, withMean=true, withStd=true,\n",
" KMeansModel: uid=KMeans_455712dbad97, k=3, distanceMeasure=euclidean, numFeatures=45]"
]
},
"metadata": {
"tags": []
},
"execution_count": 61
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 161
},
"id": "rfnR8r8BTPJM",
"outputId": "30d1f645-20e2-4fd1-b183-bd11e288ab50"
},
"source": [
"centers = model.stages[2].clusterCenters()\n",
"centers_df = pd.DataFrame(centers)\n",
"centers_df.columns = cluster_df.columns\n",
"\n",
"centers_df"
],
"execution_count": 62,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>AGE_18_29</th>\n",
" <th>AGE_30_44</th>\n",
" <th>AGE_45_59</th>\n",
" <th>AGE_60_Plus</th>\n",
" <th>AGE_18_Plus</th>\n",
" <th>RACE_Total__Asian_alone</th>\n",
" <th>RACE_Total__Black_or_African_American_alone</th>\n",
" <th>RACE_Total__Hispanic_or_Latino</th>\n",
" <th>RACE_Total__White_alone</th>\n",
" <th>GINI_Gini_Index</th>\n",
" <th>INCOME_PER_CAPITA_INCOME_IN_THE_PAST_12_MONTHS__IN_2018_INFLATION_ADJUSTED_DOLLARS_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__ASIAN_ALONE_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__BLACK_OR_AFRICAN_AMERICAN_ALONE_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__HISPANIC_OR_LATINO_</th>\n",
" <th>UNEMPLOY__16_YEARS_AND_OVER__WHITE_ALONE_</th>\n",
" <th>UNEMPLOY_Total_16_YEARS_AND_OVER</th>\n",
" <th>EDU_ATTAIN_Total__Bachelor_s_degree_or_higher</th>\n",
" <th>EDU_ATTAIN_Total__High_school_graduate__includes_equivalency_</th>\n",
" <th>EDU_ATTAIN_Total__Less_than_high_school_diploma</th>\n",
" <th>EDU_ATTAIN_Total__Some_college_or_associate_s_degree</th>\n",
" <th>INDUSTRY_Total__Agriculture__forestry__fishing_and_hunting__and_mining__Agriculture__forestry__fishing_and_hunting</th>\n",
" <th>INDUSTRY_Total__Agriculture__forestry__fishing_and_hunting__and_mining__Mining__quarrying__and_oil_and_gas_extraction</th>\n",
" <th>INDUSTRY_Total__Arts__entertainment__and_recreation__and_accommodation_and_food_services__Accommodation_and_food_services</th>\n",
" <th>INDUSTRY_Total__Arts__entertainment__and_recreation__and_accommodation_and_food_services__Arts__entertainment__and_recreation</th>\n",
" <th>INDUSTRY_Total__Construction</th>\n",
" <th>INDUSTRY_Total__Educational_services__and_health_care_and_social_assistance__Educational_services</th>\n",
" <th>INDUSTRY_Total__Educational_services__and_health_care_and_social_assistance__Health_care_and_social_assistance</th>\n",
" <th>INDUSTRY_Total__Information</th>\n",
" <th>INDUSTRY_Total__Manufacturing</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Administrative_and_support_and_waste_management_services</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Management_of_companies_and_enterprises</th>\n",
" <th>INDUSTRY_Total__Professional__scientific__and_management__and_administrative__and_waste_management_services__Professional__scientific__and_technical_services</th>\n",
" <th>INDUSTRY_Total__Public_administration</th>\n",
" <th>INDUSTRY_Total__Retail_trade</th>\n",
" <th>CITIZEN_Estimate__Total__Not_a_U_S__citizen</th>\n",
" <th>CITIZEN_Estimate__Total__U_S__citizen_by_naturalization</th>\n",
" <th>CITIZEN_Estimate__Total__U_S__citizen__born_in_the_United_States</th>\n",
" <th>HEALTH_INSURANCE_No_health_insurance_coverage</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_direct_purchase_health_insurance_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_employer_based_health_insurance_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_Medicaid_means_tested_public_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_Medicare_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_TRICARE_military_health_coverage_only</th>\n",
" <th>HEALTH_INSURANCE_With_one_type_of_health_insurance_coverageWith_VA_Health_Care_only</th>\n",
" <th>VETERANS_Estimate__Total__Veteran</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>-0.287159</td>\n",
" <td>-0.249382</td>\n",
" <td>0.125231</td>\n",
" <td>0.320757</td>\n",
" <td>-0.207120</td>\n",
" <td>-0.279015</td>\n",
" <td>-0.004489</td>\n",
" <td>-0.287263</td>\n",
" <td>0.097970</td>\n",
" <td>-0.048442</td>\n",
" <td>-0.233522</td>\n",
" <td>-0.245131</td>\n",
" <td>-0.009091</td>\n",
" <td>-0.244621</td>\n",
" <td>-0.018794</td>\n",
" <td>-0.143356</td>\n",
" <td>-0.338661</td>\n",
" <td>0.266309</td>\n",
" <td>-0.070275</td>\n",
" <td>0.007361</td>\n",
" <td>0.090156</td>\n",
" <td>-0.024996</td>\n",
" <td>-0.224085</td>\n",
" <td>-0.125167</td>\n",
" <td>-0.014100</td>\n",
" <td>-0.218072</td>\n",
" <td>-0.036337</td>\n",
" <td>-0.188347</td>\n",
" <td>0.102984</td>\n",
" <td>-0.226144</td>\n",
" <td>-0.138354</td>\n",
" <td>-0.304601</td>\n",
" <td>-0.068678</td>\n",
" <td>-0.120262</td>\n",
" <td>-0.345333</td>\n",
" <td>-0.346906</td>\n",
" <td>0.380162</td>\n",
" <td>-0.022233</td>\n",
" <td>-0.008200</td>\n",
" <td>-0.221717</td>\n",
" <td>0.060725</td>\n",
" <td>0.254616</td>\n",
" <td>-0.107198</td>\n",
" <td>0.064268</td>\n",
" <td>0.143876</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0.601502</td>\n",
" <td>0.905257</td>\n",
" <td>-0.649011</td>\n",
" <td>-0.718091</td>\n",
" <td>0.035547</td>\n",
" <td>0.026191</td>\n",
" <td>-0.338923</td>\n",
" <td>2.970818</td>\n",
" <td>-0.141378</td>\n",
" <td>0.084737</td>\n",
" <td>-0.643557</td>\n",
" <td>0.182039</td>\n",
" <td>-0.294189</td>\n",
" <td>2.638494</td>\n",
" <td>0.603661</td>\n",
" <td>1.402759</td>\n",
" <td>-0.371628</td>\n",
" <td>0.588405</td>\n",
" <td>2.426231</td>\n",
" <td>0.650506</td>\n",
" <td>0.625147</td>\n",
" <td>1.074540</td>\n",
" <td>0.126774</td>\n",
" <td>-0.271156</td>\n",
" <td>0.138014</td>\n",
" <td>0.041850</td>\n",
" <td>-0.767634</td>\n",
" <td>-0.405155</td>\n",
" <td>-0.502686</td>\n",
" <td>-0.007139</td>\n",
" <td>-0.127766</td>\n",
" <td>-0.393236</td>\n",
" <td>0.122950</td>\n",
" <td>-0.338224</td>\n",
" <td>2.179047</td>\n",
" <td>1.098175</td>\n",
" <td>-1.790288</td>\n",
" <td>1.350968</td>\n",
" <td>-0.318381</td>\n",
" <td>-0.409738</td>\n",
" <td>0.120902</td>\n",
" <td>-0.309357</td>\n",
" <td>-0.177184</td>\n",
" <td>-0.132713</td>\n",
" <td>-0.847554</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0.865168</td>\n",
" <td>0.627222</td>\n",
" <td>-0.251938</td>\n",
" <td>-0.951414</td>\n",
" <td>0.753148</td>\n",
" <td>1.021615</td>\n",
" <td>0.126450</td>\n",
" <td>0.097437</td>\n",
" <td>-0.315864</td>\n",
" <td>0.151373</td>\n",
" <td>1.070786</td>\n",
" <td>0.845992</td>\n",
" <td>0.128937</td>\n",
" <td>0.047743</td>\n",
" <td>-0.126316</td>\n",
" <td>0.074497</td>\n",
" <td>1.370794</td>\n",
" <td>-1.173952</td>\n",
" <td>-0.527116</td>\n",
" <td>-0.238068</td>\n",
" <td>-0.535520</td>\n",
" <td>-0.256075</td>\n",
" <td>0.786207</td>\n",
" <td>0.550017</td>\n",
" <td>0.007314</td>\n",
" <td>0.791539</td>\n",
" <td>0.383017</td>\n",
" <td>0.826713</td>\n",
" <td>-0.217245</td>\n",
" <td>0.837223</td>\n",
" <td>0.552214</td>\n",
" <td>1.252050</td>\n",
" <td>0.213695</td>\n",
" <td>0.553648</td>\n",
" <td>0.568514</td>\n",
" <td>0.924733</td>\n",
" <td>-0.823136</td>\n",
" <td>-0.355890</td>\n",
" <td>0.133491</td>\n",
" <td>0.951399</td>\n",
" <td>-0.263388</td>\n",
" <td>-0.839736</td>\n",
" <td>0.453209</td>\n",
" <td>-0.194250</td>\n",
" <td>-0.256408</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" AGE_18_29 ... VETERANS_Estimate__Total__Veteran\n",
"0 -0.287159 ... 0.143876\n",
"1 0.601502 ... -0.847554\n",
"2 0.865168 ... -0.256408\n",
"\n",
"[3 rows x 45 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 62
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "9rL0ZGs3WYRZ"
},
"source": [
"## Cluster frequencies"
]
},
{
"cell_type": "code",
"metadata": {
"id": "F9aXDTRkU07y"
},
"source": [
"prediction.groupBy('prediction').count().show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "d5Fxz3HbWVHM"
},
"source": [
"## Reusable function\n",
"\n",
"The following functions captures the previous steps (assemble Pipeline, fit model, make predictions, assess fit, obtain cluster centers and frequencies) for reusability."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Q4kL8DxdWXDl"
},
"source": [
"def run_clustering(k):\n",
" # Define and assemble the Pipeline\n",
" vecAssembler = VectorAssembler(inputCols=cols, outputCol=\"vecfeatures\")\n",
" scaler = StandardScaler(inputCol=\"vecfeatures\", outputCol=\"features\",\n",
" withStd=True, withMean=True)\n",
" kmeans = KMeans(k=k, seed=1)\n",
" pipeline = Pipeline(stages=[vecAssembler, scaler, kmeans])\n",
" \n",
" # Fit the pipeline \n",
" model = pipeline.fit(cluster_df) \n",
" \n",
" # Make a prediction \n",
" prediction = model.transform(cluster_df)\n",
" \n",
" # Evaluate clustering by computing Silhouette score\n",
" evaluator = ClusteringEvaluator()\n",
" silhouette = evaluator.evaluate(prediction)\n",
" \n",
" # Cluster centers\n",
" centers = model.stages[2].clusterCenters()\n",
" centers_df = pd.DataFrame(centers)\n",
" centers_df.columns = cluster_df.columns\n",
" \n",
" # Cluster frequencies\n",
" cluster_freq = prediction.groupBy('prediction').count()\n",
" \n",
" out = {\n",
" \"silhouette\": silhouette,\n",
" \"centers\": centers_df,\n",
" \"freq\": cluster_freq\n",
" }\n",
" \n",
" return(out)"
],
"execution_count": 51,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "-jo5IOUCW9eD"
},
"source": [
"## Assess a range of K values\n",
"\n",
"Use the previously defined `run_clustering` function to assess the Silhouette score for a range of *K* values."
]
},
{
"cell_type": "code",
"metadata": {
"id": "kHD4HNybWcWv"
},
"source": [
"k_values = list(range(3,20))\n",
"\n",
"k_clustering = [run_clustering(i) for i in k_values]\n",
"\n",
"silhouette_results = [{\"k\": k, \"silhouette\": i['silhouette']} for k,i in zip(k_values, k_clustering)]\n",
"\n",
"silhouette_df = pd.DataFrame(silhouette_results)"
],
"execution_count": 67,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 314
},
"id": "KRGtwTxcil0p",
"outputId": "5720a281-67ab-4ae8-fcbc-059183f5fbf4"
},
"source": [
"silhouette_df.plot.line(x='k', y='silhouette', title = \"Silhouette score for a range of K values\")"
],
"execution_count": 71,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x7f2782b3f1d0>"
]
},
"metadata": {
"tags": []
},
"execution_count": 71
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment