Ad hoc D3 function - d3.js

I have n columns of data that I would like to make D3 scatter plots from. It takes 2 columns to make a plot so n columns would end up n-permutations of 2 times of the same code that I would like to make a plot function from. Problem is I don't know how to set the variable in D3 fashion. An example of data and the snippet of code are bellow. I appreciate for any hint. Thanks.
// Potentially, there are a lot more columns.
var data = [
{"col1": 34, "col2": 54, "col3": 345, "col4": 35, "col5": 52},
{"col1": 75, "col2": 98, "col3": 917, "col4": 03, "col5": 47},
{"col1": 63, "col2": 23, "col3": 236, "col4": 34, "col5": 78},
{"col1": 23, "col2": 38, "col3": 198, "col4": 12, "col5": 18},
{"col1": 57, "col2": 48, "col3": 274, "col4": 67, "col5": 39},
{"col1": 65, "col2": 12, "col3": 381, "col4": 27, "col5": 45}
];
// The code is long
// I just list here the parts that are involved
// How to replace FOO and BAR with a pair of parameters "col1", "col2", etc ...
var x = d3.scale.linear().range([0, width]),
y = d3.scale.linear().range([height, 0]);
x.domain(d3.extent(data, function(q) {return q.FOO;}));
y.domain(d3.extent(data, function(q) {return q.BAR;}));
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 4)
.attr("cx", function(d) {return x(d.FOO);})
.attr("cy", function(d) {return y(d.BAR);})
.style('fill', 'blue');
EDIT:
I took a hint from mgold and make a function to rebuild the original dataset to a 2 columns dataset instead of a function of drawing scatter plot. It may not be elegant but just works. :) Thanks!
function get2Columns(foo, bar) {
var tempArr = [];
for (var i = 0; i < data.length; i++) {
tempArr.push({"FOO":data[i][foo],"BAR":data[i][bar]});
}
return tempArr;
}
data = get2Columns('col1', 'col2');

I'm not sure exactly what you're looking for, but I can try to talk about things that I think are relevant and hopefully give you some leads.
I would represent your data as an array of arrays, rather than an array of objects. This makes it much more amenable to array methods, from both vanilla JavaScript and D3. The transpose method may be especially useful, coupled with some mapping and reducing, for finding extents.
There are a handful of ways to choose two elements from an array or similar data structure. Zipping takes a pair of arrays and returns an array of pairs: the 0th items together, the 1st items together, and so on. Pairing takes one array and returns sequential pairs. Both of these have D3 implementations among those array methods.
Closer to what you want (maybe?) is the product of arrays, which given n arrays is every way to pick 1 item from each. This is typically represented as an array of arrays where the inner arrays have length n. There doesn't seem to be a D3 function to do this (I'd support adding one) but you can see this SO post. (No luck with Underscore either.)
But since you already have your matrix, maybe all you need to know is that functions like attr pass their second argument (when a function) not just the datum d but also the index i. You can also try using each and/or forEach to go from an array of values to individual values, keeping the indices distinct to get your 2D coordinate.
(On reread, the last paragraph is likely to be the most helpful.)

Related

Algorithm: Dynamic Sum of n Numbers

Algorithm question
The following array exists list = [1, 3, 6, 8, 12, 18, 25, 28, 30, 40, 45, 50, 60, 68, 78, 88, 98, 128, 158, 198, 248, 298 , 348, 418, 488, 548, 588, 618, 648, 698, 798, 818, 848, 898, 998, 1048, 1098, 1148, 1198, 1248, 1298, 1398, 1448, 149, 8, 1998 , 2298, 2598, 2998, 3298, 3998, 4498, 4998, 5898, 6498], the target value is a number, you need to select the sum of n numbers from list as target, n The range is [1,10], where items in the list allow repeated selection, for example:
Example1: Assuming target = 10,
✅ Possible outcomes are as follows:
Result 1: 10*1 = 10, => [1,1,1,1,1,1,1,1,1,1]
Result 2: 1*8 + 2*1 = 10, => [8,1,1]
Result 3: 1*6 + 3*1 + 1*1 = 10, => [6,3,1]
Result 4: 3*3 + 1*1 = 10, => [3,3,3,1]
...
Example2: Assuming target = 20,
✅ Possible outcomes are as follows:
Result 1: 18*1 + 2*1 = 20, => [18,1,1]
Result 2: 12*1 + 8*1 = 20, => [12,8]
...
❌ Bad Result:
20 1s, against the rules: pick up to 10
[1,2,5], the sum is not right
Note
Pick a maximum of 10, a minimum of 1, repeatable picks
if no suitable result is found, return undefined
Solution
Solution1- Recursive computation - 10/25/2022
Recursion always solves this kind of problem, maybe there are other better ways, I will keep updating and trying.
TypeScript Playground - Recursive computation
Question
I want to find an optimal solution, speed first.
const list = [
1,
3,
6,
8,
12,
18,
25,
28,
30,
40,
45,
50,
60,
68,
78,
88,
98,
128,
158,
198,
248,
298,
348,
418,
488,
548,
588,
618,
648,
698,
798,
818,
848,
898,
998,
1048,
1098,
1148,
1198,
1248,
1298,
1398,
1448,
1498,
1598,
1648,
1998,
2298,
2598,
2998,
3298,
3998,
4498,
4998,
5898,
6498,
];
function getCombinations(
list: number[],
target: number
): Array<number> | undefined {
// TODO...
}
This is solvable with dynamic programming. I'll just outline the solution. I do have other posts where I've actually given working code for problems with similar ideas. For example Finding all possible combinations of numbers to reach a given sum.
First imagine we have a 2-D data structure with the following information in each Node:
{
is_root: true|false, // True only at the root of the data structure
current_sum: ..., // At this node, here is the current sum
solution_count: ..., // How many solutions below here
current_value: ..., // A value to find more solutions under
current_value_count: ..., // How many times this value has been used.
// First dimension, go back by current_value
prev_sum_solution: ptr to Node,
// Second dimension, go back to current sum, previous value
prev_value_solution: ptr to Node,
}
These nodes will hold ONLY information for solutions. You can report how many there are, or produce the solutions recursively. If node.prev_sum_solution is not null, then node.prev_sum_solution->solution_count gives how many solutions will have current_value next. And if f node.prev_value_solution is not null, then node.prev_value_solution->solution_count gives how many solutions will have current_sum with a previous value next. This allows you to find any particular solution as well.
Now how do we generate this solution? Well first we initialize an array of target+1 pointers to nodes:
solution = [null, null, ..., null]
And then we replace solution[0] with our root node:
{
is_root: true,
current_sum: 0,
solution_count: 1,
current_value: 0,
current_value_count: 0,
prev_sum_solution: null,
prev_value_solution: null,
}
(Warning. I'll use a mix of notations for this pseudo-code. And specifically . accesses an object's properties, while -> accesses properties from a pointer to an object. You can make the ideas work in any language whether or not it has pointers though.)
And then we go as follows:
for value in list:
// Add solutions that use this value
for i in range(n): // ie i is in {0, 1, 2, ..., n-1}
// We iterate down to avoid solutions that use this.
for (j = list.length-1; (i+1) * value <= j; j--) {
this_solution = solution[j]
prev_solution = solution[j-value]
if prev_solution == null or
(prev_solution.current_value != value and 0 < i) or
(prev_solution.current_value_count != i-i):
continue // can't add value here.
else:
if (this_solution == null) {
solution[j] = pointer to Node{
is_root: false,
current_sum: i,
solution_count: prev_solution->solution_count,
current_value: value,
current_value_count: i,
prev_sum_solution: prev_solution,
prev_value_solution: null,
}
}
else {
solution[i] = pointer to Node{
is_root: false,
current_sum: j,
solution_count: prev_solution->solution_count + this_solution->solution_count,
current_value: value,
current_value_count: i,
prev_sum_solution: prev_solution,
prev_value_solution: this_solution,
}
}
}
}
And when we are done, if we have no mistakes, solution[limit] will hold a pointer to a Node that has all the information about all of the possible solutions that exist.

Minimising the number of bus changes in a matrix

[ 42, 45, 47, x, x] -> stop1 to stop2
[ 45, 47, 42, 88, x] -> stop2 to stop3
[ 21, 77, 42, x, x] -> stop3 to stop4
[ 22, 47, 42, 88, x] -> stop4 to stop5
[ 23, 47, 42, x, x] -> stop5 to stop6
[ 24, 47, 42, 8, 91] -> stop6 to stop7
[ 25, 13, 42, 3, 84] -> stop7 to stop8
[ 26, 10, 11, 4, 54] -> stop8 to stop9
[ 27, 9, 8, 88, 71] -> stop9 to stop10
x is there just for formatting. The first row means that there are only three buses from stop1 to stop2(42, 45, 47).
I have this matrix like structure where each row represents the buses going from one stop to another. I need to minimize the number of bus changes a person has to make to go from stop1 to stop10.
For example one of the output should be 42, 42, 42, 42, 42, 42, 42, 26, 27 another can be 42, 42, 42, 42, 42, 42, 42, 10, 9. If the number of changes is more than three I can discard the result.
What's the most optimal way to achieve this as brute forcing through it is pretty unefficient right now?
You can solve this problem by modeling it as a graph search.
Imagine you're a person and you're trying to get from point A to point B. The information most relevant to you is
where you currently are, and
which bus line, if any, you are currently on.
You can therefore model a person's state as a pair of a location (a bus stop) and a bus line (which might be "not on a line" when they start or finish). So create a graph with one node for each combination of a location and a bus line.
The edges in this graph will correspond to changes in state. You can change state either by
staying on your current bus line and going somewhere, or
switching bus lines.
If you're currently on a bus line, you can stay on that line to move from one location to the next if the line goes from the first location to the second. So create edges ((location1, line), (location2, line)) if bus line line goes from location1 to location2. This doesn't involve a transfer, so give this edge a cost of 0.
Alternatively, you can always get off of a bus or go from being off a bus to being on a bus. So add an edge ((location, line), (location, free)) for each line and each location (you always have the option to get off of a bus line) and give it cost 0, since this doesn't involve changing lines. Similarly, add edges ((location, free), (location, line)) for each bus line line available at the given location. Give it cost 1 to indicate that this requires you to get on a bus.
Now, imagine you find a path from (point A, free) to (point B, free) in this graph. This corresponds to getting on and off of a series of buses that start you at point A and end at point B, and the cost will be the number of different buses that you ended up getting on. If you run a shortest paths algorithm in this graph (say, Dijkstra's algorithm), you'll find the path from the start to end point that minimizes the number of bus transfers!
You could go through the array once, and keep a set of buses that are common to the visited stops. As soon as none such buses can be found, take the previous set, choose one bus from it, and fill the result with that bus for that many stops.
Then put all buses at the current stop in the set, and repeat the operation for the subsequent stops, ...etc.
Here is the algorithm coded in ES6 JavaScript. It uses a Set to allow constant-time access to the items (buses) it stores.
// Helper function: given a reduced set of buses, and a count,
// add one of those buses as the bus to take during that many stops
function addToResult(common, count, result) {
let bus = common.values().next().value; // pick any available bus
while (count > 0) {
result.push(bus);
count--;
}
}
// Main algorithm
function getBusRide(stops) {
if (stops.length === 0) return [];
let result = [],
count = 0,
common;
for (let buses of stops) {
if (count == 0) { // First iteration only
common = new Set(buses); // all buses are candidate
count = 1;
} else {
let keep = new Set();
for (let bus of buses) {
// Only keep buses as candidate when they
// are still served here
if (common.has(bus)) keep.add(bus);
}
if (keep.size == 0) { // Need to change bus
addToResult(common, count, result);
count = 0;
keep = new Set(buses); // all buses are candidate
}
common = keep;
count++;
}
}
addToResult(common, count, result);
return result;
}
// Sample input
const stops = [
[ 42, 45, 47],
[ 45, 47, 42, 88],
[ 21, 77, 42],
[ 22, 47, 42, 88],
[ 23, 47, 42],
[ 24, 47, 42, 8, 91],
[ 25, 13, 42, 3, 84],
[ 26, 10, 11, 4, 54],
[ 27, 9, 8, 88, 71]
];
// Apply the algorithm
console.log(getBusRide(stops));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This algorithm runs in O(n) where n is the total number of values in the input, so in the example n = 37.

ZIO 2013: Dolls

Your shop sells several different types of dolls. Each doll has a suggested price, and no
two types of doll have the same price. You would like to fix an actual selling price for
each doll so that dolls of different types are as different in price as possible. Due to
some government regulations, you can only modify the suggested price within a fixed
band of ±K—in other words, if the suggested price is p, you can pick any selling price
in the range {p− K, p− K + 1, . . . , p+ K −1, p+ K}. Of course, the selling price must
always be non-negative.
For instance, suppose there are four types of dolls with suggested prices 130, 210, 70
and 90 and you are allowed to modify prices within a band of 20. Then, you can adjust
the prices to 150, 210, 50 and 100, respectively, so that the minimum difference in price
between any two types of dolls is 50. (For the second doll, you could have picked any
price between 200 and 230.) You can check that this is the largest separation that you
can achieve given the constraint.
In each of the cases below, you are given a sequence of prices and the value of K. You
have to determine the maximum separation that you can achieve between all pairs in
the sequence if you are allowed to modify each price by upto ±K.
(a) K = 13. Sequence: 144, 152, 214, 72, 256, 3, 39, 117, 238, 280.
(b) K = 10. Sequence: 10, 48, 57, 32, 61, 74, 33, 45, 99, 81, 19, 24, 101.
(c) K = 20. Sequence: 10, 19, 154, 67, 83, 39, 54, 110, 124, 99, 139, 170
So basically, I just need to find the value of maximum separation without coding. I tried to devise an algorithm, but failed miserably, so I just started brute forcing it, by basically increasing/decreasing each of the prices by a certain value, but the bruteforcing applied here is just too tough due to the value of K. (It would have been simple for any K<6).
Can someone define a function or recurrence relation to calculate it? The solutions are up online, but they only give the answer as an integer and don't explain how to reach the solution. I am a beginner in programming, so try explaining using pseudocode/ little bit of C++, please. Thank you.
Source: http://www.iarcs.org.in/inoi/2013/zio2013/zio2013-qpaper.pdf
Solution: http://www.iarcs.org.in/inoi/2013/zio2013/zio2013-solutions.pdf
Here is a O(nlogn) algorithm.
To illustrate I will use the second example: 10, 48, 57, 32, 61, 74, 33, 45, 99, 81, 19, 24, 101 with K=10
Sort the list (10, 19, 24, 32, 33, 45, 48, 57, 61, 74, 81, 99, 101)
Use bisection to find the minimum separation x
For a trial value of x, assign the final values greedily placing them as small as possible while satisfying the conditions (non-negative, within K of original value, at least x greater than previous).
So let us start with x=10.
We will move as follows:
10->0 (can't go negative so this is smallest allowed)
19->10 (can't go within K=10 of the previous value)
24->20
32->30
33->40
45->50
48 becomes impossible. We can only assign values between 38 and 58, but none of these are more than 10 away from the previous 50.
We conclude that x=10 is too high a separation and we need to move lower.
You might try x=7 and find it is possible, x=9 find it is impossible, then try x=8:
10->0
19->9 (can only move to values 9->29)
24->17
32->25
33->33
45->41
48->49
57->56
61->64
74->72
81->80
99->89
101->97
And so we have found that x=8 is possible, x=9 is impossible and therefore x=8 is the maximum possible separation.

Creating multiple distinct path elements in D3JS

Let us consider this simple data:
var data = [
{
"id": "A",
"geometry": [ [0, 0], [10, 10], [10, 20], [0, 20] ]
},
{
"id": "B",
"geometry": [ [10, 10], [25, 10], [25, 30], [10, 20] ]
},
];
I'd like to display "A" and "B" using a distinct area for each, as doing so will let me apply a class to them (useful because I want them to use different background colors and to react separately to clicks and mouse hovers.)
I'm able to use d3.svg.area() to draw a continuous graph however it assumes that "the input data is a two-element array of numbers" (not my case) and it does not seem to support the drawing of distinct areas.
What is the pattern for it?
UPDATE
I'm using polygons in the sample data for simplicity. Overall, the goal however is to produce a stream that be composed of several areas instead of just a single one. Best illustrated with the picture below:
I'll update the post if more details are needed.
Hard to understand what exactly you mean by wanting to draw distinct areas. Do you mean that you want to end up with 2 path elements –– one for the geometry of the object with id:"A" and the other for id: "B"? If so:
var pathGenerator = d3.svg.area()
var paths = d3.select("svg").selectAll("path").data(data);
paths.enter()
.append("path")
.attr("class", function(d) {
if(d.id == "A") { return 'class-A'; }
else if(d.id == "B") { return 'class-B'; }
})
.attr("d", function(d) {
return pathGenerator(d.geometry);
});

D3 Interpolate Line Chart on Time Axes

I have a pretty basic line chart I'm using to plot some data on based on time. This is all working fine however the problem I need to address is when the plots along the time axes are very close.
Consider the following data points:
{ "Id": 101, x:15, y: 10, "OriginDt": "2012-12-01T18:49:06"}
{ "Id": 101, x:15, y: 10, "OriginDt": "2012-12-02T28:49:06"}
{ "Id": 101, x:15, y: 10, "OriginDt": "2012-12-03T18:49:06"}
{ "Id": 101, x:15, y: 10, "OriginDt": "2012-12-04T08:10:06"}
{ "Id": 101, x:15, y: 10, "OriginDt": "2012-12-04T09:21:06"}
{ "Id": 101, x:15, y: 10, "OriginDt": "2012-12-04T11:32:06"}
For the first 3 points the chart looks fine but as soon as it gets to data that occurs within a short space of time the data starts to get bunched up and very hard to appreciate in the chart.
I've been trying to find a sample online that deals with something like this but haven't found it yet.
I've tried using the basic interpolation commands on the axes but these don't work.
How can I change my axes:
var yAxis = d3.svg.axis()
.tickSize(-width).tickPadding(10)
.scale(y)
.orient("left");
To "spread out" the points in these situations?
This would be a comment but I don't have the necessary reputation.
You might want to look into cartesian fisheye distortion

Resources