Retrieve underlying values of a binned vega (lite) histogram? - d3.js

How can you retrieve and display the values used to compute the high of binned bars in a vega histogram?
In this d3 observable notebook I illustrate what want to achieve... When a bar is clicked, I update the selected_text variable with the bar's underlying values which were counted to give the bar's high.
The related question deals with the fold transform but in my case, I am looking for the inverse of the flatten transform.
Here is an illustration of the data transformation I am looking for. Going from:
[
{"name": "alpha", "data": 123, "bin0": 1, "bin1": 2},
{"name": "alpha", "data": 789, "bin0": 2, "bin1": 3},
{"name": "beta", "data": 456, "bin0": 2, "bin1": 3},
{"name": "beta", "data": 789, "bin0": 3, "bin1": 4},
{"name": "beta", "data": 0AB, "bin0": 3, "bin1": 4}
]
to
[
"bin0": 1, "bin1": 2, values: [{"name": "alpha", "data": 123}]
"bin0": 2, "bin1": 3, values: [{"name": "alpha", "data": 789},
{"name": "beta", "data": 456}]
"bin0": 3, "bin1": 4, values: [{"name": "beta", "data": 789},
{"name": "beta", "data": 0AB}]
]
I think this could be achieved if the concat expression was available to the groupby operator of the aggregation transforms, but it is not.

The "unflatten" transform can be roughly achieved using an aggregate transform with the values aggregate:
{
"data": {
"values": [
{"name": "alpha", "data": 123, "bin0": 1, "bin1": 2},
{"name": "alpha", "data": 789, "bin0": 2, "bin1": 3},
{"name": "beta", "data": 456, "bin0": 2, "bin1": 3},
{"name": "beta", "data": 789, "bin0": 3, "bin1": 4},
{"name": "beta", "data": 789, "bin0": 3, "bin1": 4}
]
},
"transform": [
{
"aggregate": [{"op": "values", "as": "values"}],
"groupby": ["bin0", "bin1"]
}
],
"mark": "point"
}
In the data viewer of the Vega Editor, you can see that the transformed data are:
[
{"bin0": 1, "bin1": 2,
"values": [{"name": "alpha", "data": 123, "bin0": 1, "bin1": 2}]},
{"bin0": 2, "bin1": 3,
"values": [{"name": "alpha", "data": 789, "bin0": 2, "bin1": 3},
{"name": "beta", "data": 456, "bin0": 2, "bin1": 3}]},
{"bin0": 3, "bin1": 4,
"values": [{"name": "beta", "data": 789, "bin0": 3, "bin1": 4},
{"name": "beta", "data": 0AB, "bin0": 3, "bin1": 4}]}
]

Given Jake's answer, here is a complete example in Vega of what I was aiming for.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A histogram of film ratings, modified to include null values.",
"width": 400,
"height": 200,
"padding": 5,
"autosize": {"type": "fit", "resize": true},
"signals": [
{"name": "binCount", "update": "(bins.stop - bins.start) / bins.step"},
{"name": "barStep", "update": "(width ) / (1 + binCount)"},
{
"name": "tooltip",
"value": {},
"on": [
{"events": "rect:mouseover", "update": "slice(datum.dlist,0,2)"},
{"events": "rect:mouseout", "update": "{}"}
]
}
],
"data": [
{
"name": "table",
"values": [
{"name": "alpha", "data": "123"},
{"name": "alpha", "data": "789"},
{"name": "beta", "data": "456"},
{"name": "beta", "data": "789"},
{"name": "gamma", "data": "789"},
{"name": "beta", "data": "300"}
],
"transform": [
{"type": "extent", "field": "data", "signal": "extent"},
{
"type": "bin",
"signal": "bins",
"field": "data",
"extent": {"signal": "extent"},
"maxbins": 4
}
]
},
{
"name": "aggregat",
"source": "table",
"transform": [
{
"type": "aggregate",
"groupby": ["bin0", "bin1"],
"ops": ["values"],
"fields": ["undefined"],
"as": ["values"]
}
]
},
{
"name": "counts",
"source": "table",
"transform": [{"type": "aggregate", "groupby": ["bin0", "bin1"]},
{
"type": "lookup",
"from": "aggregat",
"key": "bin0",
"fields": ["bin0"],
"values": ["values"],
"as": ["dlist"]
}]
}
],
"scales": [
{
"name": "yscale",
"type": "linear",
"range": "height",
"round": true,
"nice": true,
"domain": {"fields": [{"data": "counts", "field": "count"}]}
},
{
"name": "xscale",
"type": "linear",
"range": [{"signal": "barStep "}, {"signal": "width"}],
"round": true,
"domain": {"signal": "[bins.start, bins.stop]"},
"bins": {"signal": "bins"}
}
],
"axes": [
{"orient": "bottom", "scale": "xscale", "tickMinStep": 0.5},
{"orient": "left", "scale": "yscale", "tickCount": 5, "offset": 5}
],
"marks": [
{
"type": "rect",
"from": {"data": "counts"},
"encode": {
"update": {
"tooltip" : { "signal": "tooltip"},
"x": {"scale": "xscale", "field": "bin0", "offset": 1},
"x2": {"scale": "xscale", "field": "bin1"},
"y": {"scale": "yscale", "field": "count"},
"y2": {"scale": "yscale", "value": 0},
"fill": {"value": "steelblue"}
},
"hover": {"fill": {"value": "firebrick"}}
}
}
]
}

Related

How to use field value as a color value in vega lite pie chart?

I came across this pie chart vega lite visualization:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple pie chart with labels.",
"data": {
"values": [
{"category": "a", "value": 4},
{"category": "b", "value": 6},
{"category": "c", "value": 10},
{"category": "d", "value": 3},
{"category": "e", "value": 7},
{"category": "f", "value": 8}
]
},
"encoding": {
"theta": {"field": "value", "type": "quantitative", "stack": true},
"color": {"field": "category", "type": "nominal", "legend": null}
},
"layer": [{
"mark": {"type": "arc", "outerRadius": 80}
}, {
"mark": {"type": "text", "radius": 90},
"encoding": {
"text": {"field": "category", "type": "nominal"}
}
}]
}
It renders as follows:
My data contains color key:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple pie chart with labels.",
"data": {
"values": [
{"category": "a", "value": 4, "color": "rgb(121, 199, 227)"},
{"category": "b", "value": 6, "color": "rgb(140, 129, 22)"},
{"category": "c", "value": 10, "color": "rgb(96, 43, 199)"},
{"category": "d", "value": 3, "color": "rgb(196, 143, 99)"},
{"category": "e", "value": 7, "color": "rgb(12, 103, 19)"},
{"category": "f", "value": 8, "color": "rgb(196, 243, 129)"}
]
},
"encoding": {
"theta": {"field": "value", "type": "quantitative", "stack": true},
"color": {"field": "color", "type": "nominal", "legend": null}
},
"layer": [{
"mark": {"type": "arc", "outerRadius": 80}
}, {
"mark": {"type": "text", "radius": 90},
"encoding": {
"text": {"field": "category", "type": "nominal"}
}
}]
}
I want to use the rgb() color value specified in this color key's value to color individual pie. I tried specifying this field in color channel: "field": "color".
"color": {"field": "color", "type": "nominal", "legend": null}
However, no use. It still renders the same as above. How can use color value specified in field's value as actual color?
PS: Link to above visualization.
The documentation says:
To directly encode the data value, the scale property can be set to null.
So you need to set the scale to null.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple pie chart with labels.",
"data": {
"values": [
{"category": "a", "value": 4, "color": "rgb(121, 199, 227)"},
{"category": "b", "value": 6, "color": "rgb(140, 129, 22)"},
{"category": "c", "value": 10, "color": "rgb(96, 43, 199)"},
{"category": "d", "value": 3, "color": "rgb(196, 143, 99)"},
{"category": "e", "value": 7, "color": "rgb(12, 103, 19)"},
{"category": "f", "value": 8, "color": "rgb(196, 243, 129)"}
]
},
"encoding": {
"theta": {"field": "value", "type": "quantitative", "stack": true},
"color": {"field": "color", "type": "nominal", "legend": null, "scale":null}
},
"layer": [
{"mark": {"type": "arc", "outerRadius": 80}},
{
"mark": {"type": "text", "radius": 90},
"encoding": {"text": {"field": "category", "type": "nominal"}}
}
]
}
This outputs:

Using a query in the lookup transform of vega-lite inside Kibana

I have an index containing two fields which are disjoint. Since I can't fetch both the fields using a union query (Elasticsearch doesn't support union queries yet), I was trying to use Vega-Lite's lookup transform to send a separate query to retrieve the second disjoint field.
[{"a": "10", "b": "x"}, {"a": "20", "b": "y"}, {"a": "25", "b": "z"} ....
.... {"a": "21", "c": "l"}, {"a": "22", "c": "m"}, {"a": "40", "c": "n"}]
I'd like to get the values for b and c for a's from 20 to 25
The answer in this case would be b = ["y", "z"], c = ["l", "m"]
My vega-lite json config looks like this:
{
"data": {
"url": {
"index": "index-name",
"query": "" // Query to fetch all values of b
},
"transform": [{
"lookup": "", // What to put here?
"from": {
"data": {
"url": {
"index": "index-name",
"query": "" // Query to fetch all values of c
}
},
"key": "", // What to put here?
"fields": [] // and here?
}
}],
"mark": ...,
"encoding": {
...
}
}
Essentially what I'm looking for is obtaining the values of b first and then selecting a range of a's
and querying again over this range of a's for c's.
Is it possible?
Because I keep running into errors in Vega-lite.
You can try the following in vega-lite editor. You will need 2 data set, 1 is c_data and the other is for b_data. The transform lookup is like a left join, so the join will only work for the 'a' field having common values.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"datasets": {
"c_data": [
{"a": "21", "c": "l"},
{"a": "25", "c": "m"},
{"a": "40", "c": "n"}
]
},
"data": {
"name": "b_data",
"values": [
{"a": "10", "b": "x"},
{"a": "20", "b": "y"},
{"a": "25", "b": "z"}
]
},
"transform": [
{
"lookup": "a",
"from": {"data": {"name": "c_data"}, "key": "a", "fields": ["c"]},
"as": ["c"]
}
],
"width": "container",
"height": "container",
"mark": {"type": "bar"},
"encoding": {"x": {"field": "c"}, "y": {"field": "a", "type": "quantitative"}}
}
Let me know if this works for you.

Vega Lite / Kibana - Area Mark shows no values

I have a pretty straightforward problem but I'm totally new to Vega/Vega-Lite and the tutorials examples don't help me much to resolve my issue.
When I try to display my floating point values only Mark: Point/Bar seems to work. Everything else that demands a connection between neighboring points seems to fail, like "Area" or "Line".
What have I missed in order to connect my values to a Area-Chart?
Aggregation? Layer? Is the timestamp values calculation wrong?
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {
"url": {
"%context%": true,
"%timefield%": "#timestamp",
"index": "default-*",
"body": {"size": 10000, "_source": ["#timestamp", "numericData"]}
},
"format": {"property": "hits.hits"}
},
"transform": [
{"calculate": "toDate(datum._source['#timestamp'])", "as": "time"}
],
"vconcat": [
{
"width": 1200,
"mark": {"type": "area", "line": true, "point": true},
"encoding": {
"x": {
"field": "time",
"scale": {"domain": {"selection": "brush"}},
"type": "temporal",
"axis": {"title": ""}
},
"y": {
"field": "_source.numericData",
"type": "quantitative",
"scale": {"domain": [0, 10]}
}
}
},
{
"width": 1200,
"height": 60,
"mark": {"type": "area", "line": true, "point": true}, // <-- only points are rendered :(
"selection": {"brush": {"type": "interval", "encodings": ["x"]}},
"encoding": {
"x": {"field": "time", "type": "temporal"},
"y": {
"field": "_source.numericData",
"type": "quantitative",
"formatType": "String",
"axis": {"tickCount": 3, "grid": false}
}
}
}
]
}
Points are visible - the values are there but the Area doesn't get rendered because, I suspect, I need to tell Vega Lite to interpret the numerical float values on Y to be interpreted over the whole timefield.
You didn't share your data, so I can only guess why this is happening. But one reason you might see this result is if there are null values interspersed in your data. Here is a simple example of this (open in editor):
{
"data": {
"values": [
{"x": 1, "y": 1},
{"x": 2, "y": null},
{"x": 3, "y": 2},
{"x": 4, "y": null},
{"x": 5, "y": 3},
{"x": 6, "y": null}
]
},
"mark": {"type": "area", "point": true, "line": true},
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "y", "type": "quantitative"}
}
}
Unlike ponts, lines and areas are defined not via single values, but via adjacent values. Because there are no pairs of adjacent non-null values, there is no place where a line or area will be drawn.
If this is the case, you can remove the null points with an appropriate filter transform (open in editor):
{
"data": {
"values": [
{"x": 1, "y": 1},
{"x": 2, "y": null},
{"x": 3, "y": 2},
{"x": 4, "y": null},
{"x": 5, "y": 3},
{"x": 6, "y": null}
]
},
"transform": [{"filter": "isValid(datum.y)"}],
"mark": {"type": "area", "point": true, "line": true},
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "y", "type": "quantitative"}
}
}

How to filter laravel collection from given data

i have a collection data
{
"success": true,
"doctor": [
{
"id": 1,
"name": "Dr. Mayank",
"dob": "1975-01-01",
"about": "This is description",
"status": 1,
"rating": 2,
"rating_given_by": 1,
"alternative_number": "7686876876",
"profile_photo": [],
"speciality": [
{
"id": 3,
"name": "Acupuncture",
"image": null,
"dashboard_flag": 1
},
{
"id": 4,
"name": "Acupuncturist",
"image": null,
"dashboard_flag": 1
},
{
"id": 1,
"name": "Accident and emergency medicine",
"image": "http://192.168.16.21/remidify/media/174/detail.png",
"dashboard_flag": 1
}
],
"service": [
{
"id": 78,
"name": "Correction of gummy smile",
"cost": "12.00"
},
{
"id": 77,
"name": "Dental aesthetics",
"cost": "43.00"
}
],
"clinics": [
{
"id": 1,
"name": "akram",
"entity_id": 1,
"entity_type": "App\Doctor",
"contact_number": "2132132132132",
"status": 0,
"consultancy_fee": "12.00",
"available_today": "No",
"owner_name": "Dr. Mayank",
"pivot": {
"doctor_id": 1,
"clinic_id": 1
},
"address": {
"id": 1,
"address_1": "asdasdasdsa",
"address_2": "",
"locality": "downtown",
"city": "noida",
"state": "up",
"postal_code": "41561566"
},
"speciality": [],
"service": [
{
"id": 11,
"name": "Laminates",
"cost": "20.00"
},
{
"id": 12,
"name": "Dental surgery",
"cost": "300.00"
}
],
"clinic_image": [
{
"id": 7,
"model_id": 1,
"model_type": "App\Clinic",
"collection_name": "clinic_image",
"file_name": "1494863957588.566162.jpg",
"disk": "media",
"url": "http://192.168.16.21/remidify/media/7/1494863957588.566162.jpg"
}
],
"id_image": [
{
"id": 8,
"model_id": 1,
"model_type": "App\Clinic",
"collection_name": "id_image",
"file_name": "1494863966218.348877.jpg",
"disk": "media",
"url": "http://192.168.16.21/remidify/media/8/1494863966218.348877.jpg"
}
],
"location": {
"id": 1,
"latitude": 0,
"longitude": 0,
"entity_id": 1,
"entity_type": "App\Clinic",
"created_at": "2017-05-16 03:00:10",
"updated_at": "2017-05-16 03:00:10"
},
"clinic_timings": [
{
"day": "sun",
"opens_at": "09:28:00",
"closes_at": "21:28:00"
}
]
}
],
"education": [
{
"id": 19,
"degree": "MBBS",
"university": "Univercity",
"year": "2017",
"entity_id": 1,
"entity_type": "App\Doctor",
"created_at": "2017-05-16 05:44:11",
"updated_at": "2017-05-16 05:44:11",
"location": "Delhi"
}
],
"experience": [
{
"id": 19,
"hospital": "Hospital name",
"post": "pta ni hai",
"from": "1970-01-01",
"to": "0000-00-00",
"entity_id": 1,
"entity_type": "App\Doctor",
"created_at": "2017-05-16 05:44:12",
"updated_at": "2017-05-16 05:44:12",
"location": "Locations12",
"is_currently_working": 1
}
],
"registration": {
"id": 1,
"registration_number": "Reg # 2324324",
"registration_year": 1975,
"registration_council": "Council",
"experience": null,
"doctor_id": 1,
"created_at": "2017-05-16 02:56:37",
"updated_at": "2017-05-16 02:56:37",
"adhaar_number": "232131231232",
"id_proof": [
{
"id": 2,
"model_id": 1,
"model_type": "App\DoctorRegistration",
"collection_name": "id_proof",
"file_name": "1494863680447.329102.jpg",
"disk": "media",
"url": "http://192.168.16.21/remidify/media/2/1494863680447.329102.jpg"
}
],
"registration_proof": [
{
"id": 3,
"model_id": 1,
"model_type": "App\DoctorRegistration",
"collection_name": "registration_proof",
"file_name": "1494863687436.266846.jpg",
"disk": "media",
"url": "http://192.168.16.21/remidify/media/3/1494863687436.266846.jpg"
}
],
"qualification_proof": [
{
"id": 4,
"model_id": 1,
"model_type": "App\DoctorRegistration",
"collection_name": "qualification_proof",
"file_name": "1494863695576.803955.jpg",
"disk": "media",
"url": "http://192.168.16.21/remidify/media/4/1494863695576.803955.jpg"
}
]
},
"preference": {
"availability": 1,
"appointment_confirmation_method": "manual",
"average_time": 7,
"holiday_from": null,
"holiday_till": null,
"patients_per_hour": null,
"preferred_appointment_type": "timeslot",
"appointment_frequency": null,
"preferred_payment_method": [
{
"payment_method": "cash"
},
{
"payment_method": "online"
}
]
},
"user": null,
"doctor_clinic": [
{
"doctor_id": 1,
"clinic_id": 1,
"consultancy_fee": "12.00",
"deleted_at": null,
"workdays": [
{
"day": "sun",
"available": 1,
"workhours": [
{
"from": "09:28:00",
"to": "21:28:00"
}
]
}
],
"service": []
}
]
}
]
}
Now how can i find the doctors whose "about or specialty name" matches with some given search string.
Thanks in advance.
Try using dd() helper and use like this below
$youcollectionvariables = { "success": true, "doctor": [ { "id": 1, "name": "Dr. Mayank", "dob": "1975-01-01"................. }
dd($youcollectionvariables)
you will see a formatted data and can find what you need clearly.
Hope that helps.

How to set locale (globally) in Vega

I would like to configure the locale for a visualization in Vega. I need a German number format, which means that I need a comma (,) as decimal separator. Without any configuration in Vega, a decimal point (.) is used as separator.
Here is a complete working example which shows a simple bar chart. As you can see in the image below, I want to format the numbers on the y-Axis and the values inside the bars with two decimal places.
How can I set a specific locale (e.g. German) either globally for the whole visualization or for each number format separately? (I would prefer a global setting.)
(Note: You can use the Vega Editor to paste and try out my bar chart example.)
{
"width": 600,
"height": 300,
"padding": {"top": 10, "left": 35, "bottom": 30, "right": 10},
"data": [
{
"name": "table",
"values": [
{"x": 1, "y": 0.5},
{"x": 2, "y": 0.8},
{"x": 3, "y": 0.3},
{"x": 4, "y": 0.6}
]
}
],
"scales": [
{
"name": "x",
"type": "ordinal",
"range": "width",
"domain": {"data": "table", "field": "x"},
"padding": 0.1
},
{
"name": "y",
"type": "linear",
"range": "height",
"domain": {"data": "table", "field": "y"}
}
],
"axes": [
{"type": "x", "scale": "x"},
{"type": "y", "scale": "y", "format": ".2f"}
],
"marks": [
{
"type": "rect",
"from": {"data": "table"},
"properties": {
"enter": {
"x": {"scale": "x", "field": "x"},
"width": {"scale": "x", "band": true, "offset": -1},
"y": {"scale": "y", "field": "y"},
"y2": {"scale": "y", "value": 0}
},
"update": {
"fill": {"value": "steelblue"}
}
}
},
{
"type": "text",
"from": {"mark": "bars"},
"properties": {
"enter": {
"y": {"field": "y", "offset": 10},
"x": {"field": "x"},
"dx": {"field": "width", "mult": 0.6},
"fill": {"value": "white"},
"align": {"value": "right"},
"baseline": {"value": "middle"},
"text": {"template": "{{datum.datum.y | number:'.2f'}}"}
}
}
}
]
}
I found a pull request in the Vega GitHub repository which allows to set the number and time locale. Also runtime changes are supported.
Example for changing the number format to German:
vg.util.format.numberLocale({
decimal: ",",
thousands: ".",
grouping: [3],
currency: ["", "\xa0€"]
});
Example for changing the time format to German:
vg.util.format.timeLocale({
dateTime: "%A, der %e. %B %Y, %X",
date: "%d.%m.%Y",
time: "%H:%M:%S",
periods: ["AM", "PM"], // unused
days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
shortDays: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
shortMonths: ["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
});

Resources