# Observable Plot: How to map the density map to both sides?

Hi, all
I drew a density map and now I want to add density estimates for each axis separately. What should I do?
The target style is as follows:

Thank you for your early help.

Great question. There have been several attempts already, for examples see

We don’t yet have a satisfying answer though for the exact case you’re after, because we don’t have a handy 1d-density mark (see 1-d KDE transform? · Issue #1469 · observablehq/plot · GitHub), and also because this would also need to use multiple scales (one for the umap dimension, the other for the density) for each dimension.

It’s still possible, by arranging several charts side by side (Chart with a marginal histogram / recifs | Observable), but it’s a bit too much work.

Thank you for your answer. I just carefully read the document of plot.js and found that it contains a bin option. Then, I added it and used relative positioning to combine the original image with it. The results are as follows:

And I have reviewed the link you just shared , your method is more concise. I will discuss with the leader to select the appropriate solution. Thank you very much

I just tried the method you shared and found that the performance has deteriorated, it is very sluggish, and the browser has been loading. Why?

`````` Plot.dot(
data,
Plot.dodgeY({
x: "umap_x",
anchor: "bottom",
r: 1.5,
fill: this.colored,
})
),
Plot.dot(
data,
Plot.dodgeX({
y: "umap_y",
anchor: "right",
r: 1.5,
fill: this.colored,
})
),
Plot.density(data, {
x: "umap_x",
y: "umap_y",
stroke: this.colored,
strokeOpacity: 0.8,
strokeWidth: 0.3,
clip: true,
thresholds: 10,
}),
Plot.dot(data, {
clip: true,
x: "umap_x",
y: "umap_y",
log2R: "log2R",
cell: "cell",
lib: "lib",
cell_designed: "cell_designed",
index: "index",
// stroke:"cell",
source: "source",
source_type: "source_type",
fill: this.colored,
fillOpacity: 0.7,
r: 3,
channels: {
x: "umap_x",
y: "umap_y",
log2R: "log2R",
cell: "cell",
source: "source",
source_type: "source_type",
lib: "lib",
cell_designed: "cell_designed",
index: "index",
},
tip: { lineHeight: 1.3, maxRadius: 5 },
}),
Plot.dot(data, {
x: "umap_x",
y: "umap_y",
filter: (d) => d.index == Number(this.index),
r: 17,
symbol: "square",
fill: this.colored,
}),

Plot.dot(top, {
clip: true,
x: "umap_x",
y: "umap_y",
fill: this.colored == "lib" ? "lib" : "cell",
log2R: "log2R",
cell: "cell",
lib: "lib",
cell_designed: "cell_designed",
index: "index",
// stroke:"cell",
source: "source",
source_type: "source_type",
channels: {
x: "umap_x",
y: "umap_y",
log2R: "log2R",
cell: "cell",
source: "source",
source_type: "source_type",
lib: "lib",
cell_designed: "cell_designed",
index: "index",
},
// anchor: "right"
tip: { lineHeight: 1.3, maxRadius: 5 },
r: 10,
symbol: "star",
}),

Plot.axisY({
tickSpacing: 50,
}),
Plot.axisX({
tickSpacing: 50,
}),
Plot.gridX({ tickSpacing: 10 }),
Plot.gridY({ tickSpacing: 10 }),
Plot.ruleY([0], { strokeOpacity: 0.5 }),
Plot.ruleX([0], { strokeOpacity: 0.5 })
``````

Can you share a notebook? SVG performance (in the browser) starts lagging when you reach ~10 k nodes, so I wouldn’t be surprised if you have a large dataset. (There are always methods to make things faster once you identify the bottleneck.)

Sorry, I don’t understand how to share a notebook. I’m just getting started with this website. I guess it’s because my dataset is too large, with 76000 data in it.

Yes, I don’t think the dodge transform can accommodate that many points, unless you set r to a very small number; and then, the browser will not be able to display all the dots which are created as svg circle nodes.

This is a case where you have to do aggregation, i.e. binning to create bars or a density line.

Okay, then I can only use the bin transform. Sincerely thank you for your help.