2014-12-16 6 views
8

Ich möchte mit dem Circlize-Paket ein Akkorddiagramm erstellen. Ich habe einen Datenrahmen, der Autos mit vier Spalten enthält. Die zwei ersten Spalten enthalten Informationen über die Autoband- und Modellbesitz- und die nächsten zwei Spalten über die Marke und das Modell, zu dem der Befragte gewandert ist.R Kreis/Akkorddiagramm mit Circlize aus Datenframe erstellen

Hier ist ein einfaches Beispiel für den Datenrahmen:

Brand_from model_from Brand_to Model_to 
1:  VOLVO  s80  BMW 5series 
2:  BMW 3series  BMW 3series 
3:  VOLVO  s60 VOLVO  s60 
4:  VOLVO  s60 VOLVO  s80 
5:  BMW 3series  AUDI  s4 
6:  AUDI   a4  BMW 3series 
7:  AUDI   a5  AUDI  a5 

Es wäre toll, die Lage sein, dies in ein Akkord-Diagramm zu machen. Ich fand ein Beispiel in der Hilfe, die funktionierte, aber ich bin nicht in der Lage, meine Daten in das richtige Format zu konvertieren, um die Handlung zu machen. Dieser Code stammt aus der Hilfe im Circlize-Paket. Dies erzeugt eine Schicht, ich denke, ich brauche zwei, Marke und Modell.

mat = matrix(1:18, 3, 6) 
rownames(mat) = paste0("S", 1:3) 
colnames(mat) = paste0("E", 1:6) 

rn = rownames(mat) 
cn = colnames(mat) 
factors = c(rn, cn) 
factors = factor(factors, levels = factors) 
col_sum = apply(mat, 2, sum) 
row_sum = apply(mat, 1, sum) 
xlim = cbind(rep(0, length(factors)), c(row_sum, col_sum)) 

par(mar = c(1, 1, 1, 1)) 
circos.par(cell.padding = c(0, 0, 0, 0)) 
circos.initialize(factors = factors, xlim = xlim) 
circos.trackPlotRegion(factors = factors, ylim = c(0, 1), bg.border = NA, 
         bg.col = c("red", "green", "blue", rep("grey", 6)), track.height = 0.05, 
         panel.fun = function(x, y) { 
         sector.name = get.cell.meta.data("sector.index") 
         xlim = get.cell.meta.data("xlim") 
         circos.text(mean(xlim), 1.5, sector.name, adj = c(0.5, 0)) 
}) 

col = c("#FF000020", "#00FF0020", "#0000FF20") 
for(i in seq_len(nrow(mat))) { 
    for(j in seq_len(ncol(mat))) { 
    circos.link(rn[i], c(sum(mat[i, seq_len(j-1)]), sum(mat[i, seq_len(j)])), 
       cn[j], c(sum(mat[seq_len(i-1), j]), sum(mat[seq_len(i), j])), 
       col = col[i], border = "white") 
    } 
} 
circos.clear() 

Dieser Code erzeugt die folgende Handlung:

enter image description here

Ideale Ergebnis wie in diesem Beispiel sein würde, aber statt Kontinenten würde ich Automarke und auf dem inneren Kreis wie die Automodelle, die zu die Marke

Antwort

8

Als ich das Paket ein wenig aktualisiert, gibt es jetzt einen einfacheren Weg, es zu tun. Ich werde hier eine andere Antwort geben, falls jemand daran interessiert ist.

In den neuesten mehrere Versionen von circlize akzeptiert chordDiagram() beide Adjazenzmatrix und Adjazenzliste als Eingabe, was bedeutet, können Sie jetzt einen Datenrahmen schaffen, die auf die Funktion paarweise Beziehung enthält. Außerdem gibt es eine highlight.sector() Funktion, die mehr als einen Sektor zur gleichen Zeit markieren oder markieren kann.

Ich werde die Handlung umzusetzen, die ich vorher zeigte aber mit kürzerem Code:

df = read.table(textConnection(" 
brand_from model_from brand_to model_to 
     VOLVO  s80  BMW 5series 
     BMW 3series  BMW 3series 
     VOLVO  s60 VOLVO  s60 
     VOLVO  s60 VOLVO  s80 
     BMW 3series  AUDI  s4 
     AUDI   a4  BMW 3series 
     AUDI   a5  AUDI  a5 
"), header = TRUE, stringsAsFactors = FALSE) 

brand = c(structure(df$brand_from, names=df$model_from), 
      structure(df$brand_to,names= df$model_to)) 
brand = brand[!duplicated(names(brand))] 
brand = brand[order(brand, names(brand))] 
brand_color = structure(2:4, names = unique(brand)) 
model_color = structure(2:8, names = names(brand)) 

Der Wert für brand, brand_color und model_color ist:

> brand 
    a4  a5  s4 3series 5series  s60  s80 
"AUDI" "AUDI" "AUDI" "BMW" "BMW" "VOLVO" "VOLVO" 
> brand_color 
AUDI BMW VOLVO 
    2  3  4 
> model_color 
    a4  a5  s4 3series 5series  s60  s80 
     2  3  4  5  6  7  8 

Diesmal fügen wir nur eine zusätzliche Spur, die Linien und Markennamen setzt. Und Sie können auch finden, dass die Eingabevariable tatsächlich ein Datenrahmen ist (df[, c(2, 4)]).

library(circlize) 
gap.degree = do.call("c", lapply(table(brand), function(i) c(rep(2, i-1), 8))) 
circos.par(gap.degree = gap.degree) 

chordDiagram(df[, c(2, 4)], order = names(brand), grid.col = model_color, 
    directional = 1, annotationTrack = "grid", preAllocateTracks = list(
     list(track.height = 0.02)) 
) 

Selben wie die zuvor werden die Modellnamen manuell hinzugefügt:

circos.trackPlotRegion(track.index = 2, panel.fun = function(x, y) { 
    xlim = get.cell.meta.data("xlim") 
    ylim = get.cell.meta.data("ylim") 
    sector.index = get.cell.meta.data("sector.index") 
    circos.text(mean(xlim), mean(ylim), sector.index, col = "white", cex = 0.6, facing = "inside", niceFacing = TRUE) 
}, bg.border = NA) 

Am Ende fügen wir die Linien und die Markennamen von highlight.sector() Funktion. Hier kann der Wert sector.index ein Vektor mit einer Länge von mehr als 1 sein und die Linie (oder ein dünnes Rechteck) wird alle angegebenen Sektoren abdecken. Eine Marke wird in der Mitte der Sektoren hinzugefügt und die radikale Position wird durch die Option text.vjust gesteuert.

for(b in unique(brand)) { 
    model = names(brand[brand == b]) 
    highlight.sector(sector.index = model, track.index = 1, col = brand_color[b], 
    text = b, text.vjust = -1, niceFacing = TRUE) 
} 

circos.clear() 

enter image description here

+0

Das ist großartig. Ein Wort der Warnung für die Benutzer: die '2: 4' und' 2: 8' von brand_color und model_color sind fest codiert und wenn Sie Ihre eigenen Daten verwenden, müssten Sie dynamisch als zB model_color = structure (seq (2, Länge (Namen (Marke)) + 1), Namen = Namen (Marke)) ' – JustinJDavies

-1

Lesen Sie Ihre Daten mit read.table, resultierend in 7x4 data.frame (brand.txt sollte Tab getrennt sein).

mt <- read.table("//your-path/brand.txt",header=T,sep="\t",na.string="NA") 

Ihr Variablen-Name (mt) ist: "Brand_from", "model_from", "Brand_to" und "Model_to". Wählen Sie zwei Variablen von Interesse , zum Beispiel:

mat <- table(mt$Brand_from, mt$model_from) 

Dies ist in der folgenden Tabelle ergibt:

# >mat 
#  3series a4 a5 s60 s80 
# AUDI  0 1 1 0 0 
# BMW   2 0 0 0 0 
# VOLVO  0 0 0 2 1 

Dann können Sie alles gleich aus „rn = rownames laufen (mat) "wie Sie in Ihrem Circlize - Skript angegeben haben

enter image description here

+0

versuchte ich Ihren Vorschlag, aber ich habe diese Fehlermeldung läuft die circos.trackPlotRegion Funktion: Fehler in if (NCut) {: Argument ist nicht interpretierbar als logische – jonas

+0

Vielleicht prüfen, ob Matte das entspricht eine, die ich in der Antwort angegeben habe. Für Ihre Infos: Ich verwende "R Version 3.0.2 (2013-09-25)". –

+0

Ich erhalte den Fehler, wenn ich die Tabelle mit den Markenspalten ausprobiere. Es funktioniert gut auf Marke zu modellieren, wie in Ihrem Fall, aber ich würde gerne die Migration von Marken – jonas

8

Der Schlüssel hier ist, Ihre Daten in eine Matrix zu konvertieren (Adjazenzmatrix in welche Zeilen entsprechen von 'und' Spalten entsprechen 'zu').

df = read.table(textConnection(" 
Brand_from model_from Brand_to Model_to 
     VOLVO  s80  BMW 5series 
     BMW 3series  BMW 3series 
     VOLVO  s60 VOLVO  s60 
     VOLVO  s60 VOLVO  s80 
     BMW 3series  AUDI  s4 
     AUDI   a4  BMW 3series 
     AUDI   a5  AUDI  a5 
"), header = TRUE, stringsAsFactors = FALSE) 

from = paste(df[[1]], df[[2]], sep = ",") 
to = paste(df[[3]], df[[4]], sep = ",") 

mat = matrix(0, nrow = length(unique(from)), ncol = length(unique(to))) 
rownames(mat) = unique(from) 
colnames(mat) = unique(to) 
for(i in seq_along(from)) mat[from[i], to[i]] = 1 

Wert von mat ist

> mat 
      BMW,5series BMW,3series VOLVO,s60 VOLVO,s80 AUDI,s4 AUDI,a5 
VOLVO,s80    1   0   0   0  0  0 
BMW,3series   0   1   0   0  1  0 
VOLVO,s60    0   0   1   1  0  0 
AUDI,a4    0   1   0   0  0  0 
AUDI,a5    0   0   0   0  0  1 

Dann um die Matrix zu chordDiagram mit Angabe order und directional senden. Manuelle Spezifikation von order ist, um sicherzustellen, dass die gleichen Marken zusammen gruppiert sind.

par(mar = c(1, 1, 1, 1)) 
chordDiagram(mat, order = sort(union(from, to)), directional = TRUE) 
circos.clear() 

Um die Zahl komplexer zu machen, Sie einen Titel für Markennamen erstellen können, eine Spur für identication von Marken, eine Spur für die Modellnamen. Auch wir können die Lücke zwischen Marken größer als innerhalb jeder Marke setzen.

1 Satz gap.degree

circos.par(gap.degree = c(2, 2, 8, 2, 8, 2, 8)) 

2 vor Akkord-Diagramm zeichnen, erstellen wir zwei leere Spuren, eine für Markennamen, eine für die Identifizierung Linien durch preAllocateTracks Argument.

par(mar = c(1, 1, 1, 1)) 
chordDiagram(mat, order = sort(union(from, to)), 
    direction = TRUE, annotationTrack = "grid", preAllocateTracks = list(
     list(track.height = 0.02), 
     list(track.height = 0.02)) 
) 

3 den Modellnamen an den Anmerkungsspur hinzufügen (dieser Titel wird standardmäßig erstellt, die dickere Spur in den linken und rechten Figuren. Beachten Sie dies innerhalb der dritten Spur von Außenkreis ist)

circos.trackPlotRegion(track.index = 3, panel.fun = function(x, y) { 
    xlim = get.cell.meta.data("xlim") 
    ylim = get.cell.meta.data("ylim") 
    sector.index = get.cell.meta.data("sector.index") 
    model = strsplit(sector.index, ",")[[1]][2] 
    circos.text(mean(xlim), mean(ylim), model, col = "white", cex = 0.8, facing = "inside", niceFacing = TRUE) 
}, bg.border = NA) 

4 Markenidentifikationslinie hinzufügen. Da die Marke mehr als einen Sektor abdeckt, benötigen wir , um den Start- und Endgrad für die Linie (Bogen) manuell zu berechnen. Im folgenden sind rou1 und rou2 Höhe von zwei Grenzen in der zweiten Spur. Die Identifikationslinien werden in der zweiten Spur gezeichnet.

all_sectors = get.all.sector.index() 
rou1 = get.cell.meta.data("yplot", sector.index = all_sectors[1], track.index = 2)[1] 
rou2 = get.cell.meta.data("yplot", sector.index = all_sectors[1], track.index = 2)[2] 

start.degree = get.cell.meta.data("xplot", sector.index = all_sectors[1], track.index = 2)[1] 
end.degree = get.cell.meta.data("xplot", sector.index = all_sectors[3], track.index = 2)[2] 
draw.sector(start.degree, end.degree, rou1, rou2, clock.wise = TRUE, col = "red", border = NA) 

5 zuerst erhalten die in dem polaren Textkoordinatensystem, dann Karte, um Daten System durch reverse.circlize koordinieren. Beachten Sie, dass die Zelle, die Sie zuordnen, die Koordinate zurückgibt und dass die Zelle, die Sie zeichnen, dieselbe Zelle sein soll.

m = reverse.circlize((start.degree + end.degree)/2, 1, sector.index = all_sectors[1], track.index = 1) 
circos.text(m[1, 1], m[1, 2], "AUDI", cex = 1.2, facing = "inside", adj = c(0.5, 0), niceFacing = TRUE, 
    sector.index = all_sectors[1], track.index = 1) 

Für die anderen beiden Marken, mit dem gleichen Code.

start.degree = get.cell.meta.data("xplot", sector.index = all_sectors[4], track.index = 2)[1] 
end.degree = get.cell.meta.data("xplot", sector.index = all_sectors[5], track.index = 2)[2] 
draw.sector(start.degree, end.degree, rou1, rou2, clock.wise = TRUE, col = "green", border = NA) 
m = reverse.circlize((start.degree + end.degree)/2, 1, sector.index = all_sectors[1], track.index = 1) 
circos.text(m[1, 1], m[1, 2], "BMW", cex = 1.2, facing = "inside", adj = c(0.5, 0), niceFacing = TRUE, 
    sector.index = all_sectors[1], track.index = 1) 

start.degree = get.cell.meta.data("xplot", sector.index = all_sectors[6], track.index = 2)[1] 
end.degree = get.cell.meta.data("xplot", sector.index = all_sectors[7], track.index = 2)[2] 
draw.sector(start.degree, end.degree, rou1, rou2, clock.wise = TRUE, col = "blue", border = NA) 
m = reverse.circlize((start.degree + end.degree)/2, 1, sector.index = all_sectors[1], track.index = 1) 
circos.text(m[1, 1], m[1, 2], "VOLVO", cex = 1.2, facing = "inside", adj = c(0.5, 0), niceFacing = TRUE, 
    sector.index = all_sectors[1], track.index = 1) 

circos.clear() 

Wenn Sie Farben einstellen möchten, gehen Sie bitte die Packungs Vignette, wenn Sie möchten, können Sie auch circos.axis verwenden Achsen auf dem Grundstück hinzuzufügen.

enter image description here

Verwandte Themen