21. Klyngeanalyse
21. Klyngeanalyse

21. Klyngeanalyse

Klyngeanalyse eller klusteranalyse er en statistisk teknik brugt til at gruppere en samling af observationer i undergrupper kaldet klynger, således at observationerne inden for hver klynge har lignende karakteristika eller egenskaber, mens de er forskellige fra observationer i andre klynger.
I klyngeanalyse grupperer man observationerne, mens faktoranalyse grupperer variablene. Både klynge- og faktoranalyse kaldes strukturanalyser. Disse minder en del om hinanden. I klyngeanalysen er der ikke regler for hvor mange klynger man bør danne, her dannes klynger der giver mening, således at der er bestemte karakteristika for klyngerne.
Klyngeanalyse bruges bredt inden for mange felter som marketing, biologi, medicin, psykologi, økonomi og mere, til opgaver som markedssegmentering, genetisk forskning, mønstergenkendelse og datareduktion.
I modsætning til klassificeringsopgaver, hvor grupper er defineret på forhånd, opdager klyngeanalysen grupperinger i data uden foruddefinerede etiketter.

Herunder er eksempler på i hvilke områder klyngeanalyse finder anvendelse i forskellige sektorer, bemærk der er angivet eksempler på 2 klynger, analysen kan sagtens indeholde flere klynger.

Sektor Eksempel
Retail En stor detailkæde kunne anvende klyngeanalyse til at identificere forskellige kundesegmenter baseret på købsadfærd og demografiske data.
Mulige klynger kunne være:

Klynge 1: Værdibevidste kunder
Karakteristika for klyngen:
• Søger primært tilbud og rabatter
• Køber store mængder under salg

Klynge 2: Premium kunder
Karakteristika for klyngen:
• Foretrækker højkvalitetsprodukter
• Mindre prissensitive
Bank Banker kan bruge klyngeanalyse baseret på kundedata til at segmentere kunder baseret på finansielle adfærd og behov.
Mulige klynger kunne være:

Klynge 1: Aktive investorer
Karakteristika for klyngen:
• Høj aktivitet i investeringsporteføljer
• Interesseret i forskellige investeringsmuligheder

Klynge 2: Opsparingsorienterede kunder
Karakteristika for klyngen:
• Fokus på opsparingskonti og fastindtægtsprodukter
• Risikoaverse i forhold til investeringer
Forsikring Forsikringsselskaber kan anvende klyngeanalyse baseret på kundedata til at segmentere kunder og forstå risikoprofiler.
Mulige klynger kunne være:

Klynge 1: Højrisikokunder
Karakteristika for klyngen:
• Hyppige kravshistorikker
• Engageret i risikofyldte aktiviteter

Klynge 2: Lavrisikokunder
Karakteristika for klyngen:
• Sjældne forsikringskrav
• Forsigtige og sikkerhedsorienterede adfærdsmønstre
Ejendomsadministration Ejendomsadministratorer kan bruge klyngeanalyse baseret på kundedata til at forstå forskellige lejertyper og optimere ledelsestjenester.
Mulige klynger kunne være:

Klynge 1: Langtidslejere
Karakteristika for klyngen:
• Søger stabilitet og langvarige lejemål
• Foretrækker boliger med god adgang til offentlig transport og skoler

Klynge 2: Korttidslejere
Karakteristika for klyngen:
• Ofte studerende eller midlertidige arbejdstagere
• Leder efter fleksible lejevilkår og møblerede boliger
Telekommunikation Et telekommunikationsselskab kunne anvende klyngeanalyse til at segmentere kunder baseret på brugsmønstre og præferencer.
Mulige klynger kunne være:

Klynge 1: Data-tunge brugere
Karakteristika for klyngen:
• Høj dataforbrug
• Foretrækker ubegrænsede dataplaner

Klynge 2: Grundlæggende brugere
Karakteristika for klyngen:
• Lavt dataforbrug
• Foretrækker billigere planer med basale funktioner
Transport Et transportselskab kunne anvende klyngeanalyse til at forstå forskellige brugermønstre og forbedre ruteplanlægning.
Mulige klynger kunne være:

Klynge 1: Pendlere
Karakteristika for klyngen:
• Daglige ture i spidsbelastningstider
• Foretrækker direkte ruter til arbejdspladser

Klynge 2: Lejlighedsrejsende
Karakteristika for klyngen:
• Uregelmæssige rejsetider
• Tiltrækkes af fritidsrelaterede destinationer
Underholdning En streamingtjeneste kunne bruge klyngeanalyse til at identificere forskellige seerpræferencer for at personalisere anbefalinger.
Mulige klynger kunne være:

Klynge 1: Filmelskere
Karakteristika for klyngen:
• Høj præference for nyere filmudgivelser
• Interesseret i filmfestivaler og priser

Klynge 2: Serieentusiaster
Karakteristika for klyngen:
• Foretrækker lange TV-serier
• Engagerer sig ofte i serie-maraton

Tabel 21.1: Eksempler på, hvordan klyngeanalyse kan anvendes til kundesegmentering i syv forskellige sektorer — retail, bank, forsikring, ejendomsadministration, telekommunikation, transport og underholdning — med hver to illustrative klynger og deres karakteristika.
Kilde: Egen tilvirkning.

Klyngeanalyse er ofte et første, uovervåget skridt i en større ML-pipeline; fx kan man opdage kundesegmenter forud for svindeldetektion eller gruppere porteføljer efter risikoprofil, inden mere avancerede modeller, som Random ForestRandom Forest kombinerer mange beslutningstræer til én model. Hvert træ ser på et tilfældigt udsnit af data, og flertallet af træernes svar bliver det endelige resultat — derfor "skov".
Leo Breiman introducerede metoden i 2001; den er i dag en af de mest populære inden for maskinlæring. Han startede som fysiker og arbejdede med kernevåben, før han skiftede til statistik.

Klik her for at læse mere om Random Forest i kapitel 1!
, trænes på de fundne grupper.

Eksempel Bilmodeller

Vi har et datasæt bestående af 32 bilmodeller med følgende 11 variable:

mtcars datasættet
Variabel Forklaring på de 11 variable
mpg Miles per gallon. Dette er en måling af brændstoføkonomi og angiver, hvor mange miles en bil kan køre per gallon benzin.
1 mile er lig med ca. 1,6 kilometer.
1 US gallon er lig med ca. 3,8 liter.
Man kan omregne mpg til km/l ved at gange med ca. 0,42.
cyl Antal cylindre i bilens motor. Typiske værdier er 4, 6, eller 8 cylindre.
disp Motorvolumen (displacement) i kubik tommer. Dette er et mål for motorstørrelsen. Kubik-tommer kan omregnes til kubik-centimeter ved at gange med ca. 16.
hp Hestekræfter. Dette er en måling af bilens motorkraft.
drat Bagakseludveksling (differential ratio). Dette tal angiver forholdet mellem antallet af tænder på ringgearet til antallet af tænder på drivgearet i bilens differential.
wt Vægt (1000 lbs). Dette er bilens vægt målt i tusinder af pund. Vægt i 1000 pund kan omregnes til tons ved at gange med ca. 0,45.
qsec 1/4 mile tid. Tiden det tager for bilen at køre en fjerdedels mile (cirka 0,4 kilometer) fra stillestående start, lavere værdi betyder hurtigere acceleration. qsec kan ikke direkte omsættes til 0-100 km/t tider.
vs Motorform. Dummy-variabel med niveauerne 0 (V-formet motor) og 1 (lige motor).
am Gearkassetype. Dummy-variabel med niveauerne 0 (automatisk gearkasse) og 1 (manuel gearkasse).
gear Antal fremadgående gear i bilens gearkasse.
carb Antal karburatorer. Dette tal angiver antallet af karburatorer i bilens motor.

Tabel 21.2: Variabeloversigt for mtcars-datasættet — 11 tekniske og ydeevnerelaterede mål (brændstoføkonomi, cylindre, motorvolumen, hestekræfter m.m.) for 32 bilmodeller, som skal grupperes i klynger ud fra deres specifikationer.
Kilde: Motor Trend US (1974), indbygget R-datasæt "mtcars".

Vi vil gerne gruppere de forskellige bilmodeller i forskellige grupper eller klynger ud fra deres specifikationer. For at undersøge, om vi på baggrund af tekniske karakteristika, kan gruppere bilmodellerne, benytter vi klyngeanalyse. Bemærk i faktoranalysen grupperer vi variablene, i klyngeanalysen grupperer vi respondenterne eller observationerne, her altså bilerne.


Der findes overordnet 2 typer af klyngeudvælgelse:

Ikke-hierarkisk, k-means metoden benyttes ofte, hvis vi har store datasæt hvor der kræves mange beregninger, man vælger på forhånd hvor mange klynger man vil have.

Hierarkisk klyngedannelse, agglomerative metode hvor man starter med at hver respondent har sin egen klynge og man derefter sammenhober disse trin for trin kaldes den sammenhobede eller agglomerative metode.
Vi benytter både den agglomerative metode og k-means på bildatasættet mtcars, for at se hvordan begge metoder fungerer.
Det kan være en fordel at få overblik vha. et klyngetræ vha. den agglomerative metode og derefter benytte k-means med et specifikt antal klynger i den videre analyse.



#Vi gemmer datasættet mtcars i variablen data
data <- mtcars
#Vi kan se starten af datasættet med head kommandoen
head(data)
  
mpg cyl disp hp drat wt qsec vs am gear carb Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

Vi kan se at når vi sammenligner forskellige variable ser det ud til at der er forskellige grupper. Nedenfor ser vi fx de 32 biler plottet i et diagram efter hestekræfter og miles per gallon. Bilerne er farvekodet med antal cylindre.


#Her angiver vi modelnavne for hver bil
#med label rownames vi bruger geom_text i stedet for point her.
pacman::p_load("ggplot2")
ggplot(data, aes(hp, mpg, color = cyl)) +
geom_point() #Plot med kun punkter
  





ggplot(data, aes(hp, mpg, color = cyl,label=rownames(data)))+
geom_text(size=3,check_overlap = TRUE)




Vi kan benytte t til at transponere data matricen, så kan vi tegne et corrplot, det er meget mørkt da alle bilerne er positivt korrelerede, men man kan ane nogle sammenhænge. t(data) betyder vi transponerer (vender) matricen, så ser vi i stedet på grupper af respondenter, som vi netop analyserer i klyngeanalysen. Hvis ikke vi vender matricen ser vi på korrelationsmatricenKorrelationen angiver hvor meget 2 variable samvarierer, vi kan måle korrelationen vha. korrelationskoefficienten der er et tal mellem -1 og 1.
Korrelationskoefficienten er klart positiv hvis høje og lave værdier for den ene variabel følges med høje og lave værdier for den anden variabel.
Korrelationskoefficienten er klart negativ hvis høje og lave værdier for den ene variabel følges med lave og høje værdier for den anden variabel.
Er der ingen samvariation mellem 2 variable er korrelationskoefficienten tæt på 0.
Korrelationskoefficienten i en kiosk mellem afsætning af is og temperaturen vil være positiv, jo koldere/varmere det er jo mindre/større afsætning af is.
Korrelationskoefficienten i en kiosk mellem afsætning af varm kakao og temperaturen vil være negativ, jo koldere/varmere det er jo større/mindre afsætning af kakao.
For to variable som ikke er korrelerede (de samvarierer ikke) som fx. Apple aktie kursen og nedbørsmængden i Dragør, vil vi få en korrelationskoefficient tæt på 0.


Klik her for at lære mere om korrelationsanalyse!
mellem de 11 variable i stedet, ligesom vi tidligere har gjort med faktoranalysen.


#korrelationsmatricen for transponeret data, hclust betyder vi 
#ordner efter variable der passer sammen
pacman::p_load(corrplot)
corrplot(cor(t(data)), order = "hclust", tl.col='black', tl.cex=.5)
  





#Her er korrelationsmatricen ikke transponeret, hvilket svarer til en 
#matrice baseret på variable som ved faktoranalysen vi tidligere så på.
corrplot(cor(data), order = "hclust", tl.col='black', tl.cex=.5)
  




Hierarkisk klyngeanalyse hclust-kommandoen





Vi får nu R til at danne klynger vha. hclust-kommandoen (hierarchical cluster). Denne benytter som standard en metode der hedder complete til at finde ens klynger, der findes mange andre metoder. For at benytte hclust skal R først beregne afstandene mellem bilerne, dette gøres med dist-kommandoen. Algoritmen beregner afstandene mellem de forskellige bilmodeller vha. den euklidiske metrik. Biler med kort afstand kommer i klynger sammen, biler med lang afstand kommer i forskellige klynger.

Nedenfor ses et udsnit af afstandene mellem hver af de 32 biler, det er en meget stor 32 × 32 matrice, derfor har vi benyttet head for kun at vise noget af matricen. Fx er afstanden mellem to forskellige biler som en Mazda RX4 og en Lincoln Continental 318,05 hvilket er en stor afstand i forhold til fx. Mazda RX4 og Mazda RX4 Wag på kun 0,62. Bemærk hvordan supersportsvognen Maserati Bora har store afstande til de fleste af de øvrige biler, Maseratien var en komfortabel, rummeligere og kraftigere og tungere sportsvogn end fx. Ferrari Dino.

head(as.matrix(dist(data)))

Vi gemmer hclust data i clusters variablen, vi så kan benytte til at tegne en oversigt over klyngerne. Vi kan nu plotte en grafisk oversigt over bilerne. I nederste linje er den fineste inddeling, hvor samtlige biler er i deres egen klynge. Den blå linje med 3 skæringer i dendogrammet indikerer der er 3 klynger, den røde 4 klynger.


clusters <- hclust(dist(data))
plot(clusters,cex=0.5,main = "Dendogram af mtcars",xlab = "Klyngetræ",sub="Bilmodeller")
abline(h = 190, col="red") #Tegn rød vandret linje h betyder horisontal
abline(h = 230, col="blue")




Hvis vi ønsker at undersøge en inddeling med et bestemt antal klynger, kan vi bruge cutree i R, til at undersøge klyngerne i en skæring med fx. 4 klynger nærmere. Her ser vi som nævnt, Maserati Bora skiller sig ud ved at have sin egen klynge. Nummeret ved hver af de 32 biler angiver hvilken klynge bilen tilhører.


clusterCut <- cutree(clusters, 4) #Opdeling i 4 klynger.
clusterCut
Mazda RX4 Mazda RX4 Wag Datsun 710 Hornet 4 Drive 1 1 1 2 Hornet Sportabout Valiant Duster 360 Merc 240D 3 2 3 1 Merc 230 Merc 280 Merc 280C Merc 450SE 1 1 1 2 Merc 450SL Merc 450SLC Cadillac Fleetwood Lincoln Continental 2 2 3 3 Chrysler Imperial Fiat 128 Honda Civic Toyota Corolla 3 1 1 1 Toyota Corona Dodge Challenger AMC Javelin Camaro Z28 1 2 2 3 Pontiac Firebird Fiat X1-9 Porsche 914-2 Lotus Europa 3 1 1 1 Ford Pantera L Ferrari Dino Maserati Bora Volvo 142E 3 1 4 1

Vi kan benytte subset-kommandoen til at se på hvilke variable der er i hver klynge, herunder ser vi på klynge 3.

subset(clusterCut,clusterCut==3)
Hornet Sportabout Duster 360 Cadillac Fleetwood Lincoln Continental 3 3 3 3 Chrysler Imperial Camaro Z28 Pontiac Firebird Ford Pantera L 3 3 3 3

Vi kan ligeledes sammenligne klyngeinddelingen med de enkelte variable, og undersøge om der synes at være nogle mønstre.

table(clusterCut, data$cyl)

table(clusterCut, data$mpg)

Bemærk herunder mpg i klynge 1 har alle de bilerne med god benzinøkonomi der kører langt på literen, dvs. der er høje tal for mpg.
Klynge 3 og 4 har alle bilerne med dårlig benzinøkonomi, der har et lavt tal for mpg.

clusterCut 10.4 13.3 14.3 14.7 15 15.2 15.5 15.8 16.4 17.3 17.8 18.1 18.7 19.2 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 2 0 0 0 0 0 2 1 0 1 1 0 1 0 0 3 2 1 1 1 0 0 0 1 0 0 0 0 1 1 4 0 0 0 0 1 0 0 0 0 0 0 0 0 0 clusterCut 19.7 21 21.4 21.5 22.8 24.4 26 27.3 30.4 32.4 33.9 1 1 2 1 1 2 1 1 1 2 1 1 2 0 0 1 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0

Bemærk herunder hp i klynge 1 har alle de bilerne med få hestekræfter.
Klynge 3 og 4 har alle bilerne med mange hestekræfter.

table(clusterCut, data$hp)
clusterCut 52 62 65 66 91 93 95 97 105 109 110 113 123 150 175 180 205 215 230 1 1 1 1 2 1 1 1 1 0 1 2 1 2 0 1 0 0 0 0 2 0 0 0 0 0 0 0 0 1 0 1 0 0 2 0 3 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 1 1 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 clusterCut 245 264 335 1 0 0 0 2 0 0 0 3 2 1 0 4 0 0 1

Herunder ses carb antal karburatorer i denne tabel er mønsteret ikke så entydigt tydeligt.

table(clusterCut, data$carb)
clusterCut 1 2 3 4 6 8 1 5 6 0 4 1 0 2 2 2 3 0 0 0 3 0 2 0 6 0 0 4 0 0 0 0 0 1

Bemærk herunder wt i klynge 1 har alle de bilerne med lav vægt. I klynge 3 og 4 er bilerne tungere.

table(clusterCut, data$wt)
clusterCut 1.513 1.615 1.835 1.935 2.14 2.2 2.32 2.465 2.62 2.77 2.78 2.875 1 1 1 1 1 1 1 1 1 1 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 clusterCut 3.15 3.17 3.19 3.215 3.435 3.44 3.46 3.52 3.57 3.73 3.78 3.84 3.845 1 1 0 1 0 0 2 0 0 0 0 0 0 0 2 0 0 0 1 1 0 1 1 0 1 1 0 0 3 0 1 0 0 0 1 0 0 1 0 0 1 1 4 0 0 0 0 0 0 0 0 1 0 0 0 0 clusterCut 4.07 5.25 5.345 5.424 1 0 0 0 0 2 1 0 0 0 3 0 1 1 1 4 0 0 0 0

Hvis dendogrammet virker lidt uoverskueligt, kan man vælge ape-pakken for at lave mere fancy plots, her er rigtig mange muligheder. Herunder er klyngerne også farvekodede, lyserød er fx klynge 4 med kun 1 Maserati, rød er klyngen med de benzinøkonomiske biler.


#install.packages("ape")
library("ape")
colors = c("red", "blue", "green", "pink")
clus4 = cutree(clusters, 4)
plot(as.phylo(clusters), type = "fan", tip.color = colors[clus4], label.offset = 0, cex = 0.5)




Herunder er et plot, hvor farvekoden er baseret på klyngerne. Rød er klynge 1 og blå er klynge 4 med kun Maserati.


ggplot(data, aes(hp, mpg)) +
geom_point(alpha = 0.4, size = 3.5) 
+ geom_point(col = clusterCut)




Ikke-hierarkisk klyngeanalyse kmeans-kommandoen


Vi kunne også have brugt kmeans-metoden, her skal vi så angive hvor mange klynger, vi ønsker i analysen. Her benytter vi K-means og får 4 klynger med 7, 6, 9, 10 biler.

Herunder ses det kommenterede output af kmeans-kommandoen i R.


mtcarsCluster4 <- kmeans(data, 4)
mtcarsCluster4

Cluster means: For hver klynge vises den gennemsnitlige værdi af hver variabel. I søjle 2 der hedder cyl er der i klynge 3 et gennemsnitligt antal cylindre på 4. Dette skyldes at alle de 10 biler i denne klynge har 4 cylindre. Bemærk antallet i klyngerne og nummeret på klyngerne, er ikke nødvendigvis det samme som ved hclust-metoden, der er tale om 2 forskellige algoritmer. I K-means klyngeanalysen har de økonomiske biler klynge nummer 3 og indeholder 10 biler, i hclust vi så tidligere var de økonomiske biler i klynge 1 og her var 16 biler (heraf 11 med 4 cylindre).
Kører du analyserne vil du opleve mindre afvigelser, hver gang du kører analysen på ny, det skyldes algoritmen udvikler sig forskelligt hver gang, men essensen er den samme.
Clustering vector: En vektor af klyngetildelinger for hver observation. Dette viser, hvilken klynge hver observation er blevet tildelt.
Within cluster sum of squares by cluster: Måler den samlede varians inden for hver klynge; lavere værdier indikerer, at observationerne ligger tættere på klyngens centrumspunkt, hvilket generelt anses for bedre.
between_SS Dette er den samlede varians mellem klyngerne. Dette mål hjælper med at forstå, hvor adskilte de forskellige klynger er.
total_SS Den samlede varians i datasættet.
between_SS / total_SS Angiver hvor meget af den samlede variation klyngerne forklarer, dette er et tal mellem 0% og 100%
Available components: Man kan særskilt hente enkeltelementer fra analysen fx elementer som "totss", "withinss", "tot.withinss",...
Man angiver variablen med klyngeanalysen og $ efterfulgt af elementet. Fx giver kommandoen:

mtcarsCluster4$iter
[1] 2 Her ses hvor mange iterationer algoritmen har gennemført (her 2 iterationer) for at nå til netop denne klyngedeling.
Bemærk hclust- og kmeans-kommandoerne gennemfører en algoritme og kommer ofte med forskellige opdelinger af klyngerne, derfor får du sandsynligvis ikke præcis samme tal, når du kører analysen i R.

Nedenfor ser vi på en tabel inddeling med 4 klynger med de 32 biler, sorteret efter antallet af cylindre. Rækker er klynger og søjler er cylindre. Bemærk klynge 3 der indeholder 10 biler, har kun biler med 4 cylindre, det var netop klyngen med bedst benzinøkonomi. Læg mærke til at klyngegennemsnittet for klynge 3 for variablen cylindre er netop 4 (se figuren med klyngeoutput og kommentarer herover), dette skyldes jo at samtlige biler i klyngen har 4 cylindre.

table(mtcarsCluster4$cluster, data$cyl)
4 6 8 1 1 6 0 2 0 0 9 3 10 0 0 4 0 1 5

Nedenfor ser vi på en tabel inddeling med kun 3 klynger med de 32 biler, sorteret efter antallet af cylindre. Her er klynge 1 nu klyngen med økonomiske biler. gennemfører vi kmeans analysen igen, falder bilerne ikke nødvendigvis i samme klynger som tidligere, det skyldes algoritmen kan give forskellig optimale inddelinger.


mtcarsCluster3 <- kmeans(data, 3)
table(mtcarsCluster3$cluster, data$cyl)
4 6 8 1 11 0 0 2 0 7 0 3 0 0 14

Vi kan lave et klyngeplot der viser forskellene på de 32 biler, herunder ses plottet med 3 klynger. Bemærk vi skal hente pakken factoextra, der indeholder plotfunktionen fviz_cluster(). Vi har benyttet funktionen scale(), det er en rigtig god idé at benytte, hvis der er stor forskel på måleenhederne i en data.frame. Funktionen scale() bringer variablene i samme skala.
Klyngeplottet giver et godt grafisk overblik over klyngerne, skalaerne på x- og y-aksen skal man ikke hæfte sig nærmere ved. Disse er en dimensionsreduceret repræsentation af de oprindelige variable.


km3.res <- kmeans(scale(data), 3, nstart = 25)
pacman::p_load(factoextra)
fviz_cluster(km3.res, data = data, main = "Klyngeplot biler opdelt i 3 klynger",repel = TRUE)




Herunder beder vi kun om middelværdierne for hver af de 3 klyngerne for hver af de 11 variable, vi har fundet ovenfor ved kommandoen km3.res <- kmeans(scale(data), 3, nstart = 25).
Husk man behøver ikke hente alt output med km3, men kan hente delelementer ved at benytte $.
Læg mærke til, at middelværdierne er standardiserede, i forhold til før hvor vi ikke standardiserede, da vi brugte kommandoen kmeans(data, 4). Standardisering sikrer en uniform vægtning af de enkelte variable.


km3.res$centers
  


Bemærk høje værdier markeret med grønne rammer, og lave værdier markeret med røde rammer, for de standardiserede middelværdier, stemmer med karakteristika for de 3 klynger af biler, i klyngeplottet.

Klyngeplot der viser forskellene på de 32 biler, herunder ses plottet med 5 klynger.


km5.res <- kmeans(scale(data), 5, nstart = 25)
fviz_cluster(km5.res, data = data, main = "Klyngeplot biler opdelt i 5 klynger",repel = TRUE)




Bemærk hvis skriften fylder meget, så mange labels ikke kommer med, kan man ændre skriftstørrelsen med argumentet labelsize = 6 i fviz_cluster-kommandoen som fx. herunder:

km5.res <- kmeans(scale(data), 5, nstart = 25)
fviz_cluster(km5.res, labelsize = 6, data = data, main = "Klyngeplot biler opdelt i 5 klynger",repel = TRUE)




Validering med ANOVA

Skal man undersøge om grupperne/klyngerne er forskellige med hensyn til de forskellige variable, kan man benytte ANOVA ANOVA testet benyttes til test af gennemsnit for flere end 2 kvantitative variable, populationerne skal have samme varians.
Eksempler på stikprøver, hvor middelværdierne kan testes vha. ANOVA testet kunne være:
For 4 stikprøver med højder for CBS, KU, CPH Business og SDU studerende, kan man teste om middelhøjden er ens med ANOVA testet.
For 6 stikprøver med med salgspriser for ejendomme i 6 byer, kan man teste om middelværdierne er ens med ANOVA testet.
ANOVA testet er en udvidelse af pooled t-test, hypoteserne kan generisk skrives som:
H0: Alle middelværdier er ens
H1: Ikke alle middelværdier er ens

Klik her for at lære mere om ANOVA!

, hvor klyngerne er den uafhængige variabel. Man skal da gerne nå frem til at klyngegennemsnittene er signifikant forskellige mht. flere af variablene, der indgår i analysen.

Quiz

Quiz om klyngeanalyse

Spørgsmål

Vi vil her gennemføre en klyngeanalyse af datasættet om Verdensøkonomien.
Bemærk Rusland og Ukraine er desværre ikke medtaget i datasættet, da det er svært at indhente pålidelige data pga. krigen. En del andre interessante lande som f.eks. Argentina og Nord Korea er ligeledes ikke med i analysen, dette skyldes igen manglende eller ubekræftede data.

Plots er ikke lagt direkte i besvarelsen da disse er meget grafiktunge for at få alle detaljer med, men klik på links for at se disse.

Excelfilen om Verdensøkonomien
Datasættet består af 139 lande, målt på 6 variable:
Variable
  1. Land
  2. BNP USD
  3. BNP pr indbygger USD
  4. Befolkning
  5. BNP Vækst fra året før %
  6. Inflation %
  7. Arbejdsløshed %
Vi kan kun gennemføre en klyngeanalyse på kvantitative data, men vi vil gerne beholde landene som rækkenavne i R.
Dette kan vi ordne med koden herunder:

# Klyngeanalyse med Verdensøkonomien
# Vi sætter nu alt output til normale tal, dvs. ikke-scientific notation
options(scipen = 999)
# Vi importerer Excel datasættet Verdensøkonomien_2023.xlsx, man kan også importere ved peg og klik metoden
library(readxl)
Verdensøkonomien_2023 <- read_excel("...ANGIV STI TIL FILEN HER.../Verdensøkonomien_2023.xlsx")
View(Verdensøkonomien_2023)
# Vi gemmer Verdensøkonomien_2023.xlsx i variablen data
data <- Verdensøkonomien_2023
# Vi vil gerne gemme landenavnene i første søjle som rækkenavne, før vi kan sætte row.names sættes data som en data.frame
data <- as.data.frame(data)
row_names <- data[,1]
row.names(data) <- row_names
# Vi fjerner nu 1. søjle da denne er gemt som rækkenavne
data <- data[,-1]
              

Vi startede med koden fra spørgsmålet for at klargøre data:


# Klyngeanalyse med Verdensøkonomien
# Vi sætter nu alt output til normale tal, dvs. ikke-scientific notation
options(scipen = 999)
# Vi importerer Excel datasættet Verdensøkonomien_2023.xlsx, man kan også importere ved peg og klik metoden
library(readxl)
Verdensøkonomien_2023 <- read_excel("...ANGIV STI TIL FILEN HER.../Verdensøkonomien_2023.xlsx")
View(Verdensøkonomien_2023)
# Vi gemmer Verdensøkonomien_2023.xlsx i variablen data
data <- Verdensøkonomien_2023
# Vi vil gerne gemme landenavnene i første søjle som rækkenavne, før vi kan sætte row.names sættes data som en data.frame
data <- as.data.frame(data)
row_names <- data[,1]
row.names(data) <- row_names
# Vi fjerner nu 1. søjle da denne er gemt som rækkenavne
data <- data[,-1]
                          

Her har vi startet med at plotte data inden selve klyngeanalysen, for at danne os et overblik, til dette bruges pakken ggplot2 i R:


# For at tegne figurer skal vi først loade pakken ggplot2
library(ggplot2)
#Herunder er plot af BNP pr indbygger mod arbejdsløshed farvekodet med Befolkning, for at få et overblik over data
ggplot(data, aes(`BNP pr indbygger USD`, `Arbejdsløshed %`, color = Befolkning,label=rownames(data)))+
  geom_text(size=3,check_overlap = TRUE)
#Herunder er plot af BNP mod Befolkning farvekodet med BNP Vækst fra året før %, for at få et overblik over data
ggplot(data, aes(`BNP USD`, Befolkning, color = `BNP Vækst fra året før %`,label=rownames(data)))+
  geom_text(size=2,check_overlap = TRUE)
BNP pr indbygger mod arbejdsløshed farvekodet med Befolkning
BNP mod Befolkning farvekodet med BNP Vækst fra året før %

I det første plot ovenfor ses at Sydafrika har meget høj arbejdsløshed, Luxemborg har grundet lav beskatning et meget højt BNP pr. indbygger.
I det andet plot ovenfor kan vi se at USA, Kina og Indien skiller sig ud fra de øvrige observationer, pga. deres store befolkninger og høje BNP.
I det andet plot kan ikke vises særligt mange lande, da vi har valgt at navnene på landene skal vises, derfor kunne man f.eks. udelade outliers USA, Kina og Indien.
datalille i koden herunder er data uden disse 3 lande. Herefter genereres de samme 2 plots:


# For at kunne se alle de små lande fjerner vi Kina, USA og Indien og gemmer datasættet som datalille
datalille <- subset(data, !(rownames(data) %in% c("China", "India","United States")))
#Herunder er plot af BNP pr indbygger mod arbejdsløshed farvekodet med Befolkning, for at få et overblik over data
ggplot(datalille, aes(`BNP pr indbygger USD`, `Arbejdsløshed %`, color = Befolkning,label=rownames(datalille)))+
  geom_text(size=3,check_overlap = TRUE)
#Herunder er plot af BNP mod Befolkning farvekodet med BNP Vækst fra året før %, for at få et overblik over data
ggplot(datalille, aes(`BNP USD`, Befolkning, color = `BNP Vækst fra året før %`,label=rownames(datalille)))+
  geom_text(size=2,check_overlap = TRUE)

BNP pr indbygger mod arbejdsløshed farvekodet med Befolkning
BNP mod Befolkning farvekodet med BNP Vækst fra året før %

Nu kan vi f.eks. se Danmark i plot af BNP mod Befolkning, vi ligger relativt godt set fra et økonomisk perspektiv med højt BNP i forhold til vores lille befolkning. Men ikke så godt som f.eks. Norge og Schweiz. Herunder er et udsnit af plottet, vi kan se at mørkeblå lande som Belarus og Sri Lanka har negativ vækst (recession). Mens lyseblå lande som Niger, har meget høj vækst.

Herunder er koden til at plotte et hclust klyngetræ

          
clusters <- hclust(dist(data))
plot(clusters,cex=0.5,main = "Dendogram af Lande",xlab = "Klyngetræ",sub="Verdensøkonomien")
          
          

Herunder ses klyngetræet der er genereret i R, vi kan se at Kina og USA skiller sig meget ud, ligesom andre store økonomier som Japan, Tyskland, Indien, Frankrig og UK synes at afvige fra de øvrige lande.

Klyngetræ for Verdensøkonomien

Vi kan se af klyngetræet at vi mindst bør have 2-3 klynger. Benytter vi kmeans med

Vi kan nu danne klyngeplots for at få et bedre overblik.
Husk at loade pakken factoextra, hvis du skal tegne klyngeplots.
Herunder er R-koden til klyngeanalysen for 5 klynger med scale, et kald for at vise klyngernes standardiserede middelværdier, klyngestørrelser samt klyngeplottet.


library(factoextra)
# Klyngeanalysen gemmes i k5 herunder
k5 <- kmeans(scale(data), 5, nstart = 25)
# centers og size hentes fra k5
k5$centers
k5$size
# Klyngeplot dannes baseret på k5
fviz_cluster(k5,  data = data, main = "Klyngeplot lande opdelt i 5 klynger",repel = TRUE,labelsize = 4)

Vi kan af ovenstående værdier se, at klyngerne er opdelt ud fra følgende kriterier:

  • Klynge 1: Klynge 1 er den store røde mellemgruppe i klyngeplottet herunder med 81 lande, klyngen har ingen særlige karakteristika.
  • Klynge 2: Klynge 2 er den gule klynge i klyngeplottet herunder, bestående af lande med høj arbejdsløshed.
  • Klynge 3: Klynge 3 er den grønne klynge bestående af lande med højt BNP pr. indbygger, i denne klynge findes f.eks. Danmark, Norge og lande med gunstige skatteregler.
  • Klynge 4: Klynge 4 er den blå klynge med meget høj inflation og lav vækst.
  • Klynge 5: Klynge 5 er lille i plottet herunder og består af 3 lande (Indien, Kina og USA) med store befolkninger og højt absolut BNP.
Plot med 5 klynger for Verdensøkonomien

Vi udelader nu Kina, Indien og USA, ved at benyttet datalille datasættet vi dannede tidligere.
Herunder er koden til klyngeplot med 4 klynger, for det reducerede datasæt.


fviz_cluster(kmeans(scale(datalille), 4, nstart = 25),  data = datalille, main = "Klyngeplot lande opdelt i 4 klynger\nUden Indien, USA og Kina",repel = TRUE,labelsize = 4)

Herunder ses klyngeplottet der er genereret med kun 4 klynger i R for det datalille.

Plot med 4 klynger for Verdensøkonomien

Klynge 1: Danmark ligger i klynge 1, lande med mindre befolkninger, højt BNP pr. indbygger og relativt solide mindre økonomier. Havde vi fx. haft areal med som en variabel var vi nok ikke i klynge med Canada og Australien.
Klynge 2: Klynge 2 domineres af lande med større befolkninger med relativt stort BNP, hvor Nigeria og Pakistan ligger meget yderligt i klyngen.
Klynge 3: Klynge 3 består af lande med ringe økonomier, meget høj inflation, lav vækst. Tyrkiet er lidt overraskende i klynge 3, hvilket nok primært skyldes den meget høje inflation.
Klynge 4: Klynge 4 spænder noget bredere end de øvrige klynger, noget mindre og helt små lande mht. befolkninger og BNP, bedre økonomier end klynge 3, men ikke som klynge 1.

Der er ingen regler for hvor mange klynger man har med i sin klyngeanalyse, dette afhænger af hvor fintmasket analysen ønskes.
Herunder er koden til klyngeplot med hele 20 klynger, for det reducerede datasæt. Disse 20 klynger er ikke kommenteret, Danmark er nu ikke længere i det supergode selskab med f.eks. Norge og Schweiz.


fviz_cluster(kmeans(scale(datalille), 20, nstart = 25),  data = datalille, main = "Klyngeplot lande opdelt i 20 klynger\nUden Indien, USA og Kina",repel = TRUE,labelsize = 4)

Herunder ses klyngeplottet der er genereret med hele 20 klynger i R for det datalille.

Plot med 20 klynger for Verdensøkonomien
Vi ser her på et datasæt, bestående af et udvalg af 53 større revisionsselskaber, der er målt på 17 variable.

Datasæt med revisionsselskabers regnskaber

Forklaring af datasættet
Variabel Forklaring
Firmanavn Revisionsselskabets navn
Postnr. Postnummeret hvor selskabet er hjemmehørende
Antal ansatte Hvor mange ansatte er registreret i virksomheden
Etableringsår Hvilket år er revisionsselskabet registreret
Bruttofortjeneste (1.000 kr) Indtægter fra revisionsydelser minus direkte omkostninger (f.eks. lønninger til revisorer, omkostninger til revisionsteknologi etc.).
Primært Resultat (1.000 kr) Indtjening fra kerneaktiviteter (revision og rådgivning) før renter, skatter og andre finansielle poster.
Årets nettoresultat (1.000 kr) Total indtjening efter fradrag af alle omkostninger, skatter og renteudgifter.
Overført resultat (1.000 kr) Den del af årets resultat, der ikke udbetales som udbytte, men overføres til næste regnskabsår.
Egenkapital (1.000 kr) Ejernes investering i firmaet plus akkumuleret overskud, efter fradrag af eventuelle tab.
Balance (1.000 kr) Finansiel opgørelse, der viser firmaets aktiver, forpligtelser og egenkapital på et givet tidspunkt.
Kapacitetsgrad (%) Mål for, hvor meget af firmaets totale kapacitet (f.eks. revisorers arbejdstimer) der faktisk anvendes.
Afkastningsgrad (%) Hvor effektivt firmaet anvender sine aktiver til at generere indtjening.
Likviditetsgrad (%) Firmaets evne til hurtigt at omdanne aktiver til likvide midler for at dække kortfristede forpligtelser.
Soliditetsgrad (%) Andel af firmaets aktiver, der er finansieret med egenkapital, et mål for finansiel stabilitet.
Egenkapitalforrentning (%) Afkast på ejernes investering, beregnet som nettoresultatet divideret med egenkapitalen.
Sikkerhedsmargin (%) Hvor meget omsætningen kan falde, før virksomheden ikke længere er rentabel.
Indre værdi (%) En estimeret værdi af firmaets aktie, baseret på dets aktiver og fremtidige indtjeningspotentiale.

Tabel 21.3: Variabeloversigt for datasættet med 53 revisionsselskaber — 17 regnskabs- og nøgletalsvariable (bruttofortjeneste, egenkapital, afkastningsgrad, soliditetsgrad m.fl.), der bruges til at klynge selskaberne.
Kilde: Egen tilvirkning baseret på datasættet "Revisionsselskaber 2023".

Herunder kan du se koden for at bibeholde, revisionsselskabernes navne i R, så disse er med i output.

      
# Vi omdøber til data efter importen af Excelfilen i R
data <- data.frame(Revisionsselskaber_2023)
# Rækkenavne gemmes i variablen row_names
row_names <- Revisionsselskaber_2023[,1]
# data får rækkenavne
rownames(data) <- t(row_names)
# 1. søjle med rækkenavne fjernes fra datasættet, disse er nu gemt som rækkenavne.
data <- data[,-1]
          
          

R-kode til at tegne, dendogrammet/klyngetræet for revisionsselskaberne ses herunder.


clusters <- hclust(dist(data))
plot(clusters,cex=0.25,main = "Dendogram",xlab = "Klyngetræ",sub="revisorer")

Dendogrammet/klyngetræet for revisionsselskaberne kan ses herunder.

Dendogrammet for revisionsselskaber

I løsningsforslaget er valgt en klyngeanalyse med 5 klynger. Herunder er R-kode og output.


library(factoextra)
k5 <- kmeans(scale(data), 5, nstart = 25)
k5$size
k5$centers
fviz_cluster(k5,  data = data, main = "Klyngeplot revisorer opdelt i 5 klynger",repel = TRUE,labelsize = 4)
    

Herunder ses R-output for k5$size og k5$centers. Vi har 3 mindre klynger og 2 større. Store og små værdier for de skalerede klyngegennemsnit er markeret med hhv. grønne og røde rammer.

Vi kan af centers bestemme følgende karakteristika for de 5 klynger:
Klynge 1: Lille klynge af 2 store revisionsselskaber (KPMG og Beierholm) målt på Antal ansatte Bruttofortjeneste, Egenkapital og Balance. Dog har selskaberne en lavere Afkastningsgrad, Soliditetsgrad og Egenkapitalforrentning.
Klynge 2: Klynge af 5 lidt ældre revisionsselskaber i Københavnsområdet (lav værdi af Postnr.) med flotte resultater og Egenkapitalforrentning.
Klynge 3: Stor klynge af revisionsselskaber kendetegnet ved høje Soliditetsgrader, disse selskaber har altså primært finansieret sig ved egenkapital.
Klynge 4: Denne klynge har relativt ringere resultater end de øvrige klynger.
Klynge 5: Lille klynge af forholdsmæssigt veldrevne revisionsselskaber med høj Egenkapitalforrentning og Afkastningsgrad.

Klyngeplottet for revisionsselskaber
Vi ser her på et datasæt, bestående af 357 ultrarige amerikanere, der er målt på 9 variable.

Datasæt med US superrige

US_Rich datasættet
Variabel Forklaring på de 9 variable
Name/Company Navn på personen, samt hvilket selskab vedkommende primært er kendt for.
age Hvor gammel er personen.
finalWorth mio. USD Formue i mio USD, er værdien her f.eks. 1.000 betyder det at personen har en formue på 1 mia. USD.
selfmade_dummy Har personen selv skabt formuen er værdien 1, ellers 0.
female_dummy 1 hvis kvinde, 0 hvis mand.
philanthropyScore En rang der angiver hvor meget vedkommende giver til godgørende formål. Hvor 1 er lavest, 5 er højest.
tech_dummy Er formuen primært skabt i teknologi sektoren.
finance_dummy Er formuen primært skabt i den finansielle sektor.
food_dummy Er formuen primært skabt i sektoren indenfor føde- og drikkevarer.

Tabel 21.4: Variabeloversigt for US_Rich-datasættet — 9 variable (alder, formue, selfmade-status, køn, filantropi-score og branchedummies) for 357 ultrarige amerikanere, der skal grupperes i klynger.
Kilde: Egen tilvirkning baseret på datasættet "US_Rich".

      
# Vi omdøber
data <- data.frame(US_Rich)
# Rækkenavne gemmes i variablen row_names
row_names <- US_Rich[,1]
# data får rækkenavne
rownames(data) <- t(row_names)
# 1. søjle med rækkenavne fjernes fra datasættet, disse er nu gemt som rækkenavne.
data <- data[,-1]
      
      

Dendogrammet klyngetræet er lidt svært at se navnene ordentligt, da der er 357 personer med i datasættet, dette er ses herunder, hvis man zoomer ind kan man læse navnene.

Dendogrammet for US superrige

I løsningsforslaget er valgt en klyngeanalyse med 5 klynger.


library(factoextra)
k5 <- kmeans(scale(data), 5, nstart = 25)
k5$size
k5$centers
fviz_cluster(k5,  data = data, main = "Klyngeplot superrige opdelt i 5 klynger",repel = TRUE,labelsize = 4)

Herunder ses R-output for k5$size og k5$centers. Vi har 2 store klynger, 1 og med 137 superrige og 2 med 91 superrige.

Vi kan af centers bestemme følgende karakteristika for de 5 klynger:
Klynge 1: Stor klynge af lidt ældre superrige, der er ellers ingen særlige kendetegn udover at de ikke har tjent formuen indenfor tech, finans eller food.
Klynge 2: Større klynge der primært har tjent formuen indenfor finans.
Klynge 3: De mindst velhavende af de superrige, de giver ikke noget særligt til velgørenhed og har tjent formuen i fødevareindustrien.
Klynge 4: Primært kvinder i denne klynge, respondenterne i klyngen har typisk arvet formuen.
Klynge 5: Yngre ultra superrige med selvskabte formuer primært tjent i tech-industrien

Klyngeplottet for US superrige 5 klynger

Bemærk hvor tydeligt karakteristika kan aflæses i de 5 klynger herover. Gul klynge 2 med finansfolk, Blå klynge 4, med mange kvinder, Lilla klynge 5 med unge tech ultrarige tech folk.


Lav en klyngeanalyse for datasæt med 50 observationer for amerikanske stater på 4 variable, data stammer fra World Almanac and Book of facts 1975. (Crime rates).

Bemærk når du henter data fra Excel er rækkenavne som regel bare numre 1,2,3... i stedet for fx. Alabama, Arizona,...
For at angive rækkenavne fra første søjle i Excel regnearket kan man benytte rownames-kommandoen i R, som nedenfor.

      
  # Tjek hvilke rækkenavne dit datasæt har, når du har importeret fra Excel
  rownames(usarrest)
# Fjern første kolonne med navnene på staterne i det importerede datasæt, kald usarrest for data data <- usarrest[,-1]
# Lav data til en dataframe data <- as.data.frame(data) # Angiv at data's rækkenavne skal være staterne i USA, så der ikke blot står et rækkenummer rownames(data) <- t(usarrest[,1])
# Tjek at navnene i dit datasæt nu er staterne rownames(data)

Excelfilen om USArrests

Datasættet består af følgende 4 variable:
1. Mord, antal arrestationer (pr 100,000)
2. Overfald, antal arrestationer (pr 100,000)
3. Urbaniseringsgrad andel af bybefolkning.
4. Voldtægt, antal arrestationer (pr 100,000)


pacman::p_load(datasets)
arrest <- USArrests
head(arrest)
Murder Assault UrbanPop Rape Alabama 13.2 236 58 21.2 Alaska 10.0 263 48 44.5 Arizona 8.1 294 80 31.0 Arkansas 8.8 190 50 19.5 California 9.0 276 91 40.6 Colorado 7.9 204 78 38.7 Undersøg om der kan dannes klynger og hvorledes disse kan karakteriseres. Illustrer grafisk og kommenter på karakteristika for klyngerne.

Du har nu adgang

Alle kapitler er åbne.