Definitiv guide til hierarkisk klyngedannelse med Python og Scikit-Learn PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Definitiv guide til hierarkisk klyngedannelse med Python og Scikit-Learn

Introduktion

I denne guide vil vi fokusere på at implementere Hierarkisk klyngealgoritme med Scikit-Learn at løse et marketingproblem.

Efter at have læst guiden vil du forstå:

  • Hvornår skal man anvende hierarkisk clustering
  • Hvordan man visualiserer datasættet for at forstå, om det er egnet til klyngedannelse
  • Hvordan man forbehandler funktioner og konstruerer nye funktioner baseret på datasættet
  • Hvordan man reducerer datasættets dimensionalitet ved hjælp af PCA
  • Hvordan man bruger og læser et dendrogram for at adskille grupper
  • Hvad er de forskellige koblingsmetoder og afstandsmålinger, der anvendes til dendrogrammer og klyngealgoritmer
  • Hvad er de agglomerative og splittende klyngestrategier, og hvordan fungerer de
  • Sådan implementeres den agglomerative hierarkiske klyngedannelse med Scikit-Learn
  • Hvad er de hyppigste problemer, når man beskæftiger sig med klyngealgoritmer, og hvordan man løser dem

Bemærk: Du kan downloade notesbogen, der indeholder al koden i denne vejledning link..

Motivation

Forestil dig et scenarie, hvor du er en del af et datavidenskabsteam, der kommunikerer med marketingafdelingen. Marketing har i et stykke tid indsamlet kundekøbsdata, og de ønsker at forstå, baseret på de indsamlede data, om der er ligheder mellem kunder. Disse ligheder opdeler kunder i grupper, og det at have kundegrupper hjælper med at målrette kampagner, kampagner, konverteringer og opbygge bedre kunderelationer.

Er der en måde, du kan hjælpe med at afgøre, hvilke kunder der ligner hinanden? Hvor mange af dem tilhører samme gruppe? Og hvor mange forskellige grupper er der?

En måde at besvare disse spørgsmål på er ved at bruge en klyngedannelse algoritmer, såsom K-Means, DBSCAN, Hierarchical Clustering osv. Generelt finder klyngealgoritmer ligheder mellem datapunkter og grupperer dem.

I dette tilfælde er vores markedsføringsdata ret små. Vi har kun oplysninger om 200 kunder. I betragtning af marketingteamet er det vigtigt, at vi tydeligt kan forklare dem, hvordan beslutningerne blev truffet baseret på antallet af klynger, og derfor forklare dem, hvordan algoritmen faktisk fungerer.

Da vores data er små, og forklarlighed er en vigtig faktor, kan vi udnytte Hierarkisk klyngedannelse at løse dette problem. Denne proces er også kendt som Hierarkisk klyngeanalyse (HCA).

En af fordelene ved HCA er, at den er fortolkelig og fungerer godt på små datasæt.

En anden ting at tage i betragtning i dette scenarie er, at HCA er en uden opsyn algoritme. Når vi grupperer data, vil vi ikke have en måde at bekræfte, at vi korrekt identificerer, at en bruger tilhører en bestemt gruppe (vi kender ikke grupperne). Der er ingen etiketter, som vi kan sammenligne vores resultater med. Hvis vi har identificeret grupperne korrekt, vil det senere blive bekræftet af marketingafdelingen på daglig basis (målt ved målinger som ROI, konverteringsrater osv.).

Nu hvor vi har forstået det problem, vi forsøger at løse, og hvordan vi løser det, kan vi begynde at tage et kig på vores data!

Kort sonderende dataanalyse

Bemærk: Du kan downloade det datasæt, der bruges i denne vejledning link..

Når du har downloadet datasættet, skal du bemærke, at det er en CSV (kommaseparerede værdier) fil kaldet shopping-data.csv. For at gøre det nemmere at udforske og manipulere dataene, indlæser vi dem i en DataFrame bruger pandaer:

import pandas as pd


path_to_file = 'home/projects/datasets/shopping-data.csv'
customer_data = pd.read_csv(path_to_file)

Marketing sagde, at det havde indsamlet 200 kunderegistre. Vi kan kontrollere, om de downloadede data er komplet med 200 rækker ved hjælp af shape attribut. Det vil fortælle os, hvor mange rækker og kolonner vi har, henholdsvis:

customer_data.shape

Dette resulterer i:

(200, 5)

Store! Vores data er komplet med 200 rækker (klient optegnelser) og vi har også 5 kolonner (funktioner). For at se hvilke egenskaber marketingafdelingen har indsamlet fra kunder, kan vi se kolonnenavne med columns attribut. For at gøre det skal du udføre:

customer_data.columns

Scriptet ovenfor returnerer:

Index(['CustomerID', 'Genre', 'Age', 'Annual Income (k$)',
       'Spending Score (1-100)'],
      dtype='object')

Her ser vi, at markedsføring har genereret en CustomerID, samlede Genre, Age, Annual Income (i tusindvis af dollars), og en Spending Score går fra 1 til 100 for hver af de 200 kunder. Da de blev spurgt om afklaring, sagde de, at værdierne i Spending Score kolonne angiver, hvor ofte en person bruger penge i et indkøbscenter på en skala fra 1 til 100. Med andre ord, hvis en kunde har en score på 0, bruger denne person aldrig penge, og hvis scoren er 100, har vi lige set højeste bruger.

Lad os tage et hurtigt kig på fordelingen af ​​denne score for at inspicere brugernes forbrugsvaner i vores datasæt. Det er der, pandaerne hist() metode kommer ind for at hjælpe:

customer_data['Spending Score (1-100)'].hist()

img

Ved at se på histogrammet ser vi, at mere end 35 kunder har scores imellem 40 , 60, så har mindre end 25 scores imellem 70 , 80. Så de fleste af vores kunder er balancerede brugere, efterfulgt af moderate til høje brugere. Vi kan også se, at der er en streg efter 0, til venstre for fordelingen, og en anden linje før 100, til højre for fordelingen. Disse tomme mellemrum betyder sandsynligvis, at distributionen ikke indeholder ikke-forbrugere, som ville have en score på 0, og at der heller ikke er høje brugere med en score på 100.

For at kontrollere, om det er sandt, kan vi se på fordelingens minimums- og maksimumværdier. Disse værdier kan nemt findes som en del af den beskrivende statistik, så vi kan bruge describe() metode til at få en forståelse af andre numeriske værdifordelinger:


customer_data.describe().transpose()

Dette vil give os en tabel, hvorfra vi kan læse distributioner af andre værdier af vores datasæt:

 						count 	mean 	std 		min 	25% 	50% 	75% 	max
CustomerID 				200.0 	100.50 	57.879185 	1.0 	50.75 	100.5 	150.25 	200.0
Age 					200.0 	38.85 	13.969007 	18.0 	28.75 	36.0 	49.00 	70.0
Annual Income (k$) 		200.0 	60.56 	26.264721 	15.0 	41.50 	61.5 	78.00 	137.0
Spending Score (1-100) 	200.0 	50.20 	25.823522 	1.0 	34.75 	50.0 	73.00 	99.0

Vores hypotese er bekræftet. Det min værdien af Spending Score is 1 og max er 99. Så det har vi ikke 0 or 100 score brugere. Lad os så tage et kig på de andre kolonner i den transponerede describe bord. Når man ser på mean , std kolonner, kan vi se det for Age og mean is 38.85 og std er ca 13.97. Det samme sker for Annual Income, Med en mean of 60.56 , std 26.26, Og Spending Score med en mean of 50 , std of 25.82. For alle funktioner mean er langt fra standardafvigelsen, hvilket indikerer vores data har høj variabilitet.

For bedre at forstå, hvordan vores data varierer, lad os plotte Annual Income fordeling:

customer_data['Annual Income (k$)'].hist()

Hvilket vil give os:

img

Bemærk i histogrammet, at de fleste af vores data, mere end 35 kunder, er koncentreret i nærheden af ​​tallet 60, på vores mean, in the horizontal axis. But what happens as we move towards the ends of the distribution? When going towards the left, from the $60.560 mean, the next value we will encounter is $34.300 – the mean ($60.560) minus the standard variation ($26.260). If we go further away to the left of our data distribution a similar rule applies, we subtract the standard variation ($26.260) from the current value ($34.300). Therefore, we’ll encounter a value of $8.040. Notice how our data went from $60k to $8k quickly. It is “jumping” $26.260 each time – varying a lot, and that is why we have such high variability.

img

Variabiliteten og størrelsen af ​​dataene er vigtige i klyngeanalyse, fordi afstandsmålinger af de fleste klyngealgoritmer er følsomme over for datastørrelser. Forskellen i størrelse kan ændre klyngeresultaterne ved at få et punkt til at virke tættere på eller fjernere fra et andet, end det faktisk er, og forvrænge den faktiske gruppering af data.

Indtil videre har vi set formen på vores data, nogle af dens distributioner og beskrivende statistik. Med Pandas kan vi også liste vores datatyper og se, om alle vores 200 rækker er fyldt eller har nogle null værdier:

customer_data.info()

Dette resulterer i:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200 entries, 0 to 199
Data columns (total 5 columns):
 #   Column                  Non-Null Count  Dtype 
---  ------                  --------------  ----- 
 0   CustomerID              200 non-null    int64 
 1   Genre                   200 non-null    object
 2   Age                     200 non-null    int64 
 3   Annual Income (k$)      200 non-null    int64 
 4   Spending Score (1-100)  200 non-null    int64 
dtypes: int64(4), object(1)
memory usage: 7.9+ KB

Her kan vi se, at der ikke er nogen null værdier i dataene, og at vi kun har én kategorisk kolonne – Genre. På dette stadie er det vigtigt, at vi har for øje, hvilke funktioner der synes interessante at blive tilføjet til klyngemodellen. Hvis vi vil tilføje Genre-kolonnen til vores model, bliver vi nødt til at transformere dens værdier fra kategorisk til numerisk.

Lad os se hvordan Genre udfyldes ved at tage et hurtigt kig på de første 5 værdier af vores data:

customer_data.head() 

Dette resulterer i:

    CustomerID 	Genre 	Age 	Annual Income (k$) 	Spending Score (1-100)
0 	1 			Male 	19 		15 					39
1 	2 			Male 	21 		15 					81
2 	3 			Female 	20 		16 					6
3 	4 			Female 	23 		16 					77
4 	5 			Female 	31 		17 					40

Det ser ud til, at det kun har Female , Male Kategorier. Det kan vi være sikre på ved at tage et kig på dets unikke værdier med unique:

customer_data['Genre'].unique()

Dette bekræfter vores antagelse:

array(['Male', 'Female'], dtype=object)

Indtil videre ved vi, at vi kun har to genrer, hvis vi planlægger at bruge denne funktion på vores model, Male kunne omdannes til 0 , Female til 1. Det er også vigtigt at tjekke forholdet mellem genrer, for at se om de er afbalancerede. Det kan vi gøre med value_counts() metode og dens argumentation normalize=True for at vise procenten mellem Male , Female:

customer_data['Genre'].value_counts(normalize=True)

Dette udsender:

Female    0.56
Male      0.44
Name: Genre, dtype: float64

Vi har 56 % af kvinderne i datasættet og 44 % af mændene. Forskellen mellem dem er kun 16%, og vores data er ikke 50/50, men er det balanceret nok ikke for at skabe problemer. Hvis resultaterne var 70/30, 60/40, så kunne det have været nødvendigt enten at indsamle flere data eller at anvende en form for dataforstærkningsteknik for at gøre forholdet mere afbalanceret.

Indtil nu, alle funktioner men Age, er kort blevet undersøgt. I hvilke bekymringer Age, er det normalt interessant at dele det op i skraldespande for at kunne segmentere kunder ud fra deres aldersgrupper. Hvis vi gør det, skal vi omdanne alderskategorierne til ét tal, før vi tilføjer dem til vores model. På den måde ville vi i stedet for at bruge kategorien 15-20 år tælle hvor mange kunder der er i 15-20 kategori, og det ville være et tal i en ny kolonne kaldet 15-20.

Rådgivning: I denne guide præsenterer vi kun en kort eksplorativ dataanalyse. Men du kan gå længere, og du bør gå længere. Du kan se, om der er indkomstforskelle og scoringsforskelle baseret på genre og alder. Dette beriger ikke kun analysen, men fører til bedre modelresultater. For at gå dybere ind i Exploratory Data Analysis, tjek EDA kapitel i "Hands-On House Price Prediction – Machine Learning i Python" Vejledt projekt.

Efter at have gættet på, hvad der kunne gøres med både kategorisk – eller kategorisk at være – Genre , Age kolonner, lad os anvende det, der er blevet diskuteret.

Kodningsvariabler og funktionsteknik

Lad os starte med at dividere Age i grupper, der varierer i 10, så vi har 20-30, 30-40, 40-50, og så videre. Da vores yngste kunde er 15, kan vi starte ved 15 og slutte ved 70, hvilket er alderen på den ældste kunde i dataene. Startende ved 15 og slutter ved 70, ville vi have 15-20, 20-30, 30-40, 40-50, 50-60 og 60-70 intervaller.

At gruppere eller bin Age værdier i disse intervaller, kan vi bruge pandaerne cut() metode til at skære dem i beholdere og derefter tildele beholderne til en ny Age Groups kolonne:

intervals = [15, 20, 30, 40, 50, 60, 70]
col = customer_data['Age']
customer_data['Age Groups'] = pd.cut(x=col, bins=intervals)


customer_data['Age Groups'] 

Dette resulterer i:

0      (15, 20]
1      (20, 30]
2      (15, 20]
3      (20, 30]
4      (30, 40]
         ...   
195    (30, 40]
196    (40, 50]
197    (30, 40]
198    (30, 40]
199    (20, 30]
Name: Age Groups, Length: 200, dtype: category
Categories (6, interval[int64, right]): [(15, 20] < (20, 30] < (30, 40] < (40, 50] < (50, 60] < (60, 70]]

Bemærk, at når du ser på kolonneværdierne, er der også en linje, der angiver, at vi har 6 kategorier og viser alle de indskrevne dataintervaller. På denne måde har vi kategoriseret vores tidligere numeriske data og oprettet en ny Age Groups funktion.

Og hvor mange kunder har vi i hver kategori? Det kan vi hurtigt vide ved at gruppere kolonnen og tælle værdierne med groupby() , count():

customer_data.groupby('Age Groups')['Age Groups'].count()

Dette resulterer i:

Age Groups
(15, 20]    17
(20, 30]    45
(30, 40]    60
(40, 50]    38
(50, 60]    23
(60, 70]    17
Name: Age Groups, dtype: int64

Det er let at få øje på, at de fleste kunder er mellem 30 og 40 år, efterfulgt af kunder mellem 20 og 30 og derefter kunder mellem 40 og 50. Det er også god information til Marketingafdelingen.

I øjeblikket har vi to kategoriske variable, Age , Genre, som vi skal transformere til tal for at kunne bruge i vores model. Der er mange forskellige måder at lave den transformation på – vi vil bruge pandaerne get_dummies() metode, der opretter en ny kolonne for hvert interval og genre og derefter udfylder dens værdier med 0'er og 1'er - denne form for operation kaldes one-hot-kodning. Lad os se, hvordan det ser ud:


customer_data_oh = pd.get_dummies(customer_data)

customer_data_oh 

Dette vil give os en forhåndsvisning af den resulterende tabel:

img

Med output er det let at se, at kolonnen Genre blev opdelt i kolonner - Genre_Female , Genre_Male. Når kunden er kvinde, Genre_Female er lig med 1, og når kunden er mand, er det lig 0.

Også den Age Groups kolonne blev opdelt i 6 kolonner, en for hvert interval, som f.eks Age Groups_(15, 20], Age Groups_(20, 30], og så videre. På samme måde som Genre, når kunden er fyldt 18 år, den Age Groups_(15, 20] Værdien er 1 og værdien af ​​alle andre kolonner er 0.

fordel af one-hot-kodning er enkelheden i at repræsentere kolonneværdierne, det er ligetil at forstå, hvad der sker – mens ulempe er, at vi nu har oprettet 8 ekstra kolonner, for at opsummere med de kolonner, vi allerede havde.

Advarsel: Hvis du har et datasæt, hvor antallet af one-hot-kodede kolonner overstiger antallet af rækker, er det bedst at anvende en anden kodningsmetode for at undgå problemer med datadimensionalitet.

One-hot-kodning tilføjer også 0'er til vores data, hvilket gør dem mere sparsomme, hvilket kan være et problem for nogle algoritmer, der er følsomme over for datasparhed.

Til vores klyngebehov ser one-hot-kodning ud til at fungere. Men vi kan plotte dataene for at se, om der virkelig er forskellige grupper, som vi kan gruppere.

Grundlæggende plotning og dimensionsreduktion

Vores datasæt har 11 kolonner, og der er nogle måder, hvorpå vi kan visualisere disse data. Den første er ved at plotte den i 10-dimensioner (held og lykke med det). Ti fordi Customer_ID kolonne tages ikke i betragtning. Den anden er ved at plotte vores oprindelige numeriske træk, og den tredje er ved at transformere vores 10 træk til 2 - derfor udfører vi en dimensionsreduktion.

Plot hvert datapar

Da det er lidt umuligt at plotte 10 dimensioner, vælger vi at gå med den anden tilgang – vi plotter vores indledende funktioner. Vi kan vælge to af dem til vores klyngeanalyse. En måde, vi kan se alle vores datapar kombineret på, er med en Seaborn pairplot():

import seaborn as sns


customer_data = customer_data.drop('CustomerID', axis=1)

sns.pairplot(customer_data)

Hvilket viser:

img

På et øjeblik kan vi få øje på de spredningsdiagrammer, der ser ud til at have grupper af data. En, der virker interessant, er scatterplot, der kombinerer Annual Income , Spending Score. Bemærk, at der ikke er nogen klar adskillelse mellem andre variable scatterplots. Højst kan vi måske fortælle, at der er to forskellige koncentrationer af punkter i Spending Score vs Age scatterplot.

Begge scatterplotter bestående af Annual Income , Spending Score er i det væsentlige de samme. Vi kan se det to gange, fordi x- og y-aksen blev udvekslet. Ved at tage et kig på nogen af ​​dem kan vi se, hvad der ser ud til at være fem forskellige grupper. Lad os plotte lige disse to funktioner med en Seaborn scatterplot() for at se nærmere:

sns.scatterplot(x=customer_data['Annual Income (k$)'],
                y=customer_data['Spending Score (1-100)'])

img

Ved at se nærmere, kan vi helt sikkert skelne mellem 5 forskellige grupper af data. Det ser ud til, at vores kunder kan grupperes baseret på, hvor meget de tjener på et år, og hvor meget de bruger. Dette er endnu et relevant punkt i vores analyse. Det er vigtigt, at vi kun tager to funktioner i betragtning for at gruppere vores kunder. Enhver anden information, vi har om dem, indgår ikke i ligningen. Dette giver analysen mening – hvis vi ved, hvor meget en kunde tjener og bruger, kan vi nemt finde de ligheder, vi har brug for.

img

Det er fantastisk! Indtil videre har vi allerede to variabler til at bygge vores model. Udover hvad dette repræsenterer, gør det også modellen enklere, sparsommelig og mere forklarlig.

Tjek vores praktiske, praktiske guide til at lære Git, med bedste praksis, brancheaccepterede standarder og inkluderet snydeark. Stop med at google Git-kommandoer og faktisk lærer det!

Bemærk: Data Science foretrækker normalt så enkle tilgange som muligt. Ikke kun fordi det er nemmere at forklare for virksomheden, men også fordi det er mere direkte – med 2 funktioner og en forklarlig model er det tydeligt, hvad modellen laver, og hvordan den fungerer.

Plotning af data efter brug af PCA

Det ser ud til, at vores anden tilgang nok er den bedste, men lad os også tage et kig på vores tredje tilgang. Det kan være nyttigt, når vi ikke kan plotte dataene, fordi de har for mange dimensioner, eller når der ikke er nogen datakoncentrationer eller tydelig adskillelse i grupper. Når disse situationer opstår, anbefales det at prøve at reducere datadimensioner med en metode kaldet Principal Component Analysis (PCA).

Bemærk: De fleste mennesker bruger PCA til dimensionalitetsreduktion før visualisering. Der er andre metoder, der hjælper med datavisualisering forud for clustering, som f.eks Tæthedsbaseret rumlig klyngning af applikationer med støj (DBSCAN) , Selvorganiserende kort (SOM) klyngedannelse. Begge er klyngealgoritmer, men kan også bruges til datavisualisering. Da klyngeanalyse ikke har nogen gylden standard, er det vigtigt at sammenligne forskellige visualiseringer og forskellige algoritmer.

PCA vil reducere dimensionerne af vores data og samtidig forsøge at bevare så meget af dets information som muligt. Lad os først få en idé om, hvordan PCA fungerer, og derefter kan vi vælge, hvor mange datadimensioner vi vil reducere vores data til.

For hvert par funktioner ser PCA, om de større værdier af en variabel svarer til de større værdier af den anden variabel, og det gør det samme for de mindre værdier. Så det beregner i det væsentlige, hvor meget funktionsværdierne varierer i forhold til hinanden - vi kalder det deres kovarians. Disse resultater organiseres derefter i en matrix, der opnår en kovariansmatrix.

After getting the covariance matrix, PCA tries to find a linear combination of features that best explains it – it fits linear models until it identifies the one that explains the maksimal mængden af ​​varians.

Bemærk: PCA er en lineær transformation, og linearitet er følsom over for dataskalaen. Derfor fungerer PCA bedst, når alle dataværdier er på samme skala. Dette kan gøres ved at trække kolonnen fra betyde fra dets værdier og dividere resultatet med dets standardafvigelse. Det hedder det datastandardisering. Før du bruger PCA, skal du sørge for, at dataene er skaleret! Hvis du ikke er sikker på hvordan, så læs vores "Funktionsskaleringsdata med Scikit-Learn til maskinlæring i Python"!

Med den bedste linje (lineær kombination) fundet, får PCA retningerne for sine akser, kaldet egenvektorer, og dens lineære koefficienter, den egenværdier. Kombinationen af ​​egenvektorerne og egenværdierne – eller akseretninger og koefficienter – er de Hovedkomponenter af PCA. Og det er, når vi kan vælge vores antal dimensioner baseret på den forklarede varians af hver funktion, ved at forstå, hvilke hovedkomponenter vi ønsker at beholde eller kassere baseret på hvor stor varians de forklarer.

Efter at have opnået hovedkomponenterne, bruger PCA egenvektorerne til at danne en vektor af funktioner, der omorienterer dataene fra de oprindelige akser til dem, der repræsenteres af hovedkomponenterne – det er sådan, datadimensionerne reduceres.

Bemærk: En vigtig detalje at tage i betragtning her er, at PCA på grund af sin lineære natur vil koncentrere det meste af den forklarede varians i de første hovedkomponenter. Så når man ser på den forklarede varians, vil vores første to komponenter normalt være tilstrækkelige. Men det kan være misvisende i nogle tilfælde - så prøv at blive ved med at sammenligne forskellige plots og algoritmer, når du grupperer for at se, om de har lignende resultater.

Før vi anvender PCA, skal vi vælge mellem Age kolonne eller Age Groups kolonner i vores tidligere one-hot-kodede data. Da begge kolonner repræsenterer den samme information, påvirker vores datavarians at introducere dem to gange. Hvis Age Groups kolonne er valgt, skal du blot fjerne Age kolonne ved hjælp af pandaerne drop() metode og gentildele den til customer_data_oh variabel:

customer_data_oh = customer_data_oh.drop(['Age'], axis=1)
customer_data_oh.shape 

Nu har vores data 10 kolonner, hvilket betyder, at vi kan få en hovedkomponent for kolonne og vælge, hvor mange af dem vi vil bruge, ved at måle, hvor meget introduktion af en ny dimension forklarer mere af vores datavarians.

Lad os gøre det med Scikit-Learn PCA. Vi vil beregne den forklarede varians for hver dimension, givet af explained_variance_ratio_ , og se så på deres kumulative sum med cumsum() :

from sklearn.decomposition import PCA

pca = PCA(n_components=10)
pca.fit_transform(customer_data_oh)
pca.explained_variance_ratio_.cumsum()

Vores kumulative forklarede afvigelser er:

array([0.509337  , 0.99909504, 0.99946364, 0.99965506, 0.99977937,
       0.99986848, 0.99993716, 1.        , 1.        , 1.        ])

Vi kan se, at den første dimension forklarer 50 % af dataene, og når de kombineres med den anden dimension, forklarer de 99 %. Det betyder, at de første 2 dimensioner allerede forklarer 99 % af vores data. Så vi kan anvende en PCA med 2 komponenter, få vores hovedkomponenter og plotte dem:

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
pcs = pca.fit_transform(customer_data_oh)

pc1_values = pcs[:,0]
pc2_values = pcs[:,1]
sns.scatterplot(x=pc1_values, y=pc2_values)

img

Dataplotten efter PCA ligner meget plottet, der kun bruger to kolonner af dataene uden PCA. Bemærk, at de punkter, der danner grupper, er tættere og lidt mere koncentrerede efter PCA end før.

img

Visualisering af hierarkisk struktur med dendrogrammer

Indtil videre har vi udforsket dataene, one-hot-kodede kategoriske kolonner, besluttet, hvilke kolonner der var egnet til klyngedannelse, og reduceret datadimensionalitet. Plottene indikerer, at vi har 5 klynger i vores data, men der er også en anden måde at visualisere forholdet mellem vores punkter og hjælpe med at bestemme antallet af klynger – ved at oprette en dendrogram (almindeligvis stavet forkert som dendogram). dendro midler træ på latin.

dendrogram er et resultat af sammenkædningen af ​​punkter i et datasæt. Det er en visuel repræsentation af den hierarkiske klyngeproces. Og hvordan fungerer den hierarkiske klyngeproces? Nå ... det afhænger af - sandsynligvis et svar, du allerede har hørt meget i Data Science.

Forståelse af hierarkisk klyngedannelse

Når Hierarkisk klyngealgoritme (HCA) begynder at forbinde punkterne og finde klynger, kan den først opdele punkter i 2 store grupper og derefter opdele hver af disse to grupper i mindre 2 grupper, der har 4 grupper i alt, hvilket er splittende , top-down nærme sig.

Alternativt kan den gøre det modsatte – den kan se på alle datapunkterne, finde 2 punkter, der er tættere på hinanden, sammenkæde dem og derefter finde andre punkter, der er tættest på de forbundne punkter og blive ved med at bygge de 2 grupper fra bunden i vejret. Hvilket er agglomerativt tilgang vi vil udvikle.

Trin til at udføre agglomerativ hierarkisk klyngedannelse

For at gøre den agglomerative tilgang endnu tydelig, er der trin i Agglomerative Hierarchical Clustering (AHC) algoritme:

  1. I starten skal du behandle hvert datapunkt som én klynge. Derfor vil antallet af klynger ved starten være K – mens K er et heltal, der repræsenterer antallet af datapunkter.
  2. Dann en klynge ved at forbinde de to nærmeste datapunkter, hvilket resulterer i K-1-klynger.
  3. Dann flere klynger ved at forbinde de to nærmeste klynger, hvilket resulterer i K-2 klynger.
  4. Gentag ovenstående tre trin, indtil der er dannet en stor klynge.

Bemærk: For forenklings skyld siger vi "to nærmeste" datapunkter i trin 2 og 3. Men der er flere måder at forbinde punkter på, som vi vil se om lidt.

If you invert the steps of the ACH algorithm, going from 4 to 1 – those would be the steps to *Divisive Hierarchical Clustering (DHC)*.

Bemærk, at HCA'er kan være enten opsplittende og top-down, eller agglomerative og bottom-up. Top-down DHC-tilgangen fungerer bedst, når du har færre, men større klynger, og derfor er den dyrere beregningsmæssigt. På den anden side er bottom-up AHC-tilgangen tilpasset, når du har mange mindre klynger. Det er beregningsmæssigt enklere, mere brugt og mere tilgængeligt.

Bemærk: Enten top-down eller bottom-up, vil dendrogramrepræsentationen af ​​klyngeprocessen altid starte med en opdeling i to og ende med at hvert enkelt punkt skelnes, når dets underliggende struktur er af et binært træ.

Lad os plotte vores kundedatadendrogram for at visualisere de hierarkiske relationer mellem dataene. Denne gang vil vi bruge scipy bibliotek for at oprette dendrogrammet til vores datasæt:

import scipy.cluster.hierarchy as shc
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 7))
plt.title("Customers Dendrogram")


selected_data = customer_data_oh.iloc[:, 1:3]
clusters = shc.linkage(selected_data, 
            method='ward', 
            metric="euclidean")
shc.dendrogram(Z=clusters)
plt.show()

Outputtet af scriptet ser sådan ud:

img

I scriptet ovenfor har vi genereret klynger og underklynger med vores punkter, defineret, hvordan vores punkter vil linke (ved at anvende ward metode), og hvordan man måler afstanden mellem punkter (ved at bruge euclidean metrisk).

Med plottet af dendrogrammet kan de beskrevne processer af DHC og AHC visualiseres. For at visualisere top-down-tilgangen, start fra toppen af ​​dendrogrammet og gå ned, og gør det modsatte, start ned og bevæg dig opad for at visualisere bottom-up-tilgangen.

Sammenkoblingsmetoder

Der er mange andre koblingsmetoder, ved at forstå mere om, hvordan de virker, vil du være i stand til at vælge den passende til dine behov. Udover det vil hver af dem give forskellige resultater, når de anvendes. Der er ikke en fast regel i klyngeanalyse, hvis det er muligt, undersøg problemets art for at se, hvilken der passer bedst, test forskellige metoder og inspicér resultaterne.

Nogle af koblingsmetoderne er:

  • Enkelt kobling: også omtalt som Nærmeste nabo (NN). Afstanden mellem klynger er defineret af afstanden mellem deres nærmeste medlemmer.

img

  • Fuldstændig kobling: også omtalt som Fjerneste nabo (FN), Algoritme for det fjerneste punkt eller Voor Hees algoritme. Afstanden mellem klynger er defineret af afstanden mellem deres fjerneste medlemmer. Denne metode er beregningsmæssigt dyr.

img

  • Gennemsnitlig kobling: også kendt som UPGMA (Uvægtet pargruppemetode med aritmetisk gennemsnit). Procentdelen af ​​antallet af point for hver klynge beregnes i forhold til antallet af point for de to klynger, hvis de blev slået sammen.

img

  • Vægtet kobling: også kendt som WPGMA (Vægtet pargruppemetode med aritmetisk gennemsnit). De individuelle punkter i de to klynger bidrager til den aggregerede afstand mellem en mindre og en større klynge.
  • Centroid forbindelse: også omtalt som UPGMC (Uvægtet pargruppemetode ved hjælp af tyngdepunkter). Et punkt defineret ved middelværdien af ​​alle punkter (tyngdepunkt) beregnes for hver klynge, og afstanden mellem klynger er afstanden mellem deres respektive tyngdepunkter.

img

  • Afdelingsforbindelse: Også kendt som MISSQ (Minimal stigning i kvadratsummen). Den specificerer afstanden mellem to klynger, beregner summen af ​​kvadratfejl (ESS) og vælger successivt de næste klynger baseret på den mindre ESS. Wards metode søger at minimere stigningen af ​​ESS på hvert trin. Derfor minimerer fejl.

img

Afstandsmålinger

Udover koblingen kan vi også specificere nogle af de mest brugte afstandsmålinger:

  • Euklidisk: også omtalt som Pythagoras eller lige linje afstand. Den beregner afstanden mellem to punkter i rummet ved at måle længden af ​​et linjestykke, der passerer mellem dem. Den bruger Pythagoras sætning, og afstandsværdien er resultatet (C) af ligningen:

$$
c^2 = a^2 + b^2
$$

  • Manhattan: også kaldet Byblok, Taxa afstand. Det er summen af ​​absolutte forskelle mellem målene i alle dimensioner af to punkter. Hvis disse dimensioner er to, er det analogt med at lave en højre og derefter venstre, når du går en blok.

img

  • Minkowski: det er en generalisering af både euklidiske og Manhattan-afstande. Det er en måde at beregne afstande baseret på de absolutte forskelle til rækkefølgen af ​​Minkowski-metrikken p. Selvom det er defineret for evt p> 0, bruges den sjældent til andre værdier end 1, 2 og ∞ (uendelig). Minkowski distance er den samme som Manhattan distance hvornår p = 1, og det samme som euklidisk afstand når p = 2.

$$
Dleft(X,Yright) = left(sum_{i=1}^n |x_i-y_i|^pright)^{frac{1}{p}}
$$

img

  • Chebyshev: også kendt som Chessboard (Skakbræt) afstand. Det er det ekstreme tilfælde af Minkowski-afstand. Når vi bruger uendelighed som værdien af ​​parameteren p (p = ∞), ender vi med en metrik, der definerer afstand som den maksimale absolutte forskel mellem koordinaterne.
  • cosinus: det er vinkelcosinusafstanden mellem to sekvenser af punkter eller vektorer. Cosinus-ligheden er prikproduktet af vektorerne divideret med produktet af deres længder.
  • Jaccard: måler ligheden mellem endelige sæt af punkter. Det er defineret som det samlede antal point (kardinalitet) i de fælles punkter i hvert sæt (skæring), divideret med det samlede antal point (kardinalitet) af de samlede point i begge sæt (union).
  • Jensen-Shannon: baseret på Kullback-Leibler divergensen. Den betragter punkternes sandsynlighedsfordelinger og måler ligheden mellem disse fordelinger. Det er en populær metode til sandsynlighedsteori og statistik.

Vi har valgt Afdeling , Euklidisk for dendrogrammet, fordi de er den mest anvendte metode og metriske. De giver normalt gode resultater, da Ward linker point baseret på at minimere fejlene, og Euclide fungerer godt i lavere dimensioner.

I dette eksempel arbejder vi med to funktioner (kolonner) af marketingdataene og 200 observationer eller rækker. Da antallet af observationer er større end antallet af funktioner (200 > 2), arbejder vi i et lavdimensionelt rum.

Når antallet af funktioner (F) er større end antallet af observationer (N) – for det meste skrevet som f >> N, betyder det, at vi har en højdimensionelt rum.

Hvis vi skulle inkludere flere attributter, så vi har mere end 200 funktioner, ville den euklidiske afstand måske ikke fungere særlig godt, da den ville have svært ved at måle alle de små afstande i et meget stort rum, der kun bliver større. Med andre ord har den euklidiske afstandstilgang svært ved at arbejde med dataene sparsomhed. Dette er et problem, der kaldes dimensionalitetens forbandelse. Afstandsværdierne ville blive så små, som hvis de blev "fortyndet" i det større rum, forvrænget, indtil de blev 0.

Bemærk: Hvis du nogensinde støder på et datasæt med f >> s, vil du sandsynligvis bruge andre afstandsmålinger, såsom Mahalanobis afstand. Alternativt kan du også reducere datasættets dimensioner ved at bruge Principal Component Analysis (PCA). Dette problem er hyppigt, især når der klynges biologiske sekventeringsdata.

Vi har allerede diskuteret metrics, koblinger, og hvordan hver enkelt af dem kan påvirke vores resultater. Lad os nu fortsætte dendrogramanalysen og se, hvordan den kan give os en indikation af antallet af klynger i vores datasæt.

At finde et interessant antal klynger i et dendrogram er det samme som at finde det største vandrette rum, der ikke har nogen lodrette linjer (rummet med de længste lodrette linjer). Det betyder, at der er mere adskillelse mellem klyngerne.

Vi kan tegne en vandret linje, der går gennem den længste afstand:

plt.figure(figsize=(10, 7))
plt.title("Customers Dendogram with line")
clusters = shc.linkage(selected_data, 
            method='ward', 
            metric="euclidean")
shc.dendrogram(clusters)
plt.axhline(y = 125, color = 'r', linestyle = '-')

img

Efter at have fundet den vandrette linje, tæller vi, hvor mange gange vores lodrette linjer blev krydset af den – i dette eksempel 5 gange. Så 5 virker som en god indikation af antallet af klynger, der har størst afstand imellem sig.

Bemærk: Dendrogrammet bør kun betragtes som en reference, når det bruges til at vælge antallet af klynger. Det kan nemt få det nummer væk og er fuldstændig påvirket af typen af ​​forbindelse og afstandsmålinger. Når du udfører en dybdegående klyngeanalyse, anbefales det at se på dendrogrammer med forskellige koblinger og metrikker og at se på resultaterne, der er genereret med de første tre linjer, hvor klyngerne har størst afstand mellem dem.

Implementering af en agglomerativ hierarkisk klyngedannelse

Brug af originale data

So far we’ve calculated the suggested number of clusters for our dataset that corroborate with our initial analysis and our PCA analysis. Now we can create our agglomerative hierarchical clustering model using Scikit-Learn AgglomerativeClustering og find ud af etiketterne for marketingpunkter med labels_:

from sklearn.cluster import AgglomerativeClustering

clustering_model = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')
clustering_model.fit(selected_data)
clustering_model.labels_

Dette resulterer i:

array([4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3,
       4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 1,
       4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 0, 2, 0, 2,
       1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 2, 1, 2, 0, 2, 0, 2, 0, 2,
       0, 2, 0, 2, 0, 2, 1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
       0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
       0, 2])

Vi har undersøgt meget for at komme til dette punkt. Og hvad betyder disse etiketter? Her har vi hvert punkt i vores data mærket som en gruppe fra 0 til 4:

data_labels = clustering_model.labels_
sns.scatterplot(x='Annual Income (k$)', 
                y='Spending Score (1-100)', 
                data=selected_data, 
                hue=data_labels,
                pallete="rainbow").set_title('Labeled Customer Data')

img

Dette er vores endelige klyngedata. Du kan se de farvekodede datapunkter i form af fem klynger.

Datapunkterne nederst til højre (etiket: 0, lilla datapunkter) tilhører kunder med høje lønninger, men lavt forbrug. Det er de kunder, der bruger deres penge omhyggeligt.

Tilsvarende kunderne øverst til højre (label: 2, grønne datapunkter), er kunderne med høje lønninger og højt forbrug. Det er den type kunder, som virksomheder retter sig mod.

Kunderne i midten (label: 1, blå datapunkter) er dem med gennemsnitlig indkomst og gennemsnitligt forbrug. Det højeste antal kunder tilhører denne kategori. Virksomheder kan også målrette mod disse kunder i betragtning af, at de er i et stort antal.

Kunderne nederst til venstre (label: 4, red) er de kunder, der har lave lønninger og lavt forbrug, kan de tiltrækkes ved at tilbyde kampagner.

Og endelig kunderne øverst til venstre (label: 3, orange datapunkter) er dem med høj indkomst og lavt forbrug, som er ideelt målrettet af markedsføring.

Brug af resultatet fra PCA

Hvis vi var i et andet scenarie, hvor vi var nødt til at reducere dimensionaliteten af ​​data. Vi kunne også nemt plotte de klyngede PCA-resultater. Det kan gøres ved at oprette en anden agglomerativ klyngemodel og opnå en dataetiket for hver hovedkomponent:

clustering_model_pca = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')
clustering_model_pca.fit(pcs)

data_labels_pca = clustering_model_pca.labels_

sns.scatterplot(x=pc1_values, 
                y=pc2_values,
                hue=data_labels_pca,
                palette="rainbow").set_title('Labeled Customer Data Reduced with PCA')

img

Bemærk, at begge resultater er meget ens. Den største forskel er, at det første resultat med de originale data er meget lettere at forklare. Det er tydeligt at se, at kunderne kan opdeles i fem grupper efter deres årlige indkomst og forbrugsscore. Mens vi i PCA-tilgangen tager alle vores funktioner i betragtning, så meget som vi kan se på variansen forklaret af hver af dem, er dette et sværere koncept at forstå, især når du rapporterer til en marketingafdeling.

Jo mindst vi har for at transformere vores data, jo bedre.

Hvis du har et meget stort og komplekst datasæt, hvor du skal udføre en dimensionsreduktion før klyngedannelse – prøv at analysere de lineære relationer mellem hver af funktionerne og deres residualer for at sikkerhedskopiere brugen af ​​PCA og forbedre processens forklarlighed. Ved at lave en lineær model pr. funktionspar vil du være i stand til at forstå, hvordan funktionerne interagerer.

Hvis datamængden er så stor, bliver det umuligt at plotte funktionsparrene, vælge en prøve af dine data, så afbalanceret og tæt på normalfordelingen som muligt og udføre analysen på prøven først, forstå den, finjustere det – og anvende det senere på hele datasættet.

Du kan altid vælge forskellige klyngevisualiseringsteknikker i henhold til arten af ​​dine data (lineær, ikke-lineær) og kombinere eller teste dem alle, hvis det er nødvendigt.

Konklusion

Klyngeteknikken kan være meget praktisk, når det kommer til umærkede data. Da de fleste af dataene i den virkelige verden er umærkede og annotering af data har højere omkostninger, kan klyngeteknikker bruges til at mærke umærkede data.

I denne guide har vi bragt et reelt datavidenskabeligt problem, da klyngeteknikker i høj grad bruges i marketinganalyse (og også i biologisk analyse). Vi har også forklaret mange af undersøgelsestrinene for at komme til en god hierarkisk klyngemodel, og hvordan man læser dendrogrammer og stillet spørgsmålstegn ved, om PCA er et nødvendigt trin. Vores hovedformål er, at nogle af de faldgruber og forskellige scenarier, hvor vi kan finde hierarkisk klyngedannelse, er dækket.

Glædelig klyngedannelse!

Tidsstempel:

Mere fra Stablemisbrug