Definitiv guide till hierarkisk klustring med Python och Scikit-Learn PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

Definitiv guide till hierarkisk klustring med Python och Scikit-Learn

Beskrivning

I den här guiden kommer vi att fokusera på att implementera Hierarkisk klustringsalgoritm med Scikit-Learn för att lösa ett marknadsföringsproblem.

Efter att ha läst guiden kommer du att förstå:

  • När ska man tillämpa hierarkisk klustering
  • Hur man visualiserar datasetet för att förstå om det är lämpligt för klustring
  • Hur man förbearbetar funktioner och konstruerar nya funktioner baserat på datamängden
  • Hur man minskar dimensionaliteten hos datamängden med PCA
  • Hur man använder och läser ett dendrogram för att separera grupper
  • Vilka är de olika länkmetoderna och avståndsmåtten som tillämpas på dendrogram och klustringsalgoritmer
  • Vilka är de agglomerativa och splittande klustringsstrategierna och hur de fungerar
  • Hur man implementerar den agglomerativa hierarkiska klustringen med Scikit-Learn
  • Vilka är de vanligaste problemen när man hanterar klustringsalgoritmer och hur man löser dem

Notera: Du kan ladda ner anteckningsboken som innehåller all kod i den här guiden här..

Motivation

Föreställ dig ett scenario där du är en del av ett datavetenskapsteam som samverkar med marknadsavdelningen. Marknadsföring har samlat in kundinköpsdata ett tag, och de vill förstå, baserat på den insamlade informationen, om det finns likheter mellan kunder. Dessa likheter delar in kunder i grupper och att ha kundgrupper hjälper till att rikta in kampanjer, kampanjer, omvandlingar och bygga bättre kundrelationer.

Finns det något sätt du kan hjälpa till att avgöra vilka kunder som är lika? Hur många av dem tillhör samma grupp? Och hur många olika grupper finns det?

Ett sätt att besvara dessa frågor är att använda a klustring algoritmer, såsom K-Means, DBSCAN, Hierarchical Clustering, etc. Generellt sett hittar klustringsalgoritmer likheter mellan datapunkter och grupperar dem.

I det här fallet är vår marknadsföringsdata ganska liten. Vi har information om endast 200 kunder. Med tanke på marknadsföringsteamet är det viktigt att vi tydligt kan förklara för dem hur besluten togs baserat på antalet kluster, och därför förklara för dem hur algoritmen faktiskt fungerar.

Eftersom vår data är liten och förklaring är en viktig faktor, kan vi dra nytta av Hierarkisk klustring för att lösa det här problemet. Denna process är också känd som Hierarkisk klusteranalys (HCA).

En av fördelarna med HCA är att den är tolkningsbar och fungerar bra på små datamängder.

En annan sak att ta hänsyn till i detta scenario är att HCA är en oövervakad algoritm. När vi grupperar data kommer vi inte att ha något sätt att verifiera att vi korrekt identifierar att en användare tillhör en specifik grupp (vi känner inte till grupperna). Det finns inga etiketter för oss att jämföra våra resultat med. Om vi ​​identifierade grupperna korrekt kommer det senare att bekräftas av marknadsavdelningen på en daglig basis (mätt med mätvärden som ROI, omvandlingsfrekvenser, etc.).

Nu när vi har förstått problemet vi försöker lösa och hur vi ska lösa det, kan vi börja ta en titt på vår data!

Kort utforskande dataanalys

Notera: Du kan ladda ner datauppsättningen som används i den här guiden här..

Efter att ha laddat ner datauppsättningen, lägg märke till att det är en CSV (kommaseparerade värden) fil som heter shopping-data.csv. För att göra det lättare att utforska och manipulera data laddar vi in ​​dem i en DataFrame använder pandor:

import pandas as pd


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

Marknadsföring sa att den hade samlat in 200 kundregister. Vi kan kontrollera om den nedladdade datan är komplett med 200 rader med hjälp av shape attribut. Det kommer att berätta hur många rader och kolumner vi har, respektive:

customer_data.shape

Detta resulterar i:

(200, 5)

Bra! Vår data är komplett med 200 rader (klientregister) och vi har också 5 kolumner (funktioner). För att se vilka egenskaper marknadsavdelningen har samlat in från kunder kan vi se kolumnnamn med columns attribut. För att göra det, kör:

customer_data.columns

Skriptet ovan returnerar:

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

Här ser vi att marknadsföring har genererat en CustomerID, samlade Genre, Age, Annual Income (i tusentals dollar), och en Spending Score går från 1 till 100 för var och en av de 200 kunderna. På frågan om ett förtydligande sa de att värdena i Spending Score kolumnen anger hur ofta en person spenderar pengar i en galleria på en skala från 1 till 100. Med andra ord, om en kund har poängen 0, spenderar den här personen aldrig pengar, och om poängen är 100, har vi precis sett högsta spenderaren.

Låt oss ta en snabb titt på fördelningen av denna poäng för att inspektera användarnas utgiftsvanor i vår datauppsättning. Det är där pandorna hist() metod kommer in för att hjälpa:

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

img

Genom att titta på histogrammet ser vi att fler än 35 kunder har poäng mellan 40 och 60, då har färre än 25 poäng mellan 70 och 80. Så de flesta av våra kunder är det balanserade spenderande, följt av måttliga till höga spenderare. Vi kan också se att det finns en linje efter 0, till vänster om distributionen, och en annan rad före 100, till höger om distributionen. Dessa tomma utrymmen betyder förmodligen att distributionen inte innehåller icke-spenders, vilket skulle ha en poäng på 0, och att det inte heller finns några högutgifter med poäng på 100.

För att verifiera om det är sant kan vi titta på fördelningens lägsta och högsta värden. Dessa värden kan lätt hittas som en del av den beskrivande statistiken, så vi kan använda describe() metod för att få en förståelse för andra numeriska värdefördelningar:


customer_data.describe().transpose()

Detta kommer att ge oss en tabell där vi kan läsa distributioner av andra värden i vår datauppsättning:

 						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

Vår hypotes bekräftas. De min värdet på Spending Score is 1 och max är 99. Så det har vi inte 0 or 100 poäng spenders. Låt oss sedan ta en titt på de andra kolumnerna i den transponerade describe tabell. När man tittar på mean och std kolumner kan vi se det för Age d mean is 38.85 och std är approximativt 13.97. Samma sak händer för Annual Income, Med en mean of 60.56 och std 26.26Och för Spending Score med en mean of 50 och std of 25.82. För alla funktioner mean är långt ifrån standardavvikelsen, vilket indikerar vår data har hög variation.

För att bättre förstå hur vår data varierar, låt oss plotta Annual Income distribution:

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

Vilket ger oss:

img

Lägg märke till i histogrammet att de flesta av våra data, mer än 35 kunder, är koncentrerad nära numret 60, på vår mean, i den horisontella axeln. Men vad händer när vi går mot slutet av distributionen? När vi går åt vänster, från $60.560 medelvärdet, är nästa värde vi kommer att stöta på $34.300 – medelvärdet ($60.560) minus standardvariationen ($26.260). Om vi ​​går längre bort till vänster om vår datadistribution gäller en liknande regel, vi subtraherar standardvariationen ($26.260) från det aktuella värdet ($34.300). Därför kommer vi att stöta på ett värde på $8.040. Lägg märke till hur vår data snabbt gick från $60k till $8k. Det "hoppar" $26.260 varje gång – varierar mycket, och det är därför vi har så stor variation.

img

Variabiliteten och storleken på data är viktiga vid klustringsanalys eftersom avståndsmätningar av de flesta klustringsalgoritmer är känsliga för datastorlekar. Skillnaden i storlek kan förändra klustringsresultaten genom att få en punkt att verka närmare eller mer avlägsen en annan än den faktiskt är, vilket förvränger den faktiska grupperingen av data.

Hittills har vi sett formen på vår data, en del av dess distributioner och beskrivande statistik. Med Pandas kan vi också lista våra datatyper och se om alla våra 200 rader är fyllda eller har några null värden:

customer_data.info()

Detta resulterar 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

Här kan vi se att det inte finns några null värden i datan och att vi bara har en kategorisk kolumn – Genre. I detta skede är det viktigt att vi har i åtanke vilka funktioner som verkar intressanta att lägga till i klustringsmodellen. Om vi ​​vill lägga till Genre-kolumnen till vår modell måste vi omvandla dess värden från kategorisk till numerisk.

Låt oss se hur Genre fylls genom att ta en snabb titt på de första 5 värdena av vår data:

customer_data.head() 

Detta resulterar 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 verkar som att det bara har Female och Male kategorier. Det kan vi vara säkra på genom att ta en titt på dess unika värden med unique:

customer_data['Genre'].unique()

Detta bekräftar vårt antagande:

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

Hittills vet vi att vi bara har två genrer, om vi planerar att använda den här funktionen på vår modell, Male skulle kunna omvandlas till 0 och Female till 1. Det är också viktigt att kontrollera proportionen mellan genrer, för att se om de är balanserade. Det kan vi göra med value_counts() metod och dess argument normalize=True för att visa procenten mellan Male och Female:

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

Denna utgångar:

Female    0.56
Male      0.44
Name: Genre, dtype: float64

Vi har 56 % av kvinnorna i datasetet och 44 % av männen. Skillnaden mellan dem är bara 16 %, och vår data är inte 50/50 utan är det tillräckligt balanserad att inte orsaka några problem. Om resultaten var 70/30, 60/40, kan det ha behövts antingen att samla in mer data eller att använda någon form av dataökningsteknik för att göra förhållandet mer balanserat.

Fram till nu, alla funktioner men Age, har undersökts kort. I vilka bekymmer Age, brukar det vara intressant att dela upp det i papperskorgar för att kunna segmentera kunder utifrån deras åldersgrupper. Om vi ​​gör det skulle vi behöva omvandla ålderskategorierna till ett nummer innan vi lägger till dem i vår modell. På så sätt, istället för att använda kategorin 15-20 år, skulle vi räkna hur många kunder det finns i 15-20 kategori, och det skulle vara ett nummer i en ny kolumn som heter 15-20.

Råd: I den här guiden presenterar vi endast kortfattad utforskande dataanalys. Men du kan gå längre och du bör gå längre. Du kan se om det finns inkomstskillnader och poängskillnader baserat på genre och ålder. Detta berikar inte bara analysen utan leder till bättre modellresultat. För att gå djupare in i Exploratory Data Analysis, kolla in EDA-kapitlet i "Hands-on husprisförutsägelse – maskininlärning i Python" Guidade projekt.

Efter att ha gissat om vad som skulle kunna göras med både kategorisk – eller kategorisk att vara – Genre och Age kolumner, låt oss tillämpa det som har diskuterats.

Kodningsvariabler och funktionsteknik

Låt oss börja med att dividera Age i grupper som varierar i 10, så att vi har 20-30, 30-40, 40-50, och så vidare. Eftersom vår yngsta kund är 15 år kan vi börja vid 15 och sluta vid 70, vilket är åldern på den äldsta kunden i datan. Från och med 15 och slutar vid 70, skulle vi ha 15-20, 20-30, 30-40, 40-50, 50-60 och 60-70 intervaller.

Att gruppera eller bin Age värden i dessa intervaller, kan vi använda Pandas cut() metod för att skära dem i papperskorgar och sedan tilldela lådorna till en ny Age Groups kolumn:

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'] 

Detta resulterar 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]]

Lägg märke till att när du tittar på kolumnvärdena, finns det också en rad som anger att vi har 6 kategorier och visar alla dataintervaller som lagrats. På så sätt har vi kategoriserat våra tidigare numeriska data och skapat en ny Age Groups särdrag.

Och hur många kunder har vi i varje kategori? Det kan vi snabbt veta genom att gruppera kolumnen och räkna värdena med groupby() och count():

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

Detta resulterar 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 är lätt att upptäcka att de flesta kunder är mellan 30 och 40 år, följt av kunder mellan 20 och 30 och sedan kunder mellan 40 och 50. Detta är också bra information för Marknadsavdelningen.

För närvarande har vi två kategoriska variabler, Age och Genre, som vi behöver omvandla till siffror för att kunna använda i vår modell. Det finns många olika sätt att göra den transformationen – vi kommer att använda pandorna get_dummies() metod som skapar en ny kolumn för varje intervall och genre och sedan fyller dess värden med 0:or och 1:or – denna typ av operation kallas en-het kodning. Låt oss se hur det ser ut:


customer_data_oh = pd.get_dummies(customer_data)

customer_data_oh 

Detta kommer att ge oss en förhandsvisning av den resulterande tabellen:

img

Med utgången är det lätt att se att kolumnen Genre delades upp i kolumner - Genre_Female och Genre_Male. När kunden är kvinna, Genre_Female är lika med 1, och när kunden är man är det lika 0.

Också, den Age Groups kolumn delades upp i 6 kolumner, en för varje intervall, som t.ex Age Groups_(15, 20], Age Groups_(20, 30], och så vidare. På samma sätt som Genre, när kunden är 18 år, den Age Groups_(15, 20] värdet är 1 och värdet på alla andra kolumner är 0.

Smakämnen Fördelen av one-hot-kodning är enkelheten i att representera kolumnvärdena, det är enkelt att förstå vad som händer – medan nackdel är att vi nu har skapat ytterligare 8 kolumner, för att summera med de kolumner vi redan hade.

Varning: Om du har en datauppsättning där antalet kodade kolumner överstiger antalet rader, är det bäst att använda en annan kodningsmetod för att undvika problem med datadimensionalitet.

One-hot-kodning lägger också till nollor till vår data, vilket gör den glesare, vilket kan vara ett problem för vissa algoritmer som är känsliga för datasparsitet.

För våra klustringsbehov verkar one-hot-kodning fungera. Men vi kan plotta data för att se om det verkligen finns distinkta grupper för oss att klustera.

Grundläggande plottning och dimensionsreduktion

Vår datauppsättning har 11 kolumner, och det finns några sätt på vilka vi kan visualisera denna data. Den första är genom att plotta den i 10-dimensioner (lycka till med det). Tio eftersom Customer_ID kolumn övervägs inte. Den andra är genom att plotta våra initiala numeriska egenskaper, och den tredje är genom att omvandla våra 10 funktioner till 2 – och därför utföra en dimensionsreduktion.

Plotta varje datapar

Eftersom det är lite omöjligt att plotta 10 dimensioner, väljer vi att gå med det andra tillvägagångssättet – vi kommer att plotta våra första funktioner. Vi kan välja två av dem för vår klustringsanalys. Ett sätt vi kan se alla våra datapar kombinerade är med en Seaborn pairplot():

import seaborn as sns


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

sns.pairplot(customer_data)

Vilket visar:

img

Med ett ögonkast kan vi se spridningsdiagrammen som verkar ha grupper av data. En som verkar intressant är scatterplot som kombinerar Annual Income och Spending Score. Observera att det inte finns någon tydlig separation mellan andra variabla spridningsdiagram. Som mest kan vi kanske säga att det finns två distinkta koncentrationer av punkter i Spending Score vs Age scatterplot.

Båda scatterplots bestående av Annual Income och Spending Score är i huvudsak desamma. Vi kan se det två gånger eftersom x- och y-axeln byttes ut. Genom att ta en titt på någon av dem kan vi se vad som verkar vara fem olika grupper. Låt oss plotta bara dessa två funktioner med en Seaborn scatterplot() för att ta en närmare titt:

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

img

Genom att titta närmare kan vi definitivt urskilja 5 olika grupper av data. Det verkar som om våra kunder kan klustras baserat på hur mycket de tjänar på ett år och hur mycket de spenderar. Detta är en annan relevant punkt i vår analys. Det är viktigt att vi bara tar två funktioner i beaktande för att gruppera våra kunder. All annan information vi har om dem ingår inte i ekvationen. Detta ger analysen mening – om vi vet hur mycket en kund tjänar och spenderar kan vi enkelt hitta de likheter vi behöver.

img

Toppen! Hittills har vi redan två variabler för att bygga vår modell. Förutom vad detta representerar, gör det också modellen enklare, sparsam och mer förklarlig.

Kolla in vår praktiska, praktiska guide för att lära dig Git, med bästa praxis, branschaccepterade standarder och medföljande fuskblad. Sluta googla Git-kommandon och faktiskt lära Det!

Notera: Data Science föredrar vanligtvis så enkla metoder som möjligt. Inte bara för att det är lättare att förklara för verksamheten, utan också för att det är mer direkt – med 2 funktioner och en förklarlig modell är det tydligt vad modellen gör och hur den fungerar.

Plotta data efter användning av PCA

Det verkar som om vårt andra tillvägagångssätt förmodligen är det bästa, men låt oss också ta en titt på vårt tredje tillvägagångssätt. Det kan vara användbart när vi inte kan plotta data eftersom det har för många dimensioner, eller när det inte finns några datakoncentrationer eller tydlig separation i grupper. När dessa situationer uppstår rekommenderas det att du försöker minska datadimensionerna med en metod som kallas Huvudkomponentanalys (PCA).

Notera: De flesta använder PCA för dimensionsreduktion före visualisering. Det finns andra metoder som hjälper till vid datavisualisering inför klustring, som t.ex Densitetsbaserad rumslig klustring av applikationer med brus (DBSCAN) och Självorganiserande kartor (SOM) klustring. Båda är klustringsalgoritmer, men kan också användas för datavisualisering. Eftersom klustringsanalys inte har någon gyllene standard är det viktigt att jämföra olika visualiseringar och olika algoritmer.

PCA kommer att minska dimensionerna på vår data samtidigt som vi försöker bevara så mycket av informationen som möjligt. Låt oss först få en uppfattning om hur PCA fungerar, och sedan kan vi välja hur många datadimensioner vi ska reducera vår data till.

För varje funktionspar ser PCA om de större värdena för en variabel motsvarar de större värdena för den andra variabeln, och det gör samma sak för de lägre värdena. Så, det beräknar i huvudsak hur mycket funktionsvärdena varierar mot varandra - vi kallar det deras kovarians. Dessa resultat organiseras sedan i en matris och erhåller en kovariansmatris.

Efter att ha fått kovariansmatrisen försöker PCA hitta en linjär kombination av funktioner som bäst förklarar den – den passar linjära modeller tills den identifierar den som förklarar maximal mängden avvikelse.

Anmärkningar: PCA är en linjär transformation och linjäritet är känslig för dataskalan. Därför fungerar PCA bäst när alla datavärden är på samma skala. Detta kan göras genom att subtrahera kolumnen betyda från dess värden och dividera resultatet med dess standardavvikelse. Det kallas det datastandardisering. Innan du använder PCA, se till att data är skalad! Om du inte är säker på hur, läs vår "Funktionsskalningsdata med Scikit-Learn för maskininlärning i Python"!

Med den bästa linjen (linjär kombination) som hittats, får PCA riktningarna för sina axlar, kallad egenvektorer, och dess linjära koefficienter, den egenvärden. Kombinationen av egenvektorerna och egenvärdena – eller axelriktningar och koefficienter – är de Huvudkomponenter av PCA. Och det är då vi kan välja vårt antal dimensioner baserat på den förklarade variansen för varje funktion, genom att förstå vilka huvudkomponenter vi vill behålla eller kassera baserat på hur mycket varians de förklarar.

Efter att ha erhållit huvudkomponenterna använder PCA egenvektorerna för att bilda en vektor av funktioner som omorienterar data från de ursprungliga axlarna till de som representeras av huvudkomponenterna – det är så datadimensionerna reduceras.

Notera: En viktig detalj att ta hänsyn till här är att, på grund av sin linjära natur, kommer PCA att koncentrera det mesta av den förklarade variansen i de första huvudkomponenterna. Så när man tittar på den förklarade variansen räcker vanligtvis våra första två komponenter. Men det kan vara missvisande i vissa fall – så försök att fortsätta jämföra olika plotter och algoritmer när du klusterar för att se om de har liknande resultat.

Innan vi tillämpar PCA måste vi välja mellan Age kolumn eller Age Groups kolumner i vår tidigare one-hot-kodade data. Eftersom båda kolumnerna representerar samma information, påverkar vår datavarians att införa den två gånger. Om Age Groups kolumnen är vald, ta helt enkelt bort Age kolumn med Pandas drop() metoden och återtilldela den till customer_data_oh variabel:

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

Nu har vår data 10 kolumner, vilket innebär att vi kan få en huvudkomponent för kolumn och välja hur många av dem vi ska använda genom att mäta hur mycket införandet av en ny dimension förklarar mer av vår datavarians.

Låt oss göra det med Scikit-Learn PCA. Vi kommer att beräkna den förklarade variansen för varje dimension, givet av explained_variance_ratio_ , och titta sedan på deras ackumulerade summa med cumsum() :

from sklearn.decomposition import PCA

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

Våra kumulativa förklarade avvikelser är:

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

Vi kan se att den första dimensionen förklarar 50 % av data, och när den kombineras med den andra dimensionen förklarar de 99 %. Det betyder att de två första dimensionerna redan förklarar 2 % av vår data. Så vi kan tillämpa en PCA med 99 komponenter, erhålla våra huvudkomponenter och plotta 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

Datadiagrammet efter PCA är mycket likt plottet som endast använder två kolumner av data utan PCA. Lägg märke till att punkterna som bildar grupper är närmare och lite mer koncentrerade efter PCA än tidigare.

img

Visualisera hierarkisk struktur med dendrogram

Hittills har vi utforskat data, en-hot-kodade kategoriska kolumner, bestämt vilka kolumner som var lämpliga för klustring och minskat datadimensionalitet. Diagrammen indikerar att vi har 5 kluster i vår data, men det finns också ett annat sätt att visualisera relationerna mellan våra punkter och hjälpa till att bestämma antalet kluster – genom att skapa en dendrogram (vanligtvis felstavat som dendogram). dendro betyder träd på latin.

Smakämnen dendrogram är ett resultat av länkningen av punkter i en datauppsättning. Det är en visuell representation av den hierarkiska klustringsprocessen. Och hur fungerar den hierarkiska klustringsprocessen? Tja ... det beror på - förmodligen ett svar du redan har hört mycket inom Data Science.

Förstå hierarkisk klustring

När Hierarkisk klustringsalgoritm (HCA) börjar länka punkterna och hitta kluster, kan den först dela upp punkter i 2 stora grupper och sedan dela upp var och en av dessa två grupper i mindre 2 grupper, med totalt 4 grupper, vilket är splitt och top-down närma sig.

Alternativt kan den göra tvärtom – den kan titta på alla datapunkter, hitta 2 punkter som är närmare varandra, länka dem och sedan hitta andra punkter som ligger närmast de länkade punkterna och fortsätta bygga de 2 grupperna från botten upp. Vilken är agglomerativt tillvägagångssätt vi kommer att utveckla.

Steg för att utföra agglomerativ hierarkisk klustring

För att göra det agglomerativa tillvägagångssättet ännu tydligt finns det steg i Agglomerativ hierarkisk klustering (AHC) algoritm:

  1. Behandla varje datapunkt som ett kluster i början. Därför kommer antalet kluster i början att vara K – medan K är ett heltal som representerar antalet datapunkter.
  2. Bilda ett kluster genom att sammanfoga de två närmaste datapunkterna vilket resulterar i K-1-kluster.
  3. Bilda fler kluster genom att sammanfoga de två närmaste klustren vilket resulterar i K-2-kluster.
  4. Upprepa ovanstående tre steg tills ett stort kluster bildas.

Anmärkningar: För förenkling säger vi "två närmaste" datapunkter i steg 2 och 3. Men det finns fler sätt att länka punkter som vi kommer att se om lite.

Om du inverterar stegen i ACH-algoritmen, går från 4 till 1 – det skulle vara stegen till *Divisive Hierarchical Clustering (DHC)*.

Lägg märke till att HCA kan vara antingen splittande och uppifrån och ner, eller agglomerativa och nedifrån och upp. Top-down DHC-metoden fungerar bäst när du har färre men större kluster, därför är det dyrare beräkningsmässigt. Å andra sidan är AHC-metoden nedifrån och upp anpassad för när du har många mindre kluster. Det är beräkningsmässigt enklare, mer använt och mer tillgängligt.

Notera: Antingen uppifrån och ner eller nerifrån, kommer dendrogramrepresentationen av klustringsprocessen alltid att börja med en uppdelning i två och sluta med att varje enskild punkt urskiljs, när dess underliggande struktur är av ett binärt träd.

Låt oss plotta vårt kunddatadendrogram för att visualisera de hierarkiska relationerna mellan datan. Den här gången kommer vi att använda scipy bibliotek för att skapa dendrogrammet för vår datauppsättning:

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()

Utdata från skriptet ser ut så här:

img

I skriptet ovan har vi genererat klustren och subklustren med våra punkter, definierat hur våra punkter skulle länka (genom att tillämpa ward metod), och hur man mäter avståndet mellan punkter (genom att använda euclidean metrisk).

Med plotten av dendrogrammet kan de beskrivna processerna för DHC och AHC visualiseras. För att visualisera uppifrån och ner-tillvägagångssättet, börja från toppen av dendrogrammet och gå nedåt, och gör tvärtom, börja nedåt och rör dig uppåt för att visualisera nedifrån-och-upp-inställningen.

Länkningsmetoder

Det finns många andra länkmetoder, genom att förstå mer om hur de fungerar kommer du att kunna välja den lämpliga för dina behov. Förutom det kommer var och en av dem att ge olika resultat när de appliceras. Det finns ingen fast regel i klustringsanalys, studera om möjligt problemets natur för att se vilken som passar bäst, testa olika metoder och inspektera resultaten.

Några av länkmetoderna är:

  • Enkel länk: även kallad Närmaste granne (NN). Avståndet mellan kluster definieras av avståndet mellan deras närmaste medlemmar.

img

  • Komplett koppling: även kallad Fjärrste granne (FN), Farthest Point Algoritm, eller Voor Hees-algoritm. Avståndet mellan kluster definieras av avståndet mellan de längst bort. Denna metod är beräkningsmässigt dyr.

img

  • Genomsnittlig koppling: också känd som UPGMA (Oviktad pargruppsmetod med aritmetiskt medelvärde). Procentandelen av antalet poäng för varje kluster beräknas med avseende på antalet poäng för de två klustren om de slogs samman.

img

  • Viktad koppling: också känd som WPGMA (Viktad pargruppsmetod med aritmetiskt medelvärde). De individuella punkterna i de två klustren bidrar till det aggregerade avståndet mellan ett mindre och ett större kluster.
  • Centroid länkage: även kallad UPGMC (Oviktad pargruppsmetod med centroider). En punkt som definieras av medelvärdet av alla punkter (tyngdpunkt) beräknas för varje kluster och avståndet mellan kluster är avståndet mellan deras respektive tyngdpunkter.

img

  • Avdelningskoppling: Också känd som MISSQ (Minimal ökning av summan av kvadrater). Den specificerar avståndet mellan två kluster, beräknar summan av kvadratfel (ESS) och väljer successivt nästa kluster baserat på den mindre ESS. Ward's Method strävar efter att minimera ökningen av ESS vid varje steg. Därför minimerar fel.

img

Avståndsmått

Förutom kopplingen kan vi också specificera några av de mest använda avståndsmåtten:

  • euklidisk: även kallad Pythagoras eller rak linje distans. Den beräknar avståndet mellan två punkter i rymden genom att mäta längden på ett linjesegment som passerar mellan dem. Den använder Pythagoras sats och avståndsvärdet är resultatet (C) av ekvationen:

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

  • Manhattan: även kallad Stadskvarter, Taxicab distans. Det är summan av absoluta skillnader mellan måtten i alla dimensioner av två punkter. Om dessa dimensioner är två, är det analogt med att göra en höger och sedan vänster när man går ett kvarter.

img

  • Minkowski: det är en generalisering av både euklidiska och Manhattan-avstånd. Det är ett sätt att beräkna avstånd baserat på de absoluta skillnaderna till ordningen för Minkowski-måttet p. Även om det är definierat för någon p> 0, används den sällan för andra värden än 1, 2 och ∞ (oändlig). Minkowski distans är samma som Manhattan distans när p = 1, och samma som euklidiskt avstånd när p = 2.

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

img

  • Chebyshev: också känd som schackbräde distans. Det är extremfallet med Minkowski-distans. När vi använder oändlighet som parameterns värde p (p = ∞), slutar vi med ett mått som definierar avstånd som den maximala absoluta skillnaden mellan koordinater.
  • cosinus: det är vinkelcosinusavståndet mellan två sekvenser av punkter eller vektorer. Cosinuslikheten är prickprodukten av vektorerna dividerat med produkten av deras längder.
  • Jaccard: mäter likheten mellan finita uppsättningar av punkter. Det definieras som det totala antalet poäng (kardinalitet) i de gemensamma punkterna i varje uppsättning (korsning), dividerat med det totala antalet poäng (kardinalitet) av de totala poängen för båda uppsättningarna (union).
  • Jensen-Shannon: baserat på Kullback-Leibler-divergensen. Den tar hänsyn till poängens sannolikhetsfördelningar och mäter likheten mellan dessa fördelningar. Det är en populär metod för sannolikhetsteori och statistik.

Vi har valt Ward och euklidisk för dendrogrammet eftersom de är den mest använda metoden och metriken. De ger vanligtvis bra resultat eftersom Ward länkar punkter baserat på att minimera felen, och Euclidan fungerar bra i lägre dimensioner.

I det här exemplet arbetar vi med två funktioner (kolumner) i marknadsföringsdata och 200 observationer eller rader. Eftersom antalet observationer är större än antalet funktioner (200 > 2), arbetar vi i ett lågdimensionellt utrymme.

När antalet funktioner (F) är större än antalet observationer (N) – mestadels skrivet som f >> N, det betyder att vi har en högdimensionellt utrymme.

Om vi ​​skulle inkludera fler attribut, så vi har mer än 200 funktioner, kanske det euklidiska avståndet inte fungerar särskilt bra, eftersom det skulle ha svårt att mäta alla små avstånd i ett mycket stort utrymme som bara blir större. Med andra ord har den euklidiska distansmetoden svårt att arbeta med data gleshet. Detta är en fråga som kallas dimensionalitetens förbannelse. Avståndsvärdena skulle bli så små, som om de blev "utspädda" i det större utrymmet, förvrängda tills de blev 0.

Notera: Om du någonsin stöter på en datauppsättning med f >> sid, kommer du förmodligen att använda andra avståndsmått, till exempel Mahalanobis distans. Alternativt kan du också minska datauppsättningens dimensioner genom att använda Huvudkomponentanalys (PCA). Detta problem är vanligt, särskilt när biologiska sekvenseringsdata samlas i kluster.

Vi har redan diskuterat mätvärden, kopplingar och hur var och en av dem kan påverka våra resultat. Låt oss nu fortsätta dendrogramanalysen och se hur den kan ge oss en indikation på antalet kluster i vår datauppsättning.

Att hitta ett intressant antal kluster i ett dendrogram är detsamma som att hitta det största horisontella utrymmet som inte har några vertikala linjer (utrymmet med de längsta vertikala linjerna). Detta innebär att det finns mer separation mellan klustren.

Vi kan rita en horisontell linje som går genom den längsta sträckan:

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 att ha lokaliserat den horisontella linjen, räknar vi hur många gånger våra vertikala linjer korsades av den – i det här exemplet 5 gånger. Så 5 verkar vara en bra indikation på antalet kluster som har störst avstånd mellan dem.

Anmärkningar: Dendrogrammet bör endast betraktas som en referens när det används för att välja antalet kluster. Den kan lätt få det siffran långt borta och påverkas helt av typen av koppling och avståndsmått. När du gör en djupgående klusteranalys, rekommenderas det att titta på dendrogram med olika kopplingar och mått och att titta på resultaten som genereras med de tre första raderna där klustren har störst avstånd mellan dem.

Implementera en agglomerativ hierarkisk klustring

Använda originaldata

Hittills har vi beräknat det föreslagna antalet kluster för vår datauppsättning som bekräftar vår initiala analys och vår PCA-analys. Nu kan vi skapa vår agglomerativa hierarkiska klustringsmodell med Scikit-Learn AgglomerativeClustering och ta reda på etiketterna för marknadsföringspunkter 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_

Detta resulterar 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ökt mycket för att komma dit. Och vad betyder dessa etiketter? Här har vi varje punkt i vår data märkt som en grupp från 0 till 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

Detta är vår sista klustrade data. Du kan se de färgkodade datapunkterna i form av fem kluster.

Datapunkterna längst ner till höger (etikett: 0, lila datapunkter) tillhör kunderna med höga löner men låga utgifter. Det är dessa kunder som spenderar sina pengar försiktigt.

På samma sätt, kunderna uppe till höger (etikett: 2, gröna datapunkter), är kunderna med höga löner och höga utgifter. Det är den typen av kunder som företagen riktar sig till.

Kunderna i mitten (etikett: 1, blå datapunkter) är de med genomsnittlig inkomst och genomsnittlig utgift. Det högsta antalet kunder tillhör denna kategori. Företag kan också rikta sig till dessa kunder med tanke på att de finns i enorma antal.

Kunderna längst ner till vänster (etikett: 4, röd) är de kunder som har låga löner och låga utgifter, de kan lockas genom att erbjuda kampanjer.

Och slutligen kunderna uppe till vänster (etikett: 3, orange datapunkter) är de med hög inkomst och låga utgifter, som helst riktas mot marknadsföring.

Använder resultatet från PCA

Om vi ​​befann oss i ett annat scenario, där vi var tvungna att minska dimensionaliteten av data. Vi kan också enkelt plotta de klustrade PCA-resultaten. Det kan göras genom att skapa en annan agglomerativ klustringsmodell och erhålla en dataetikett för varje huvudkomponent:

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

Observera att båda resultaten är mycket lika. Den största skillnaden är att det första resultatet med originaldata är mycket lättare att förklara. Det är tydligt att se att kunder kan delas in i fem grupper efter deras årliga inkomst och utgiftspoäng. Även om vi i PCA-metoden tar hänsyn till alla våra funktioner, så mycket som vi kan titta på variansen som förklaras av var och en av dem, är detta ett svårare koncept att förstå, särskilt när du rapporterar till en marknadsavdelning.

Ju minst vi behöver för att omvandla vår data, desto bättre.

Om du har en mycket stor och komplex datauppsättning där du måste utföra en dimensionsreduktion före klustring – försök att analysera de linjära sambanden mellan var och en av funktionerna och deras rester för att säkerhetskopiera användningen av PCA och förbättra processens förklaringsbarhet. Genom att göra en linjär modell per funktionspar kommer du att kunna förstå hur funktionerna samverkar.

Om datavolymen är så stor blir det omöjligt att plotta funktionerna, välj ett urval av dina data, så balanserat och nära normalfördelningen som möjligt och utför analysen på provet först, förstå det, finjustera det – och tillämpa det senare på hela datasetet.

Du kan alltid välja olika klustringsvisualiseringstekniker beroende på dina datas natur (linjära, icke-linjära) och kombinera eller testa alla vid behov.

Slutsats

Klustringstekniken kan vara väldigt praktisk när det kommer till omärkta data. Eftersom de flesta data i den verkliga världen är omärkta och att kommentera data har högre kostnader, kan klustringstekniker användas för att märka omärkta data.

I den här guiden har vi tagit med ett verkligt datavetenskapligt problem, eftersom klustringstekniker till stor del används i marknadsföringsanalys (och även i biologisk analys). Vi har också förklarat många av utredningsstegen för att komma till en bra hierarkisk klustringsmodell och hur man läser dendrogram och ifrågasatt om PCA är ett nödvändigt steg. Vårt huvudmål är att några av de fallgropar och olika scenarier där vi kan hitta hierarkisk klustring täcks.

Lycka till med klustringen!

Tidsstämpel:

Mer från Stackabuse