I am attempting to add a Heatmap to a DC/Crossfilter Dashboard. What I want to see is a horizontal line of small rectangles colored either Red of Green depending on whether the transaction return was positive or negative, this is to follow the sequence of transactions.
So I'm having trouble figuring how to relate the color to the transaction value (tried to use the type text"Profit"/"Loss" field but couldn't figure this out either). The following is where I'm at the moment but just seem to be getting the first half of the returns are green and the second half red. So stuck on the mechanics of relating color to value. Also there might be the odd zero value which I would like to match with the positive values.
I'm only a hobbyist here so don't have a great depth of knowledge and have read so many queries that nearly get there but end up making me aware of just how much I don't know. So any help here would be greatly appreciated as I take another step forward.
Here's the Fiddle
Cheers
Fred
Here's my code
var transactions = [
{date: "2020-01-23T11:15:11Z", sequence: 1, rturn: -280.00, type: "Loss"},
{date: "2020-01-23T11:22:19Z", sequence: 2, rturn: -43.75, type: "Loss"},
{date: "2020-01-23T11:28:47Z", sequence: 3, rturn: -4.05, type: "Loss"},
{date: "2020-01-23T11:33:26Z", sequence: 4, rturn: 9.47, type: "Profit"},
{date: "2020-01-23T11:50:34Z", sequence: 5, rturn: 0.11, type: "Profit"},
{date: "2020-01-23T11:53:40Z", sequence: 6, rturn: 16.46, type: "Profit"},
{date: "2020-01-23T12:16:34Z", sequence: 7, rturn: 19.23, type: "Profit"},
{date: "2020-01-23T12:24:03Z", sequence: 8, rturn: 26.65, type: "Profit"},
{date: "2020-01-23T12:38:19Z", sequence: 9, rturn: 7.70, type: "Profit"},
{date: "2020-01-23T13:12:50Z", sequence: 10, rturn: 9.80, type: "Profit"},
{date: "2020-01-23T13:27:43Z", sequence: 11, rturn: -15.58, type: "Loss"},
{date: "2020-01-23T13:35:45Z", sequence: 12, rturn: 6.18, type: "Profit"}
];
var facts = crossfilter(transactions);
var dimensionByType = facts.dimension(function(d){ return d.sequence; });
var groupByType = dimensionByType.group().reduceCount(function(d){ return d.type; });
var barChart = dc.heatMap('#heatMap')
.width(1024)
.height(250)
.dimension(dimensionByType)
.group(groupByType)
.title(function(d){ return "Total Payment: $" + d.key + " => Tip: $" +d.value; })
.xBorderRadius([25])
.yBorderRadius([25])
.colors(["steelblue","red"])
.calculateColorDomain();
dc.renderAll();
<!DOCTYPE html>
<html lang="EN">
<head>
<meta charset="utf-8">
<title>Heat Map</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.1/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.9/dc.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.9/dc.min.css" />
</head>
<body>
<h1>Transactions Result Sequence</h1>
<div id="heatMap"></div>
</body>
</html>
dc.js is built around a lot of "accessors" and "scales".
"Accessors" take a group bin ({key,value}
pair) and return some useful value.
"Scales" (a D3 concept) take a value and map it to some visual encoding like color, X or Y.
The heatmap uses
keyAccessor
and a private d3.scaleBand for XvalueAccessor
and a private d3.scaleBand for YcolorAccessor
and the scale .colors()
for color.In your case, you want
to use sequence
for X
.keyAccessor(d => d.key)
to use 0 (or any constant value) for Y
.valueAccessor(d => 0)
to use rturn
or type
for the color accessor, and a color scale that maps profit to green and loss to red
You are using sequence
for the dimension; that will provide d.key
.
A crossfilter group aggregates all the rows that fall into a bin assigned by the key.
A simple way to see the profit or loss is to reduceSum
using rturn
:
var groupByType = dimensionByType.group().reduceSum(row => row.rturn);
There is only one row per sequence, so it just passes rturn
through. Now d.value
will have row.rturn
and we can tell the colorAccessor
to decide whether it's a profit or loss based on the sign:
.colorAccessor(d => d.value >= 0 ? 'Profit' : 'Loss')
and use an ordinal scale to assign profit to green and red to loss:
.colors(d3.scale.ordinal().domain(['Profit', 'Loss']).range(["green","red"]))
There are lots of other ways to do this, using type
or other color scales, but I think this is the simplest and it still works if your heatmap bins are aggregated.
var transactions = [
{date: "2020-01-23T11:15:11Z", sequence: 1, rturn: -280.00, type: "Loss"},
{date: "2020-01-23T11:22:19Z", sequence: 2, rturn: -43.75, type: "Loss"},
{date: "2020-01-23T11:28:47Z", sequence: 3, rturn: -4.05, type: "Loss"},
{date: "2020-01-23T11:33:26Z", sequence: 4, rturn: 9.47, type: "Profit"},
{date: "2020-01-23T11:50:34Z", sequence: 5, rturn: 0.11, type: "Profit"},
{date: "2020-01-23T11:53:40Z", sequence: 6, rturn: 16.46, type: "Profit"},
{date: "2020-01-23T12:16:34Z", sequence: 7, rturn: 19.23, type: "Profit"},
{date: "2020-01-23T12:24:03Z", sequence: 8, rturn: 26.65, type: "Profit"},
{date: "2020-01-23T12:38:19Z", sequence: 9, rturn: 7.70, type: "Profit"},
{date: "2020-01-23T13:12:50Z", sequence: 10, rturn: 9.80, type: "Profit"},
{date: "2020-01-23T13:27:43Z", sequence: 11, rturn: -15.58, type: "Loss"},
{date: "2020-01-23T13:35:45Z", sequence: 12, rturn: 6.18, type: "Profit"}
];
var facts = crossfilter(transactions);
var dimensionByType = facts.dimension(function(d){ return d.sequence; });
var groupByType = dimensionByType.group().reduceSum(row => row.rturn);
var heatMap = dc.heatMap('#heatMap')
.width(1024)
.height(250)
.dimension(dimensionByType)
.group(groupByType)
.keyAccessor(d => d.key)
.valueAccessor(d => 0)
.colorAccessor(d => d.value >= 0 ? 'Profit' : 'Loss')
.title(function(d){ return "Total Payment: $" + d.key + " => Tip: $" +d.value; })
.xBorderRadius([25])
.yBorderRadius([25])
.colors(d3.scale.ordinal().domain(['Profit', 'Loss']).range(["green","red"]))
//.calculateColorDomain();
dc.renderAll();
<!DOCTYPE html>
<html lang="EN">
<head>
<meta charset="utf-8">
<title>Heat Map</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.1/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.9/dc.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.9/dc.min.css" />
</head>
<body>
<h1>Transactions Result Sequence</h1>
<div id="heatMap"></div>
</body>
</html>