Closed jfivekillerln closed 3 months ago
@jfivekillerln I have added that new "published property" and added an example of how you might use "Standard Deviations" rather than raw values. This should provide pleasing results for any data (the example below is using 2 standard deviations as being "exceptional"):
import { Heat } from "@hpcc-js/chart";
import * as df from "@hpcc-js/dataflow";
const data = [["Sat", 0, 0.18998719125426], ["Sat", 1, 0.21651075448881343], ["Sat", 2, 0.29360917730014136], ["Sat", 3, 0.1651116355055364], ["Sat", 4, 0.11885237728786953], ["Sat", 5, 0.09315256213264339], ["Sat", 6, 0.19081093933358728], ["Sat", 7, 0.26505462252549605], ["Sat", 8, 0.31304625721290896], ["Sat", 9, 0.18326323889972618], ["Sat", 10, 0.5427922043058862], ["Sat", 11, 0.33890867440986455], ["Sat", 12, 0.22287115322174472], ["Sat", 13, 0.2587162108611005], ["Sat", 14, 0.28441551468915144], ["Sat", 15, 0.19982154681583783], ["Sat", 16, 0.2930569439508717], ["Sat", 17, 0.47723750380299584], ["Sat", 18, 0.22207706211858186], ["Sat", 19, 0.33558402511639085], ["Sat", 20, 0.3690611266071652], ["Sat", 21, 0.5573599155287506], ["Sat", 22, 0.6831520252391093], ["Sat", 23, 0.5778902129422021], ["Fri", 0, 0.22511997013849297], ["Fri", 1, 0.12872968433216836], ["Fri", 2, 0.14983215685472429], ["Fri", 3, 0.2927501476457219], ["Fri", 4, 0.10428671137370601], ["Fri", 5, 0.15568583035698308], ["Fri", 6, 0.24599132277783603], ["Fri", 7, 0.2731898378837191], ["Fri", 8, 0.44567225462048016], ["Fri", 9, 0.20923559143936044], ["Fri", 10, 0.37126545805966676], ["Fri", 11, 0.25775900638903304], ["Fri", 12, 0.3814741051135274], ["Fri", 13, 0.34006938709768136], ["Fri", 14, 0.27593924410503684], ["Fri", 15, 0.4419477474759613], ["Fri", 16, 0.38965124930012096], ["Fri", 17, 0.3172503892478122], ["Fri", 18, 0.46400589048905894], ["Fri", 19, 0.2558543126612278], ["Fri", 20, 0.3590657029853837], ["Fri", 21, 0.15632447799886998], ["Fri", 22, 0.26420326277870526], ["Fri", 23, 0.297398622995917], ["Thur", 0, 0.18232751016901919], ["Thur", 1, 0.10522908735769126], ["Thur", 2, 0.2871142995201194], ["Thur", 3, 0.13264031456847822], ["Thur", 4, 0.18082727623683653], ["Thur", 5, 0.09944495435126643], ["Thur", 6, 0.11995531000488316], ["Thur", 7, 0.32298185555518627], ["Thur", 8, 0.2721533776994879], ["Thur", 9, 0.27001193948954205], ["Thur", 10, 0.4749544279655059], ["Thur", 11, 0.2692331882016368], ["Thur", 12, 0.4508438176709558], ["Thur", 13, 0.4492663733353105], ["Thur", 14, 0.3835056079807946], ["Thur", 15, 0.3464395010469424], ["Thur", 16, 0.34637609647721146], ["Thur", 17, 0.28212732557990894], ["Thur", 18, 0.19046579349029374], ["Thur", 19, 0.3223897386862471], ["Thur", 20, 0.2504833320124048], ["Thur", 21, 0.28639128289431637], ["Thur", 22, 0.4919882701545998], ["Thur", 23, 0.11120547938200998], ["Wed", 0, 0.26539414376986187], ["Wed", 1, 0.1915083896006279], ["Wed", 2, 0.1144094554621247], ["Wed", 3, 0.1915083896006279], ["Wed", 4, 0.1915083896006279], ["Wed", 5, 0.06301033647884767], ["Wed", 6, 0.18917162440973667], ["Wed", 7, 0.2034494131242346], ["Wed", 8, 0.355351933711545], ["Wed", 9, 0.3451218109163239], ["Wed", 10, 0.3226346644031917], ["Wed", 11, 0.20270338677554525], ["Wed", 12, 0.12560445263704206], ["Wed", 13, 0.2519610675488765], ["Wed", 14, 0.30086133062670817], ["Wed", 15, 0.24946221164343108], ["Wed", 16, 0.31534569552000696], ["Wed", 17, 0.4939159736052912], ["Wed", 18, 0.31544642697353115], ["Wed", 19, 0.24156016147712192], ["Wed", 20, 0.27236557847721654], ["Wed", 21, 0.3740930973387977], ["Wed", 22, 0.1085307269282787], ["Wed", 23, 0.13101787344141086], ["Tue", 0, 0.38368457249213195], ["Tue", 1, 0.11328658098527632], ["Tue", 2, 0.14541071077033996], ["Tue", 3, 0.06827291576651778], ["Tue", 4, 0.17107115373307188], ["Tue", 5, 0.11538864700272793], ["Tue", 6, 0.11217648968780919], ["Tue", 7, 0.13902321169712045], ["Tue", 8, 0.3094408893002232], ["Tue", 9, 0.40688502041473745], ["Tue", 10, 0.36313484464602097], ["Tue", 11, 0.4601351437724185], ["Tue", 12, 0.4095924978076847], ["Tue", 13, 0.22557709663316622], ["Tue", 14, 0.3283753345997203], ["Tue", 15, 0.2706332020074705], ["Tue", 16, 0.24600410595721722], ["Tue", 17, 0.5543993311840548], ["Tue", 18, 0.2738964920399142], ["Tue", 19, 0.2781803911141563], ["Tue", 20, 0.17569661936038083], ["Tue", 21, 0.48912841726342815], ["Tue", 22, 0.38632966796969875], ["Tue", 23, 0.18644420525695468], ["Mon", 0, 0.6359227793699938], ["Mon", 1, 0.5221023728137567], ["Mon", 2, 0.37279943958541595], ["Mon", 3, 0.34710013575736504], ["Mon", 4, 0.28285085353288725], ["Mon", 5, 0.25751868261666566], ["Mon", 6, 0.28428921687686476], ["Mon", 7, 0.17089474585761072], ["Mon", 8, 0.36545166807707746], ["Mon", 9, 0.21043056304791902], ["Mon", 10, 0.3696194958825379], ["Mon", 11, 0.3105586505053191], ["Mon", 12, 0.4402250862225449], ["Mon", 13, 0.2582289160630875], ["Mon", 14, 0.2996172716093256], ["Mon", 15, 0.32338989464103557], ["Mon", 16, 0.3249376820005164], ["Mon", 17, 0.22535620329345835], ["Mon", 18, 0.2782090254359703], ["Mon", 19, 0.14818108140584293], ["Mon", 20, 0.32807825351090025], ["Mon", 21, 0.5908569587793598], ["Mon", 22, 0.5080470114204925], ["Mon", 23, 0.2530276960364474], ["Sun", 0, 0.6469792068804184], ["Sun", 1, 0.4528553787783882], ["Sun", 2, 0.540662526620971], ["Sun", 3, 0.48754995027343223], ["Sun", 4, 0.2125643313502361], ["Sun", 5, 0.1999907961108455], ["Sun", 6, 0.19768419922329403], ["Sun", 7, 0.1666001089126883], ["Sun", 8, 0.39061458969829144], ["Sun", 9, 0.3091105719450119], ["Sun", 10, 0.3605096909282889], ["Sun", 11, 0.23504738724596624], ["Sun", 12, 0.2493108587995572], ["Sun", 13, 0.27608139305975626], ["Sun", 14, 0.301086314583818], ["Sun", 15, 0.4038845525503721], ["Sun", 16, 0.46976394580954595], ["Sun", 17, 0.5597125318620746], ["Sun", 18, 0.3975047233847814], ["Sun", 19, 0.19067492630497088], ["Sun", 20, 0.348541567064394], ["Sun", 21, 0.7699334507681412], ["Sun", 22, 0.606812411955852], ["Sun", 23, 1]];
// Calculate the standard deviation of the weight column ---
const extractWeight = df.map(row => row[2]);
const distribution = df.scalar(df.distribution())(extractWeight(data));
// Convert the weight column to standard deviations ---
const toStdDevs = df.map(r => {
return [r[0], r[1], distribution.mean + (r[2] - distribution.mean) / (distribution.deviation || 1)];
});
const stdDevData = toStdDevs(data);
// Normalize the standard deviations and calculate the new mean and deviation ---
const range = df.scalar(df.extent())(extractWeight(stdDevData));
const denominator = (range[1] - range[0]) || 1;
const normalize = df.map(r => {
return [r[0], r[1], (r[2] - range[0]) / denominator];
});
const normalizedStdDevData = normalize(toStdDevs(data));
const normalizedMean = (distribution.mean - range[0]) / denominator;
const normalizedDeviation = (distribution.deviation - range[0]) / denominator;
new Heat()
.target("target")
.columns(["Day", "Hour", "weight"])
.paletteID("RdYlGn")
.reversePalette(true)
.xAxisType("ordinal")
.yAxisType("time")
.yAxisTypeTimePattern("%H")
.yAxisTickFormat("%H:00")
.yAxisTitle("Hour")
.orientation("vertical")
.maxWeight(normalizedMean + 2 * normalizedDeviation)
.data([...normalizedStdDevData])
.render()
;
@GordonSmith - I do like this approach. Originally, I was thinking we would set the max value "somewhere" under 100% so you could get hotspots, but this is better thanks!
Also I found the following palette more representative of the data in question (Green/Yellow/Red):
.paletteID("RdYlGn")
.reversePalette(true)
The Heat chart currently finds the max values in the data and uses it to color the chart.
Given this set of data, the max value is 0.005 therefor 0.005 is RED
which produces
The visualization is correct in the sense that everything is the same but is misleading because to our users it says all things occur at all times.
We use this chart to show what day and what times crimes occur we would like to be able to set the maximin value to 1.0 so we can calculate the percentage of crimes occurring per hour per day.
If I had one crime occur every day and every hour, I would have a count table:
If I, then turned the count table into a percentage table I would have (1/168=0.005)
If the max value in the visualization was then set to 1, it would produce a chart like:
The chart shows all things are equal, but instead of coloring all times as red, it colors them with the same cool color.