How to show only 24hr column in line chart? - ios

I'm working with chart-ios library and i manage to show data in graph like below image.
My Json data is like below. i showing field1+field2+field3 in X and created_at in y axis. this data is for 1 day.
[
{
"created_at": "2020-05-10T00:01:09Z",
"field1": 17.57,
"field2": 56.37,
"field3": 44.06,
"equipment": 3
},
{
"created_at": "2020-05-10T00:01:55Z",
"field1": 17.57,
"field2": 54.52,
"field3": 44.62,
"equipment": 3
},
{
"created_at": "2020-05-10T00:02:40Z",
"field1": 21.63,
"field2": 52.7,
"field3": 51.27,
"equipment": 3
},
{
"created_at": "2020-05-10T00:03:26Z",
"field1": 17.15,
"field2": 54.95,
"field3": 44.21,
"equipment": 3
},
{
"created_at": "2020-05-10T00:04:12Z",
"field1": 15.97,
"field2": 54.69,
"field3": 41.36,
"equipment": 3
},
{
"created_at": "2020-05-10T00:04:57Z",
"field1": 15.86,
"field2": 38.58,
"field3": 36.59,
"equipment": 3
},
{
"created_at": "2020-05-10T00:05:43Z",
"field1": 7.66,
"field2": 51.6,
"field3": 45.46,
"equipment": 3
}
..... 1000 more...
]
What i want is to show x axis label in 1-hour interval. like 11:00, 12:00 and 13:00 , so on. its like fixed 24 hours i want to show, not more than that. but x value count is more than 1000 records.
so how do i do this? can any one point me out in right direction?
Thanks

X value need to be Double -> usually I use timeIntervalSince1970
you need a CustomValueFormatter
set xAxis minimum to date with 00:00
set xAxis maximum to date with 00:00 + 1 day
xAxis.granularity = 3600 -> only show label for every hour
chart.setVisibleXRange(minXRange: 5 * 3600, maxXRange: 5 * 3600) -> show only 5 hour range with scroll
check this project: https://github.com/aiwiguna/ExampleCharts/tree/feature/hourly-charts

Related

Highcharts: yAxis duration in hours resets every 24h

The image speaks by itself, I want to display a duration in hours on the yAxis. but everytime the value reaches 24h, the yAxis resets to 0. the actual value of the blue bar in the image is 28:19:15 but it shows only 4 hours because it has been resetted (4th value of yAxis shows 0)
my current config:
{
"chart": {
"type": "column"
},
"plotOptions": {
"column": {
"dataLabels": {
"enabled": true
}
}
},
"series": [
{
"data": [
101955467,
0,
0
],
"dataLabels": {
"enabled": true,
"format": "{y:%H:%M:%S}"
},
"name": "duration",
"stack": null,
"yAxis": "1"
}
],
"xAxis": [
{
"categories": [
"cat 1",
"cat 2",
"cat 3"
]
}
],
"yAxis": [
{
"allow_decimals": false,
"dateTimeLabelFormats": {
"day": "%H:%M",
"hour": "%H:%M",
"minute": "%H:%M",
"month": "%H:%M",
"second": "%H:%M",
"week": "%H:%M",
"year": "%H:%M"
},
"id": "1",
"labels": {
"format": "{value:%H:%M:%S}"
},
"max": 101955467,
"min": 0,
"opposite": false,
"title": {
"text": "duration"
},
"type": "datetime"
}
]
}
In fact, you want to get a total time instead of DateTime. In this case, you'll need to create your own label formatter that will convert milliseconds to a number of hours, minutes and seconds.
Example code:
dataLabels: {
enabled: true,
formatter: function() {
let s, m, h;
s = this.y / 1000;
h = parseInt(s / 3600);
s = s % 3600;
m = parseInt(s / 60);
s = (s % 60).toFixed(0);
let number = (h + ":" + m + ":" + s);
return number;
}
}
API Reference:
https://api.highcharts.com/highcharts/series.column.dataLabels.formatter
Demo:
https://jsfiddle.net/BlackLabel/Lcjewr2o/

Group a collection by a certain criteria

so I have a collection in mongoDB that has an structure like the following:
{
"_id": 129flaslkfja,
"number": 10
}
in this collection some elements have the number field set to 1 others to 2, others to 3, etc. in random order. I want to get an array that has in the first position all models with the number 1 in the next position all the models with the number 2 and so on. I know I can do this with a loop and a simple find or where but I want to know if there's a simpler, cleaner and more effective way in terms of performance.
I have tried with $group, $match but this is not what I want.
For example this is the data in my database:
[{
"_id": a1,
"number": 1
},
{
"_id": a2,
"number": 3
},
{
"_id": a3,
"number": 2
},
{
"_id": a4,
"number": 2
},
{
"_id": a5,
"number": 2
},
{
"_id": a6,
"number": 1
}]
So I want to group by number value like this:
[
[{
"_id": a1,
"number": 1
},
{
"_id": a6,
"number": 1
}],
[{
"_id": a3,
"number": 2
},
{
"_id": a4,
"number": 2
},
{
"_id": a5,
"number": 2
}],
[{
"_id": a2,
"number": 3
}]
]
You can also try below code
original_list.group_by(&:number).values
In ruby you can do:
original_list.group_by {|x| x[:number]}.values
It's not clear if you were asking for a Ruby solution, though.

Highcharts - show only years in xAxis

I have grouped data by year and want to show only 2 bars (2014 + 2015).
Any ideas why highchart also includes months between the bars?
"chart": {
"type": "column"
},
"plotOptions": {
"column": {
"stacking": "normal"
},
},
"series": [
{
"data": [
[1388534400000,88]
],
"name": "First",
"id": "series-8"
},
{
"data": [
[1388534400000,39]
],
"name": "2nd",
"id": "series-9"
},
{
"data": [
[1420070400000,34]
],
"name": "3rd",
"id": "series-10"
}
],
"xAxis": {
"type": "datetime",
"dateTimeLabelFormats": {
"year": "%Y"
}
}
jsfiddle: http://jsfiddle.net/747hs83s/
I would like to have only 2014 and 2015 labels on x-axis.
You can define a pointRange as 1 year (365 * 24 * 3600 * 1000, time in miliseconds) and set tickInterval with the same value.
"plotOptions": {
"column": {
pointRange: 365 * 24 * 3600 * 1000,
"stacking": "normal"
},
},
"xAxis": {
tickInterval: 365 * 24 * 3600 * 1000,
"type": "datetime",
"dateTimeLabelFormats": {
"year": "%Y"
}
}
Example: http://jsfiddle.net/3d3jzywq/
The answer to "why does the chart show months" is simply that there are months between your data points to be shown, and the chart has no way of knowing that you don't want it to show them.
There are probably numerous solutions, but the one I would use is the pointRange property - tell the chart that each column represents a year, and it will display it accordingly:
pointRange:86400000 * 365//one year
Example:
http://jsfiddle.net/jlbriggs/747hs83s/2/
By default, it will show the years as labels. If you show/hide series, that may change. There are a number of ways to format the label as well.
Label formatting references:
http://api.highcharts.com/highcharts#xAxis.labels.format
http://api.highcharts.com/highcharts#xAxis.labels.formatter
http://api.highcharts.com/highcharts#Highcharts.dateFormat
http://api.highcharts.com/highcharts#Highcharts.dateFormats
http://api.highcharts.com/highcharts#xAxis.dateTimeLabelFormats

Find open Shops through Timetable with Elasticsearch/Tire

I have model Shop each has relation with Timetable which could contain something like:
shop_id: 1, day: 5, open_hour: 7, open_minutes: 0, close_hour: 13, close_minute: 30
shop_id: 1, day: 5, open_hour: 14, open_minutes: 30, close_hour: 18, close_minute: 00
of course Timetable could have more elegant format, but question is next: how with elasticsearch(tire) could I find Shop which is open?
all Idea will be apreciated! Thanks!
Found solution:
create separate index for each day (sunday, monday, ..)
for each day build full array of minutes from Timetable:
((open_hour * 60 + open_minute)..(close_hour * 60 + close_minute)).to_a
add filter to search:
filter :term, current_day_name => (current_hour * 60 + current_minutes)
this solution works as well, but it looks cumbersome, because if Shop works 8-h hours per day I have created array with size: 8 * 60 = 480 (which is converted to string as indexed field), so thats why this question is still open, and maybe someone will find better solution
Tire part for #Andrei Stefan answer:
indexes :open_hours, type: :nested do
indexes :open, type: 'integer'
indexes :close, type: 'integer'
end
open_hours_query = Tire::Search::Query.new do
filtered do
query { all }
filter :range, "open_hours.open" => { lte: current_time }
filter :range, "open_hours.close" => { gte: current_time }
end
end
filter :nested, { path: 'open_hours', query: open_hours_query.to_hash }
I would consider doing it like the following:
The opening and closing hours are integer values of an array of nested objects in Elasticsearch:
Example: shop opening at 07:00 and closing at 13:30 and then opening at 14:30 and closing at 18:00 in day 1 would be translated to this in ES:
"shop_name": "Shop 1",
"open_hours": [
{ "open": 420, "close": 810 },
{ "open": 870, "close": 1080 }
]
Each day in the week (1 -> 7) represents a value (to be added to the number of minutes):
Day 1 = addition 0
Day 2 = addition 2000
Day 3 = addition 4000
...
Day 7 = addition 10000
So, for each day there is an increment of 2000 because each day contains at most 1440 minutes (24 hours * 60 minutes) and to be able to differentiate one day from a single number these numbers don't have to intersect.
So, the example above with the shop opening at 07:00 would be translated for Day 4 for example to this:
"shop_name": "Shop 1",
"open_hours": [
{ "open": 6420, "close": 6810 },
{ "open": 6870, "close": 7080 }
]
When querying these documents, that point of the day you want to search needs to obey the same rules as above. For example, if you want to see if in Day 4 at 13:45 the "Shop 1" is opened you would search for a (6000 + 13*60 + 45 = 6825) minute.
The mapping for everything above in Elasticsearch would be this:
{
"mappings": {
"shop" : {
"properties": {
"shop_name" : { "type" : "string" },
"open_hours" : {
"type" : "nested",
"properties": {
"open" : { "type" : "integer" },
"close": { "type" : "integer" }
}
}
}
}
}
}
Test data:
POST /shops/shop/_bulk
{"index":{}}
{"shop_name":"Shop 1","open_hours":[{"open":420,"close":810},{"open":870,"close":1080}]}
{"index":{}}
{"shop_name":"Shop 2","open_hours":[{"open":0,"close":500},{"open":1000,"close":1440}]}
{"index":{}}
{"shop_name":"Shop 3","open_hours":[{"open":0,"close":10},{"open":70,"close":450},{"open":900,"close":1050}]}
{"index":{}}
{"shop_name":"Shop 4","open_hours":[{"open":2000,"close":2480}]}
{"index":{}}
{"shop_name":"Shop 5","open_hours":[{"open":2220,"close":2480},{"open":2580,"close":3000},{"open":3100,"close":3440}]}
{"index":{}}
{"shop_name":"Shop 6","open_hours":[{"open":6000,"close":6010},{"open":6700,"close":6900}]}
Querying for shops opened in Day 2 at minute #2400 of the day (06:40):
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "open_hours",
"query": {
"bool": {
"must": [
{
"filtered": {
"filter": {
"range": {
"open_hours.open": {
"lte": 2400
}}}}},
{
"filtered": {
"filter": {
"range": {
"open_hours.close": {
"gte": 2400
}}}}}
]
}}}}
]
}}}
Would output Shop 4 and Shop 5:
"shop_name": "Shop 4",
"open_hours": [
{
"open": 2000,
"close": 2480
}
]
"shop_name": "Shop 5",
"open_hours": [
{
"open": 2220,
"close": 2480
},
{
"open": 2580,
"close": 3000
},
{
"open": 3100,
"close": 3440
}
]
LATER EDIT: since Elasticsearch has come a looong way since I added this reply and many things changed since then, a filtered filter (in the context of the bool must I used) can be replaced by a bool filter or even a simple must. Also, the string doesn't exist in 6.x anymore, so you can use text if you somehow need to search by shop name using analyzers, or keyword ("shop_name" : { "type" : "text" },):
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "open_hours",
"query": {
"bool": {
"filter": [
{
"range": {
"open_hours.open": {
"lte": 2400
}
}
},
{
"range": {
"open_hours.close": {
"gte": 2400
}
}
}
]
}
}
}
}
]
}
}
}

How to display points by day and not time?

I'm trying to use Highstock and am having a problem. When I have multiple series' on the chart, I want the points from the same day to match up, but currently they don't because Highstock takes the time into account.
Here is an example chart:
$('#chart').highcharts('StockChart', {
plotOptions: {
series: {
animation: {
duration: 0
}
}
},
yAxis: {
labels: {
align: 'right',
x: -4,
formatter: function() {
return '$' + Math.round(this.value);
},
},
tickPixelInterval: 40
},
series : [
{
"name": "Series 1",
"data": [
[
1389486123000,
200
],
[
1389578849000,
200
]
],
"tooltip": {
"valueDecimals": 2,
"valuePrefix": "$"
}
},
{
"name": "Series 2",
"data": [
[
1389486240000,
100
],
[
1389578849000,
100
]
],
"tooltip": {
"valueDecimals": 2,
"valuePrefix": "$"
}
}
]
});
JSFiddle: http://jsfiddle.net/EdvVW/
As you can see in JSFiddle, the second two points match up (and when you hover your mouse over them, they both show up in the tooltip, which is what I'm wanting), but even though the first two points are from the same day they don't match up because their timestamps are different.
Is there any way to decrease the accuracy of the rendered points so that data points from the same day match up regardless of time?
No, it's not possible. You need to have the same timestamp to match points.
You can preprocess your data: go over all points, create date from timestamp, then use Date.UTC(year, month, day); as x-value for a point.

Resources