2014-12-16 3 views
8

Я хотел бы сделать диаграмму аккорда, используя круговой пакет. У меня есть dataframe, содержащий автомобили с четырьмя столбцами. В двух первых столбцах содержится информация о автомобильной полосе и модели, а также следующие две колонки для бренда и модель, на которую перенаправлен респондент.R сделать круг/диаграмму аккорда с оцифровкой из dataframe

Вот простой пример dataframe:

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 

Было бы здорово, чтобы быть в состоянии сделать это в хордовой диаграммы. Я нашел пример в справке, которая работала, но я не могу преобразовать свои данные в правильный формат, чтобы сделать сюжет. Этот код из справки в пакете circlize. Это создает один слой, я думаю, мне нужны два, бренд и модель.

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

Этот код производит следующий сюжет:

enter image description here

Идеальный результат будет как в этом примере, но вместо материков я хотел автомобиль марки и на внутреннем круге моделей автомобилей, принадлежащих к марка enter image description here

ответ

8

Как я обновил пакет немного, теперь есть более простой способ сделать это. Я дам еще один ответ здесь, если кто-то заинтересован в этом.

В последние нескольких версиях circlize, chordDiagram() принимает как матрицу смежности и список смежности в качестве входных данных, а значит, теперь вы можете предоставить кадр данных, который содержит парное отношение к функции. Также есть функция highlight.sector(), которая может выделять или отмечать более одного сектора одновременно.

я реализую сюжет, который я показал раньше, но с более коротким кодом:

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

Значение brand, brand_color и model_color являются:

> 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 

На этот раз, мы только добавить один дополнительный трек, который ставит линии и фирменные наименования. А также вы можете найти входную переменную на самом деле фреймом данных (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)) 
) 

То же, что и раньше, названия моделей добавлены вручную:

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) 

В конце концов, мы добавим строки и названия брендов по highlight.sector() функции. Здесь значение sector.index может быть вектором длиной более 1, а линия (или тонкий прямоугольник) будет охватывать все указанные сектора. Метка будет добавлена ​​в середине секторов, а положение радикала контролируется опцией text.vjust.

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

Это замечательно. Слово предупреждения для пользователей: «2: 4» и «2: 8» brand_color и model_color жестко закодированы, и если вы используете свои собственные данные, их нужно будет динамически кодировать, например, 'model_color = structure (seq (2, длина (имена (бренд)) + 1), names = names (brand)) ' – JustinJDavies

-1

Читайте в своих данных, используя таблицу read.table, в результате получается 7x4 data.frame (brand.txt следует разделить на вкладку).

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

переменные имена (мт), являются: "Brand_from", "model_from", "Brand_to" и "Model_to". Выберите ваши две переменные, представляющие интерес, например:

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

Это приводит к следующей таблице:

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

Затем вы можете запустить все то же самое с «гп = rownames (мат) ", как вы указали в своем круговом письме

enter image description here

+0

Я попробовал ваше предложение, но я получил сообщение об ошибке выполняется функция circos.trackPlotRegion: Ошибка в случае (ncut) {: аргумент не интерпретирован в качестве логического – jonas

+0

Может проверить, если мат соответствует один из которых я дал в ответ. Для вашей информации: я использую «R версии 3.0.2 (2013-09-25)». –

+0

Я получаю сообщение об ошибке при попытке таблицы, содержащей столбцы брендов. Он отлично работает на модели, как в вашем случае, однако я хотел бы перейти от брендов – jonas

8

Ключевым здесь является преобразование ваших данных в матрицу (матрица смежности в , строки которой соответствуют «от», а столбцы соответствуют «по»).

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 

Значение mat является

> 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 

Затем отправить матрицу chordDiagram с указанием order и directional. Ручная спецификация order - убедиться, что одинаковые марки сгруппированы.

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

Чтобы сделать фигуру более сложным, Вы можете создать трек для фирменных наименований, трека для identication брендов, трек для названия моделей. Также может установить разрыв между брендами больше, чем внутри каждой марки.

1 комплект gap.degree

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

2 перед нанесением хордовой диаграммы, мы создаем две пустые дорожки, один для фирменных наименований, один для идентификации линий по preAllocateTracks аргумента.

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 добавить название модели в аннотации дорожки (этот трек создается по умолчанию, толще след в обоих левых и правых фигур. Обратите внимание, что это третий трек от внешнего круга внутрь)

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 добавить идентификационную линию бренда. Поскольку бренд охватывает более одного сектора, нам нужно , чтобы вручную рассчитать начальную и конечную степень для линии (дуги). При этом rou1 и rou2 - это высота двух границ во второй дорожке. Линии выдачи рисуются на втором треке.

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 первых получить координаты текста в полярной системе координат, а затем сопоставить данные координат систему reverse.circlize. Обратите внимание на ячейку, на которую вы скопируете координату, и ячейка, которую вы рисуете, должна быть той же ячейкой.

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) 

Для двух других марок, с тем же кодом.

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

Если вы хотите, чтобы установить цвета, пожалуйста, перейдите в пакет виньетка, если вы хотите, вы можете также использовать circos.axis добавить осей на участке.

enter image description here

 Смежные вопросы

  • Нет связанных вопросов^_^