Plotting Away and Home Goal Difference for Crystal Palace 2020/21 - javascript

Using javascript, vega editor and this data: https://github.com/StanWaldron/StanWaldron.github.io/blob/main/final.csv, which I got from SportDataAPI and organised using pandas, I have attempted to make a graph that plots Crystal Palace's home and away goal differences over the course of the 2020/21 season. As you can see though, I can't get a smooth plot.
The issue seems to be that the NaNs in the data are being put in as 0s for home games when they have played away and vice versa. When trying to resolve this by looping through like so:
for c in final['Home_GD']:
if math.isnan(c) == True:
c = 0.0
It doesn't seem to change the data at all. If this worked, I would just be able to simply add it to the previous value and plot it that way, for every game.
In the javascript side I have also used layers, but have struggled to find any way of separating the data completely and then using two different data sources which I can layer on the same graph.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"title": {
"text": "Home and Away Goal Difference For Crystal Palace 2020/21",
"subtitle":["Exclusively in the Premier League"],
"subtitleFontStyle":"italic",
"subtitleFontSize":10,
"anchor": "start",
"color": "black"},
"data": {
"url":
"https://raw.githubusercontent.com/StanWaldron/StanWaldron.github.io/main/final.csv"
},
"repeat": {"layer": ["Away_GD", "Home_GD"]},
"height": 300,
"width": 300,
"spec": {
"mark": {
"type":"line",
"strokeWidth":2},
"encoding": {
"x": {
"field": "Date",
"type": "temporal",
"title":null,
"axis":{
"grid": false
}},
"y": {
"field": {"repeat": "layer"},
"type": "quantitative",
"title": null},
"color": {
"datum": {"repeat": "layer"},
"scale": {"range": ["blue", "red"]},
"legend": {
"orient":"top-left",
"fillColor":"white"}}
}
}
}

I would probably do this by filtering each value individually within a layer; here's an example (open in editor):
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"url": "https://raw.githubusercontent.com/StanWaldron/StanWaldron.github.io/main/final.csv"
},
"transform": [{"fold": ["Home_GD", "Away_GD"], "as": ["key", "Goals"]}],
"encoding": {
"x": {"field": "Date", "type": "temporal"},
"y": {"field": "Goals", "type": "quantitative"},
"color": {"field": "key", "type": "nominal"}
},
"layer": [
{
"transform": [{"filter": "datum.key == 'Home_GD' && datum.Home_GD != ''"}],
"mark": "line"
},
{
"transform": [{"filter": "datum.key == 'Away_GD' && datum.Away_GD != ''"}],
"mark": "line"
}
]
}

Related

Setting a field to an object in an array, Vega-lite

I have an array of objects that I am using as a dataset in an interactive data dashboard. I want to add a new feature that displays data from only one object at a time, rather than pulling data from all objects (which I am already doing and it works well). As a test case, I have created a simple array:
var test1 = [
[{
"name": "Piece One",
"amount": 5
}, {
"name": "Piece Two",
"amount": 5
}, {
"name": "Piece Three",
"amount": 5
}],
[{
"name": "Piece One",
"amount": 1
}, {
"name": "Piece Two",
"amount": 1
}, {
"name": "Piece Three",
"amount": 5
}]
];
and the Vega-lite javascript:
var pieCreate = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"title": "A pie chart",
"description": "A simple pie chart with embedded data.",
"width": "container",
"height": "container",
"data": {
"values": test1[0]
},
"mark": "arc",
"encoding": {
"theta": {
"field": "amount",
"type": "quantitative"
},
"color": {
"field": "name",
"type": "nominal",
"legend": null
}
}
};
This works, but I want the user to be able to choose which object to display (in the dashboard, each object contains data on different schools, and I want the user to be able to choose which school's data to display using a dropdown menu). My first thought was to set up a signal in the "data": {"values": field that would change the number in brackets to the array I want, but after a lot of trial and error, I think that may be a dead end. Signals should work to modify "field": "amount" and "field": "name" but I've tried every iteration of [0].amount that I can think of, while dropping the brackets from test1[0] and nothing has worked. If I can manage to access the object by directly referencing it in "field": I believe I can figure out the process using a signal and html form, but I'm starting to doubt if I'm even on the right track here.
I also tried the process outlined here in the vega-lite documentation: https://vega.github.io/vega-lite/tutorials/streaming.html, but it's doing something much more complicated than what I'm trying to do, and my javascript knowledge isn't sufficient to break it down to something usable. Does anyone have any ideas on how to make this work, using any of the above approaches (or a new, better one)?
You can use the vega Api's to change the data. On your selection, add a change event and on some conditions you can toggle between your data using those API's.
Refer the below snippet or fiddle:
var test1 = [
[{
"name": "Piece One",
"amount": 5
}, {
"name": "Piece Two",
"amount": 5
}, {
"name": "Piece Three",
"amount": 5
}],
[{
"name": "Piece One",
"amount": 1
}, {
"name": "Piece Two",
"amount": 1
}, {
"name": "Piece Three",
"amount": 5
}]
];
var yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"title": "A pie chart",
"description": "A simple pie chart with embedded data.",
"width": "350",
"height": "400",
"data": {
"values": test1[0]
},
"mark": "arc",
"encoding": {
"theta": {
"field": "amount",
"type": "quantitative"
},
"color": {
"field": "name",
"type": "nominal",
"legend": null
}
}
}
var view;
vegaEmbed("#vis", yourVlSpec).then(res => {
view = res.view;
});
function handleChange(a, b) {
var selectValue = document.getElementById("myselect").value;
if (selectValue == 'A') {
view.data('source_0', test1[0]);
} else {
view.data('source_0', test1[1]);
}
view.runAsync();
}
<script src="https://cdn.jsdelivr.net/npm/vega#5.20.2/build/vega.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite#5.0.0/build/vega-lite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed#6.17.0/build/vega-embed.min.js"></script>
<select id="myselect" style="width:100px;" onchange="handleChange()">
<option>A</option>
<option>B</option>
</select>
<br>
<div id="vis"></div>

How do you create a partial pie chart on vega-lite for single data point?

I am using vega-lite to create a pie chart (on Airtable). I have a single data point, which is a target set by me, and the percentage complete for that target. For example, as below:
{
"Target": "Get 10 customers",
"Percentage complete": "60"
}
I would like to make a pie chart that is 60% complete, and the rest empty. Similar to the interactive single arc pie chart displayed https://vega.github.io/vega-lite/docs/arc.html.
My code currently looks like this
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"title": "Customer Acquired",
"width": "container",
"height": "container",
"data": {
"values": [
{
"Target": "Get 10 customers",
"Percentage complete": "60"
}
]},
"mark": {
"type": "arc",
"tooltip": true
},
"encoding": {
"theta": {
"field": "Percentage complete",
"type": "quantitative"
}
}
}
And my pie chart currently just looks like this:
I realise I could force the pie chart to appear the way I want it by manually setting the theta2 property like so
"mark": {
"type": "arc",
"tooltip": true,
"theta2": 3.5
}
However I don't know what the "Percentage complete" field will be and this value may change often, so I would rather not have to do it manually. Is this at all possible with vega-lite?
The domain for the theta encoding will automatically be set to the minimum and maximum of your input data. To show the correct portion of the chart, you need to set the domain to [0, 100]:
"encoding": {
"theta": {
"field": "Percentage complete",
"type": "quantitative",
"scale": {"domain": [0, 100]}
}
}
You can view the resulting chart in the vega editor:

How to access json variable which is inside script instead of using $.getJSON to get external json?

Currently working with this highcharts code:
https://jsfiddle.net/umL154cd/
It works with external data source like shown below:
$.getJSON('https://www.highcharts.com/samples/data/jsonp.php?filename=activity.json&callback=?', function (activity) {
$.each(activity.datasets, function (i, dataset) { ///
But I'd like to truncate the data and put it inside the code itself by simply anouncing json.variable:
var datafromjson =
{
"xData": [0.001567,0.011765,0.022194],
"datasets": [{
"name": "Speed",
"data": [13.833,12.524,11.441,10.651],
"unit": "km/h",
"type": "line",
"valueDecimals": 1
}, {
"name": "Elevation",
"data": [26.857,27,27.111,27.2,27.272],
"unit": "m",
"type": "area",
"valueDecimals": 0
}, {
"name": "Heart rate",
"data": [101,98,103],
"unit": "bpm",
"type": "area",
"valueDecimals": 0
}]
};
How do I replace $.getJSON request so the script will take the turncated in-script data insted of accessing external data?
Thank you!

How to Aggregate Color Encoding in Vega-Lite Choropleth Map (geoshape) with a transform.lookup, using JavaScript

I'm trying to aggregate the color encoding in a vega-lite Choropleth map, using JavaScript.
I have made a working example to make it easier to help and understand
The minimal code is:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": 600,
"height": 500,
"mark": "geoshape",
"data": {
"url": "https://gist.githubusercontent.com/benooghe/95c8b4d63f67f1856fdd81e6303c654e/raw/8ec1fdd91bfbf4973f97ffb8a5daacb8f431908e/geo_belgium.json",
"format": {"type": "topojson", "feature": "data"}
},
"encoding": {
"color": {
"field": "properties.nis_code",
"type": "quantitative",
// "aggregate":"sum" // <-- THIS BREAKS THE MAP
}
}
}
This is a simplified version, it works, but if you add , "aggregate: "sum" in the encoding.color then it doesn't work anymore.
In case I oversimplified, my complete map is just a little bit more complicated: it has a transform.lookup.
let yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": 600,
"height": 500,
"mark": "geoshape",
"data": { // TOPOJSON DATA -----
"url": 'geo_belgium.json',
"format": {
"type": "topojson",
"feature": "data"
}
},
"transform": [{ // LEFT JOIN DATA -----
"lookup": "properties.nis_code",
"from": {
"data": {
"url": "data.csv"
},
"key": "NIS5",
"fields": ["Cases", "Deaths"]
}
}],
"encoding": {
"color": {
"field": "Cases",
"type": "quantitative",
// "aggregate": "sum" // <-- THIS BREAKS THE MAP
}
}
};
vegaEmbed('#vis', yourVlSpec);
EDIT: I also tried this : in the transform
"aggregate": [{"op": "sum", "field": 'Cases', "as": 'sum_cases'}],
EDIT2: The data in data.csv looks like this:
Date,PostCode,Cases,Deaths,NIS5
2020-04-08,2470,1,,13036
2020-04-08,2430,1,,13053
2020-04-08,1457,1,,25124
2020-04-08,3212,1,,24066
2020-04-08,2400,1,,13025
2020-04-08,1651,1,,23003
2020-04-08,2360,1,,13031
2020-04-07,1070,22,12,21004
2020-04-07,1070,22,12,21001
2020-04-07,4000,24,11,62093
2020-04-07,4000,24,11,62063
2020-04-01,9320,1,,41002
2020-04-01,7380,1,,53068
2020-04-01,9308,1,,41002
2020-03-31,1070,34,7,21004
2020-03-31,1070,34,7,21001
2020-03-31,3500,7,7,71022
2020-03-31,3800,10,5,71053
2020-03-31,4000,28,4,62063
2020-03-31,4000,28,4,62093
I'm looking to plot the sum of "Cases" per "NIS5", summing up all Dates
When you express a sum as part of an encoding, it groups by all other encoding fields. In your case there are no other encoding fields, so it aggregates everything and throws-out the geometry.
One way to work around this is to add relevant fields to a detail encoding, so that the aggregation does not drop them from the data (vega editor):
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": 600,
"height": 500,
"mark": "geoshape",
"data": {
"url": "https://gist.githubusercontent.com/benooghe/95c8b4d63f67f1856fdd81e6303c654e/raw/8ec1fdd91bfbf4973f97ffb8a5daacb8f431908e/geo_belgium.json",
"format": {"type": "topojson", "feature": "data"}
},
"encoding": {
"color": {"field": "properties.nis_code", "type": "quantitative", "aggregate": "sum"},
"detail": [{"field": "geometry", "type": "nominal"}, {"field": "properties.id", "type": "nominal"}, {"field": "type", "type": "nominal"}]
}
}
In your case, however, this is the same as the original unaggregated chart because there is a single nis_code per geographic area.
If you want to group by something other than geometric region, the best way to do that is to use a joinaggregate transform (which does not drop unnamed fields) and group by the particular data you're interested in grouping by. For example, here's the sum of nis_code grouped by the level_2 feature (vega editor):
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": 600,
"height": 500,
"mark": "geoshape",
"data": {
"url": "https://gist.githubusercontent.com/benooghe/95c8b4d63f67f1856fdd81e6303c654e/raw/8ec1fdd91bfbf4973f97ffb8a5daacb8f431908e/geo_belgium.json",
"format": {"type": "topojson", "feature": "data"}
},
"transform" : [{
"joinaggregate": [{"field": "properties.nis_code", "op": "sum", "as": "sum_nis_code"}],
"groupby": ["properties.level_2"]
}],
"encoding": {
"color": {"field": "sum_nis_code", "type": "quantitative"}
}
}

Highcharts separate categories

Check this fiddle to get an idea of what I am talking about
This is my xAxis configuration:
"xAxis": {
"categories": ["Category ONE", "Category TWO"],
"allowDecimals": false,
"title": {
"text": " ",
"align": "middle",
"style": {
"color": "steelblue"
}
},
"labels": {
"y": 12,
"style": {
"color": "steelblue"
}
}
},
I need category one and category two to be separated by a few pixels (50,100, whatever amount I want) but cannot find the solution for this issue. I know there is a way to do it for series but there is no equivalent for when you only want to separate the categories.
Thanks in advance
plotOptions have some options which can control series. Padding is possible to get by changing value of groupPadding:
"plotOptions": {
"column": {
"groupPadding": 0.1
}
}
And see it live: http://jsfiddle.net/JEGGf/42/

Categories