Visualizing pie charts on a map - python

I have an excel where each row has latitude and longitude data associated with some integer values. I would like to visualize this as pie charts on a map.
What I tried so far:
Google Data Studio: The only drawback here is that we cannot zoom on the map and the map is zoomed at country level but all my data is about a district in a city.
Python with Folium: Folium is a wrapper for Leaflet.js which is excellent for geographical visualization. However it lacks pie chart feature. I looked at integrating with Vega but that is only good for popups on Markers. This is not good, I want the pie charts on the map directly.
Can you recommend any free tool, or Python solution for this?
I come from a Python background mainly, but I welcome JS based solutions as well.

I think Highcharts can help you with what you are looking for. They are based on javascript.
An example for precisely what you are looking for -
https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/maps/demo/map-pies/
{
title: {
text: 'USA 2016 Presidential Election Results'
},
chart: {
animation: false // Disable animation, especially for zooming
},
colorAxis: {
dataClasses: [{
from: -1,
to: 0,
color: 'rgba(244,91,91,0.5)',
name: 'Republican'
}, {
from: 0,
to: 1,
color: 'rgba(124,181,236,0.5)',
name: 'Democrat'
}, {
from: 2,
to: 3,
name: 'Libertarian',
color: libColor
}, {
from: 3,
to: 4,
name: 'Green',
color: grnColor
}]
},
mapNavigation: {
enabled: true
},
// Limit zoom range
yAxis: {
minRange: 2300
},
tooltip: {
useHTML: true
},
// Default options for the pies
plotOptions: {
mappie: {
borderColor: 'rgba(255,255,255,0.4)',
borderWidth: 1,
tooltip: {
headerFormat: ''
}
}
},
series: [{
mapData: Highcharts.maps['countries/us/us-all'],
data: data,
name: 'States',
borderColor: '#FFF',
showInLegend: false,
joinBy: ['name', 'id'],
keys: ['id', 'demVotes', 'repVotes', 'libVotes', 'grnVotes',
'sumVotes', 'value', 'pieOffset'],
tooltip: {
headerFormat: '',
pointFormatter: function () {
var hoverVotes = this.hoverVotes; // Used by pie only
return '<b>' + this.id + ' votes</b><br/>' +
Highcharts.map([
['Democrats', this.demVotes, demColor],
['Republicans', this.repVotes, repColor],
['Libertarians', this.libVotes, libColor],
['Green', this.grnVotes, grnColor]
].sort(function (a, b) {
return b[1] - a[1]; // Sort tooltip by most votes
}), function (line) {
return '<span style="color:' + line[2] +
// Colorized bullet
'">\u25CF</span> ' +
// Party and votes
(line[0] === hoverVotes ? '<b>' : '') +
line[0] + ': ' +
Highcharts.numberFormat(line[1], 0) +
(line[0] === hoverVotes ? '</b>' : '') +
'<br/>';
}).join('') +
'<hr/>Total: ' + Highcharts.numberFormat(this.sumVotes, 0);
}
}
}, {
name: 'Separators',
type: 'mapline',
data: Highcharts.geojson(Highcharts.maps['countries/us/us-all'], 'mapline'),
color: '#707070',
showInLegend: false,
enableMouseTracking: false
}, {
name: 'Connectors',
type: 'mapline',
color: 'rgba(130, 130, 130, 0.5)',
zIndex: 5,
showInLegend: false,
enableMouseTracking: false
}]
}

Related

Chart js space above bar graph

I generate graph depending on the value from my calculations. Problem is, I don't know how to set up 'margins', how to define where to put data labels.
Datalables definition:
datalabels: {
anchor: 'end',
align: 'start',
offset: 5,
Problem is, when a certain month value is 0, it's written over the labels on the bottom. Easy way to fix this would be to define a space about each column, so that it can that text can never go 'off screen' and define align: 'end'.
Case 2:
You could define some extra padding at the top of your chart using the option layout.padding.top.
Please take a look at below runnable code and see how it works:
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
plugins: [ChartDataLabels],
data: {
labels: ['A', 'B', 'C'],
datasets: [{
label: 'My Dataset',
data: [0, 0, 3],
backgroundColor: 'orange'
}
]
},
options: {
layout: {
padding: {
top: 30
}
},
plugins: {
legend: {
display: false
},
datalabels: {
align: 'end',
anchor: 'end'
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.0/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.0.0-rc.1/chartjs-plugin-datalabels.min.js"></script>
<canvas id="myChart" height="120"></canvas>

Chart JS only showing first 2 values

I'm using this for my chart application right now
<script>
var canvas = document.getElementById('myChart');
new Chart(document.getElementById("myCanvas"), {
type: 'line',
data: {
labels: mon_unique,
datasets: [{
data: values,
borderColor: "#3e95cd",
fill: false
},
]
},
options: {
title: {
display: true,
},
hover: {
mode: 'index',
intersect: true
},
}
});
</script>
values, what I called my data in my flask app, is a list of numbers. When I change data: [0,1,2,3,4] it graphs it, but it doesn't pass in my values at all.
data = remove_err_str
return render_template('graphing.html', values=data)
This displays only the first two points in values. Values is a list of about 50,000 items. It looks like ['1243.42','2`,...]
<body>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var canvas = document.getElementById('myChart');
new Chart(document.getElementById("myChart"), {
type: 'line',
data: {
datasets: [{
data: {{values | safe}},
borderColor: "#3e95cd",
fill: false
},
]
},
options: {
title: {
display: true,
test: "Chart for the sweep data"
},
hover: {
mode: 'index',
intersect: true
},
}
});
</script>
</body>
This is the solution I found
Graphing HTML page
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0/Chart.bundle.js"></script>
</head>
<body>
<canvas id="myChart" width="1600" height="800"></canvas>
<script>
var canvas = document.getElementById('myChart');
var chart = new Chart(canvas, {
type: 'line',
data: {
labels: {{ labels | safe }},
datasets: [{
label: "Line chart for sweep data",
data: {{ values | safe }},
fill: false
}]
},
options: {
responsive: false,
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}}}
);
</script>
Every item needs a label. If it gets passed in without a label it won't graph. Also I changed the 'beginAtZero' to be set to true, otherwise it starts the lowest y value in the list. To create labels for every value I did this in my flask app.py
for i in data: #turns it from a list of string values to float values
float_data.append(float(i))
count = count + 1
label_arr.append(count)
return render_template('graphing.html', values=float_data, labels=label_arr)
:*]
You are passing data from python-end to front-end so you have to use jinja template inside your code and for that double-brackets can be used
<script>
var canvas = document.getElementById('myChart');
new Chart(document.getElementById("myCanvas"), {
type: 'line',
data: {
labels: mon_unique,
datasets: [{
data: {{values | tojson}},
borderColor: "#3e95cd",
fill: false
},
]
},
options: {
title: {
display: true,
},
hover: {
mode: 'index',
intersect: true
},
}
});
</script>

Adding multiple list of values to a dictionary doesn't create the same order of values

I am working with Clarifai's API to write the returned responses to a CSV. For each image I submit, I want to take the top 5 images and append their metadata to a row in a data frame with the file_id as the first column. So each image would have 5 rows with the metadata attached to it in each successive columns. I seem to be generating some dictionary entries where the order of the values in the list being appended isn't preserved and I am trying to figure out why. This is a sample response I am iterating over.
[score: 0.8935652375221252
input {
id: "1002648140"
data {
image {
url: "https://images.lowes.com/product/converted/192665/192665007798.jpg"
hosted {
prefix: "https://s3.amazonaws.com/clarifai-api/img3/prod"
suffix: "3617f25f9ee5463497e7abf350091b93/c1ee1a24260772d532f0d371ac08fb06"
sizes: "orig"
sizes: "tiny"
sizes: "small"
sizes: "large"
}
}
metadata {
fields {
key: "cat_entry_id"
value {
string_value: "1002648140"
}
}
fields {
key: "catalog_name"
value {
string_value: "Roses"
}
}
fields {
key: "catalog_path"
value {
string_value: "Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Roses"
}
}
fields {
key: "link_url"
value {
string_value: "https://www.lowes.com/pd/Spring-Hill-Nurseries-in-Bare-Root-Red-Flowering-Blaze-Climbing-Rose/1002648140"
}
}
fields {
key: "product_name"
value {
string_value: "Spring Hill Nurseries in Bare Root Red Flowering Blaze Climbing Rose"
}
}
}
clusters {
id: "985_203"
}
}
created_at {
seconds: 1599796898
nanos: 391916000
}
modified_at {
seconds: 1599798078
nanos: 291417000
}
status {
code: INPUT_DOWNLOAD_SUCCESS
description: "Download complete"
}
}
annotation {
id: "c588c2f17f01465f94bde2d2c2e5687f"
input_id: "1002648140"
data {
}
status {
code: ANNOTATION_SUCCESS
description: "Annotation success"
}
created_at {
seconds: 1599798076
nanos: 759651000
}
modified_at {
seconds: 1599798076
nanos: 759651000
}
model_version_id: "bb186755eda04f9cbb6fe32e816be104"
}
, score: 0.8807249665260315
input {
id: "1000383061"
data {
image {
url: "https://images.lowes.com/product/converted/743425/743425079649.jpg"
hosted {
prefix: "https://s3.amazonaws.com/clarifai-api/img3/prod"
suffix: "3617f25f9ee5463497e7abf350091b93/756b268566daea823f07248208c05e21"
sizes: "orig"
sizes: "tiny"
sizes: "small"
sizes: "large"
}
}
metadata {
fields {
key: "cat_entry_id"
value {
string_value: "1000383061"
}
}
fields {
key: "catalog_name"
value {
string_value: "Annuals"
}
}
fields {
key: "catalog_path"
value {
string_value: "Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Annuals"
}
}
fields {
key: "link_url"
value {
string_value: "https://www.lowes.com/pd/3-Quart-Purple-Purple-Garden-Mum-in-Pot/1000383061"
}
}
fields {
key: "product_name"
value {
string_value: " 3-Quart Purple Purple Garden Mum in Pot"
}
}
}
clusters {
id: "54_203"
}
}
created_at {
seconds: 1599795418
nanos: 883441000
}
modified_at {
seconds: 1599795429
nanos: 301624000
}
status {
code: INPUT_DOWNLOAD_SUCCESS
description: "Download complete"
}
}
annotation {
id: "91434754e4bd425ba3c528be9cd901e0"
input_id: "1000383061"
data {
}
status {
code: ANNOTATION_SUCCESS
description: "Annotation success"
}
created_at {
seconds: 1599795427
nanos: 683212000
}
modified_at {
seconds: 1599795427
nanos: 683212000
}
model_version_id: "bb186755eda04f9cbb6fe32e816be104"
}
I am using a dictionary and adding the value of metadata as a list and not getting the same order of the lists being appended. Please see the following sample dictionary for reference.
{'test_output/fe_IMG_0574.jpg_0': ['Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Roses',
'https://www.lowes.com/pd/Spring-Hill-Nurseries-in-Bare-Root-Red-Flowering-Blaze-Climbing-Rose/1002648140',
'Roses',
'Spring Hill Nurseries in Bare Root Red Flowering Blaze Climbing Rose',
'1002648140'],
'test_output/fe_IMG_0574.jpg_1': ['https://www.lowes.com/pd/3-Quart-Purple-Purple-Garden-Mum-in-Pot/1000383061',
'1000383061',
' 3-Quart Purple Purple Garden Mum in Pot',
'Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Annuals',
'Annuals'],
'test_output/fe_IMG_0574.jpg_2': ['https://www.lowes.com/pd/4-Count-Dahlia-Bulbs-L3480/1000151071',
'1000151071',
' 4 Count Dahlia Bulbs (L3480)',
'Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Plant Bulbs',
'Plant Bulbs']}
which then creates a data frame that has different values for columns in what seems like sporadic rows. This is the code I am using to generate this.
final_dict = {}
for i in range(5):
response_object = response.hits[i].input.data.metadata.items()
final_list = []
url = response_object[0][1]
product_name = response_object[1][1]
catalog_entry_id = response_object[2][1]
catalog_path = response_object[3][1]
catalog_name = response_object[4][1]
final_list.append(url)
final_list.append(catalog_entry_id)
final_list.append(product_name)
final_list.append(catalog_path)
final_list.append(catalog_name)
final_dict[file_id+'_'+str(i)] = final_list
print(final_dict)
final_df = pd.DataFrame.from_dict(final_dict, orient='index')
Please see the following rows as an example of what I mean
This is for posterity. The new Clarifai GRPC client has different data structuring. So as a result, I had to parse them by first listing fields. This is what my final loop looked like. It had the ListFields added line
for i in range(5):
response_object = response.ListFields()[1]
test_meta = response_object[1][i].input.data.metadata
final_list = []
url = test_meta['link_url']
product_name = test_meta['product_name']
catalog_entry_id = test_meta['cat_entry_id']
catalog_path = test_meta['catalog_path']
catalog_name = test_meta['catalog_name']
final_list.append(i+1)
final_list.append(url)
final_list.append(catalog_entry_id)
final_list.append(product_name)
final_list.append(catalog_path)
final_list.append(catalog_name)
final_dict[file_id+'_'+str(i)] = final_list

Best way to refresh graph without page refresh (Python Django, ajax)

A bit of a general question - I am looking for ways to refresh a graph on a Django page based on user choices. The page has a graph, a few drop boxes where you can select parameters and a refresh button. Currently, I can capture the selections via ajax to my Django view and generate new data from database for the graph. I now need to feed that newly-generated data back into the graph and refresh it without a page refresh. Could anyone recommend the best methods of doing this?
Use JQuery to refresh graph without refreshing page.
I am using chart.js to create graph. first create a graph and on change event get updated data using Ajax URL call and assign values to chart data sets.
/** Graph Start Here */
window.chart = null;
$(document).on('change', '.graph-year-earning', function () {
var year = $(this).val();
$.get($('.graph-ajaxload-context').data('href'), { 'year': year, 'number': Math.floor(Math.random() * (1000000 - 10 + 1) + 10) }, function (response) {
window.chart.data.labels = response.labels;
window.chart.data.datasets[0].soldProductLabel = response.product_sold_label;
window.chart.data.datasets[0].totalCommissionLabel = response.monthly_commission_label;
window.chart.data.datasets[0].dataLabel = response.your_share_label;
if (response.total_commission == 0) {
window.chart.options.scales.yAxes[0].ticks.suggestedMin = 0;
window.chart.options.scales.yAxes[0].ticks.suggestedMax = 140000;
} else {
window.chart.options.scales.yAxes[0].ticks.suggestedMin = '';
window.chart.options.scales.yAxes[0].ticks.suggestedMax = '';
}
$.each(response.data, function (index, value) {
window.chart.data.datasets[0].soldProduct[index] = value[2];
window.chart.data.datasets[0].data[index] = Math.round(value[0]);
});
window.chart.update();
$(".txt-total-commission-by-year").html(response.total_commission)
$('.graph-ajaxload-context .inline-loader').hide();
});
});
if ($('.graph-ajaxload-context').length > 0) {
showLoader()
$('.graph-year-earning').trigger('change');
var ctx = $('#userEarningGraph');
window.chart = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [{
soldProductLabel: '',
soldProduct: [],
dataLabel: '',
data: [],
backgroundColor: '#ADAEB1',
hoverBackgroundColor: '#48C6B9'
}]
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
maxTicksLimit: 8,
userCallback: function (value, index, values) {
value = value.toString();
value = value.split(/(?=(?:...)*$)/);
value = value.join(',');
var currency_code = ' ₩'
if ($('.graph-ajaxload-context').data('currency-code') && $('.graph-ajaxload-context').data('currency-code') != 'None') {
currency_code = $('.graph-ajaxload-context').data('currency-code')
}
return value + ' ' + currency_code;
}
},
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
var soldProduct = data.datasets[tooltipItem.datasetIndex].soldProduct[tooltipItem.index];
var soldProductLabel = data.datasets[tooltipItem.datasetIndex].soldProductLabel;
var dataPro = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var dataLabel = data.datasets[tooltipItem.datasetIndex].dataLabel;
return [soldProductLabel + ':' + soldProduct, dataLabel + ':' + dataPro + ' ₩',];
}
}
}
}
});
}
$(document).on('click', '.showgraph', function (e) {
$('.graph-year-earning').trigger('change');
});
/** Graph End Here */

How to draw a scatterplot with superimposed pics?

I'm not a web programmer, but forced to deliver some kind of presentation. The task is as follows: I have a .csv with 2D points in each row, and these points have one-to-one mapping to some pictures by URL or integer index.
I need to draw a scatter plot these point in browser so that I plot associated pictures instead of plain points.
Very complex example is shown here just to get the idea of what I mean:
http://www.nytimes.com/interactive/2013/02/20/movies/among-the-oscar-contenders-a-host-of-connections.html
Again, all I want is to draw a scatterplot, in which there are rectangular pictures instead of plain points. Interactive zooming is preferred.
My candidates so far are D3 and Bokeh ( because I'm primarily Pythoniac man )
However, the better would be to take some existing solution as a template and then hack it to make suitable.
UPD: If it seems that I will hardly find something like that, it's okay to draw a plain points, but being able to hover over them with a mouse and displaying associated pictures for a point under mouse.
Again, thanks in advance!
I used D3
Here is an example of scatter chart with images:
http://plnkr.co/edit/A60Pv8I7tqSVGKU64bgr?p=preview
src code:
// Code goes here
var h = 500;
var w = 750;
var padding = 50;
//set your images and data here
var monthlySales = [{
'stock': 'GOOG',
'count': 500,
'img': "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcTccGK4ZWQcI3WB--hytI1DFaeZ8ii-6euDWrm-baUtAxR7w9OrWg"
}, {
'stock': 'MSFT',
'count': 250,
'img': "http://tr1.cbsistatic.com/fly/171-fly/bundles/techrepubliccore/images/icons/standard/icon-user-default.png"
}, {
'stock': 'FB',
'count': 50,
'img': "https://cdn1.iconfinder.com/data/icons/industry-2/96/Mine-512.png"
}, {
'stock': 'AAPL',
'count': 100,
'img': "https://cdn1.iconfinder.com/data/icons/industry-2/96/Mine-512.png"
}, {
'stock': 'EBAY',
'count': 5,
'img': "https://cdn1.iconfinder.com/data/icons/industry-2/96/Mine-512.png"
}, {
'stock': 'BABA',
'count': 37,
'img': "https://cdn1.iconfinder.com/data/icons/industry-2/96/Mine-512.png"
}];
var xScale = d3.scale.linear().domain([65, 450]).range([0, w]).nice();
var yScale = d3.scale.linear().domain([0, d3.max(monthlySales, function(d) {
return d.count;
})]).range([h, 0 + padding]).nice();
var radiusScale = d3.scale.linear().domain([0, d3.max(monthlySales, function(d) {
return d.count;
})]).range([10, 100]).nice();
var opacityScale = d3.scale.linear().domain([0, d3.max(monthlySales, function(d) {
return d.count;
})]).range([1, 0.1]).nice();
function ordinalValue(item) {
var strLen = item.length;
var total = 0;
for (var i = 0; i < strLen; i++) {
total += item.charCodeAt(i);
}
console.log(total);
return total;
}
var svg = d3.select('body').append('svg').attr({
width: w,
height: h,
style: "outline: thin solid blue"
});
var dots = svg.selectAll('g')
.data(monthlySales)
.enter()
.append("g");
dots.append('circle')
.attr({
cx: function(d) {
var ordValue = ordinalValue(d.stock);
return xScale(ordValue);
},
cy: function(d) {
return yScale(d.count);
},
r: function(d) {
return radiusScale(d.count);
},
fill: function(d) {
return '#' + Math.random().toString(16).substr(-6);
}
}).style({
"fill-opacity": function(d) {
return opacityScale(d.count);
},
"stroke": function(d) {
return '#' + Math.random().toString(16).substr(-6);
}
});
//adding an image to the group
dots.append("svg:image")
.attr("xlink:href",function(d) {return d.img})
.attr("height", "20")
.attr("width", "20")
.attr({
x: function(d) {
var ordValue = ordinalValue(d.stock);
return xScale(ordValue)-10;
},
y: function(d) {
return yScale(d.count)-10;
},
});

Categories