Making a Sankey diagram in R for printing - r

I am trying to make a Sankey diagram using R for a printed report. I have currently used 2 methods to do this but neither is achieving quite what I wanted.
The first uses the Riverplot function and produces this:
My problems with this plot is that I can't work out how to control the whitespace around the plot (it appears to be fixed and does not adjust to different sized datasets) or the positioning of the annotated text. I want to repeat this plot for a number of similar but differently sized datasets but the text placing is very variable in its positioning. This is the code I am currently using to add the text annotations:
riverplot(a, srt = 0)
y_lim<-par("yaxp") #gets y axis limits to spicify text placements
# text(1.5,y_lim[1], Name,font=2) #labels plot with Name (LA)
text(1,y_lim[1],"2004")
text(2,y_lim[1],"2010")
The second uses the rCharts and produces this:
My problems with this are (a) I would like to fix the order of the categories on each side (as in the first image) and (b) I would like to annotate the diagram as in the first image.
My questions are:
How I can fine tune the formatting of the Riverplot figure (first image), or,
How do I add annotations to the rCharts Sankey plot (I'm guessing this could be through the script section but I am new to rCharts, d3 and javascript), and,
Is it possible to fix the order of the categories int the rCharts sankey plot.

After a lot of tinkering and pinching code from a bunch of places, I have managed to produce what I wanted (solving point 2 of my original question).
The code could be refined in a number of places and could probably be done more elegantly but I have included it below in case it helps anyone else.
sankeyPlot$setTemplate(
afterScript = "
<script>
var cscale = d3.scale.ordinal()
.domain(['N','E', 'G' ,'I','T','N ','E ', 'G ' ,'I ','T '])
.range(['#d3d3d3', '#32ca32', '#1f78b4', '#e31a1c','#ecd736','#d3d3d3', '#32ca32', '#1f78b4', '#e31a1c','#ecd736'])
d3.selectAll('#{{ chartId }} svg path.link')
.style('stroke', function(d){
return cscale(d.source.name);
//returns grey links
})
d3.selectAll('#{{ chartId }} svg .node rect')
.style('fill', function(d){
return cscale(d.name)
})
.style('stroke', 'none')
var text_box = d3.selectAll('#{{ chartId }}').append('svg')
.attr('width', 500)
.attr('height', 100)
var TextData = [
{ 'cx': 0, 'cy': 20 ,'label': 'Type A', 'pos': 'left'},
{ 'cx': 250, 'cy': 20,'label': 'Label','pos': 'middle'},
{ 'cx': 500, 'cy': 20, 'label': 'Type B','pos': 'end'}];
var text = text_box.selectAll('text')
.data(TextData)
.enter()
.append('text');
//Add the text attributes
var textLabels = text
.attr('x', function(d) { return d.cx; })
.attr('y', function(d) { return d.cy; })
.text( function (d) { return d.label ; })
.attr('text-anchor', function(d) { return d.pos ;})
.attr('font-family', 'sans-serif')
.attr('font-size', '14px')
.attr('fill', 'black');
</script>
")

Related

Styling a marker in LightningChart JS

I am adding a list of markers to my chart based of certain conditions, these conditions are varying and therefore I will need to have markers that look different:
(using the setGridStrokeXStyle function doesn't seem to have an effect. What is the correct way to accomplish this?)
Markers.map((marker) => {
const chartMarker = chart.addChartMarkerXY()
.setPosition({ x: new Date(marker.x) - startDate, y: marker.y });
chartMarker
.setResultTableVisibility(UIVisibilityModes.always)
.setResultTable((table) => table
.setContent([
['Type']
])
)
.setGridStrokeXVisibility(UIVisibilityModes.whenDragged)
.setGridStrokeYVisibility(UIVisibilityModes.whenDragged)
.setTickMarkerXVisibility(UIVisibilityModes.whenDragged)
.setTickMarkerYVisibility(UIVisibilityModes.whenDragged)
.setDraggingMode(UIDraggingModes.notDraggable)
.setGridStrokeXStyle(new SolidLine({ fillStyle: new SolidFill({ color: ColorHEX("#FF69B4"), thickness: 2 }) })); //This doesn't seem to change the marker's appearance.
console.log("style", chartMarker.getGridStrokeXStyle());
});
The reason why in your code snippet the setGridStrokeXStyle is seemingly not having an effect is because the X grid stroke is not visible.
I have highlighted which element the "X grid stroke" is in the below picture with red to make sure:
As per why this is not visible in your example code, it is due to this method call which makes it so it is only displayed when the Chart Marker is being dragged.
.setGridStrokeXVisibility(UIVisibilityModes.whenDragged)
Furthermore, this other method call makes it so that the Chart Marker can not be dragged, which results in the X grid stroke never being visible.
.setDraggingMode(UIDraggingModes.notDraggable)
Here are some examples of styling different parts of the Chart Marker properly:

Highcharts: How to add another(custom) label/ legend/ something else to the top right of the graph?

In my highcharts, I already have a legend at the bottom left of the graph but want to add a custom status indicator for the entire graph. It's going to be a colored oval with a colored dot and a status on it like in the picture.
Should I be trying to use a custom legend or something else? I don't need to hide/show abiliity of the legend so maybe I'm thinking a label is more appropriate but it seems like those only go in the graph. I want something on the top right of the graph. I'm looking through the API to see what else is available but haven't found one that suites my needs.
edit-
seems like I might have to do "chart.renderer.text" but I'm not sure how to convert and make it work in typescript http://jsfiddle.net/phpdeveloperrahul/HW7Rm/
function (chart) {
var point = chart.series[0].data[8],
text = chart.renderer.text(
'Max',
point.plotX + chart.plotLeft + 10,
point.plotY + chart.plotTop - 10
).attr({
zIndex: 5
}).add(),
box = text.getBBox();
chart.renderer.rect(box.x - 5, box.y - 5, box.width + 10, box.height + 10, 5)
.attr({
fill: '#FFFFEF',
stroke: 'gray',
'stroke-width': 1,
zIndex: 4
})
.add();
});
The best way to add a custom element inside the legend is to use the legend.labelFormatter.
events: {
render() {
let chart = this,
legendAttr = chart.legend.box.getBBox(),
padding = 5;
chart.plus = chart.renderer.text('| +', chart.legend.box.parentGroup.alignAttr.translateX + legendAttr.width + padding, chart.spacingBox.height + padding / 2).attr({
cursor: 'pointer',
}).css({
fontSize: 20
}).on(
"click",
function() {
alert('plus was clicked')
}
).add()
}
}
API References:
https://api.highcharts.com/highcharts/legend.labelFormatter,
Demo: https://jsfiddle.net/BlackLabel/yu7qm9jx/1/
I decided to just get rid of the title and add custom css to the top of it.

In created:, my method doesn't work perfectly

I'm trying to make a simple vue.js & d3.js code. In the code, I want to plot some charts. For that, I'm trying to make some spaces for charts, by defining a method('add_svg') and run the method in created.
But, the method('add_svg') doesn't work what i intended. I had checked that the method was working, by inserting console.log('Running') for the first part of the method.
I could saw the message, 'Running', but the svg_space, i wanted to insert, wasn't inserted.
But when I UPDATE charts with same method in computed: part, it works.
In the computed: part, it generates some spaces several times, which means that it's working and the method has no problem.
Why is that?
Below is my code:
// GIVES CONSOLE.LOG MESSAGE('RUNNING') BUT DOES NOT APPEND SVG
created () {
console.log("")
this.add_svg() // append svg for chart
this.plot([0, 0]) // basic plot with value = [0, 0]
},
...
// SAME METHOD, BUT IN HERE, IT GIVES ME WHAT I WANTED.
// BUT TO INITIALIZE, I WANT TO RUN THE METHOD IN created: part, not in computed
computed () {
this.add_svg()
this.plot( datum )
}
...
methods: {
add_svg: function () {
console.log("Adding some space for chart")
d3.select('.chart_test2')
.append('svg')
.attr('class', 'svg_chart_test2')
.attr('width', '10px')
.attr('height', '10px')
},
other methods...
}
In Vue, you have to draw d3 charts on the mounted hook, rather than the created hook. You can't draw the chart if the page is not rendered - which is what happens when you use the created hook.
mounted () {
this.add_svg() // append svg for chart
this.plot([0, 0]) // basic plot with value = [0, 0]
}

D3 Invert Selection From Event Listener (this)

I have a visual with many rects, and I have given them the class graph_rects. My goal is when the user clicks on a rect with that class, it will keep that rect the same color but turn all the other graph_rects gray. However my approach did not work and unfortunately it's hard to trouble shoot because there were no errors in the log.
My CSS rule for the graying out:
.graph_rects unselected {
fill:gray;
}
My D3 event listener at rect creation/appending:
.on('click', function(d) {
return d3.selectAll(".graph_rects:not(this)").classed('unselected',true)
})
I'm pretty sure my CSS is ok, my hunch is my d3 logic is not right.
Update
Here are some relevant lines of code that may help piece together why the proposed solutions is not working for me (yet). I am trying to troubleshoot whether or not my matrix of graphs (such as this one) is the culprit by making the selector have redundant ids. So I created a id that has the i and j for the corresponding dimensions as well as i for the index. This way it should be impossible for a rect to have the same id with another rect from a different graph.
.attr('id', function(d,i) {
return String(p.i+p.j)+i
})
Also here is my event listener as per the suggested solution:
.on('click', function(d) {
d3.selectAll('.graph_rects:not(#'+this.id+')').classed('unselected',true)
d3.select(this).classed('unselected',false)
})
Unfortunately I get the following error:
Failed to execute 'querySelectorAll' on 'Document': '.graph_rects:not(#219)' is not a valid selector
What did I not do right I wonder?
First problem, your CSS should be:
.graph_rects.unselected {
fill: gray;
}
But that's not the big issue here. The big issue lies here:
d3.selectAll(".graph_rects:not(this)")
In that line, you're telling D3 to not select any <this></this> element. Of course, such elements don't exist!
There are several ways to achieve what you want, and different coders have their favourite approach (I have mine, which doesn't use the not). However, I'll try to provide a solution keeping that not selector: instead of using not(this), which literally selects something called this, you can select by class or by id (remember, IDs must be unique). For example:
d3.selectAll(".graph_rects:not(#" + this.id + ")")
Here is an example, click the bars:
var svg = d3.select("svg");
var rects = svg.selectAll("planck")
.data(d3.range(10))
.enter()
.append("rect")
.attr("class", "graph_rects")
.attr("id", function(d, i) {
return "rect" + i
})
.attr("x", function(d, i) {
return i * 25
})
.attr("y", 0)
.attr("width", 20)
.attr("height", 150)
.attr("fill", "firebrick")
.on('click', function() {
d3.selectAll(".graph_rects:not(#" + this.id + ")").classed('unselected', true);
d3.select(this).classed("unselected", false)
})
.graph_rects.unselected {
fill: gray;
}
.graph_rects {
cursor: pointer;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>

Change LineString style for leaflet map with R shiny

I have a leaflet map in R with a lot of LineStrings in it. The lines default to blue, with a certain thickness. I want to change the line color and have the thickness depend on a weight variable. I create the map by reading a geojson file. Adding a "style" section directly to each linestring feature in the geojson isn't working. Here is my R code for the map:
colchoice = "red"
ptl_str = paste("#! function(feature, latlng){
return L.circleMarker(latlng, {
radius: feature.properties.radius || 5,
fillColor: feature.properties.fillColor || '", colchoice, "',
color: '#000',
weight: 1,
fillOpacity: 0.8
})
} !#", sep="")
m <- Leaflet$new()
m$setView(c(38.892682, -77.031681), zoom = 12)
m$geoJson(geojson,
onEachFeature = "#! function(feature, layer){
layer.bindPopup(feature.properties.popup)
} !#",
pointToLayer = ptl_str)
m
This successfully formats points on the map into dots. But now I want to format the lines differently too. I appreciate any help. Thanks!
Upfront: i know nothing about R and not able to test what i'm saying, but i know my way around Leaflet in Javascript and can tell you how i would go about accomplishing that in JS, so you can translate it to R:
var geoJsonLayer = L.geoJson(data, {
onEachFeature: function (feature, layer) {
if (layer instanceof L.Polyline) {
layer.setStyle({
'color': feature.properties.color
});
}
}
});
Here's a working example on Plunker: http://plnkr.co/edit/g4juen?p=preview

Resources