What is d3.geo.pipeline? - d3.js

If you follow Mike Bostock's bl.ocks, you know for the last 8 months d3.geo.pipeline() has been a frequent component to his projects.
But what's it do?
You see him set pipelines up like this:
var sketch = d3.geo.pipeline()
.source(d3.geo.jsonSource)
.pipe(resample, .020)
.pipe(jitter, .004)
.pipe(smooth, .005)
.sink(d3.geo.jsonSink);
via
There is no documentation in the d3.geo wiki.
Some beautified JS in the unreleased D3 used in the code example reveals this function:
lo.geo.pipeline = function() {
var n = [];
return {
source: function() {
return n[0] = arguments, this
},
pipe: function() {
return n.push(arguments), this
},
sink: function() {
for (var t, e = arguments[0].apply(null, [].slice.call(arguments, 1)), r = e; t = n.pop();) {
var u = [].slice.call(t, 1);
u.push(e), e = t[0].apply(null, u)
}
return function() {
return e.apply(this, arguments), r.value && r.value()
}
}
}
It also appears in these bl.ocks:
Back-facing Hemisphere
Multiple rotations
Satellite

I'm not familiar with d3.js, but I looked at its source code and found that this feature is located in branch graphics-pipeline.
For example you can find related code here: https://github.com/mbostock/d3/commit/a3f2adab7f85e2a0c82288ead88c1e484c9e3ea3
Small code snippet to illustrate how it works:
var pipeline = function () {
var pipes = [];
return {
source: function () {
pipes[0] = arguments;
return this;
},
pipe: function () {
pipes.push(arguments);
return this;
},
sink: function () {
var sink = arguments[0].apply(null, [].slice.call(arguments, 1)),
pipe;
while (pipe = pipes.pop()) {
var args = [].slice.call(pipe, 1);
args.push(sink);
sink = pipe[0].apply(null, args);
}
return sink;
}
};
};
var log = document.getElementById('log');
function f() {
var argsAsString = Array.prototype.join.call(arguments, ', ');
var resultName = 'r' + f.callCounter++;
log.innerHTML += resultName + ' = f(' + argsAsString + ')<br>';
return resultName;
}
f.callCounter = 1;
pipeline().
source(f, 'a', 1).
pipe(f, 'b', 2).
pipe(f, 'c', 3).
sink(f, 'd', 4);
<div id="log"></div>
Few comments about this feature:
Methods source and pipe work with the same private property pipes. The difference only is that source set initial value for pipes (pipes[0]), when each call to pipe pushes new pipe into collection.
Previous fact gives us knowledge about d3.geo.jsonSource internal structure. It should be similar to arguments passed to pipe: first argument is something callable (function), rest of arguments - parameters.
Assume that arguments = [f, a, b, c]. Then JavaScript pattern arguments[0].apply(null, [].slice.call(arguments, 1)) means: f(a, b, c). You can see several places of its usage in sink implementation.
Regarding practical usage.
We can use it if we need to "chain" (or "pipe") data processing. For example if we have such code:
function f(a, b, previousResult)
{
return a * b + (previousResult || 0);
}
var p = pipeline().
source(f, 1, 1).
pipe(f, 2, 10).
pipe(f, 3, 100).
sink(f, 4, 1000);
Then the result (value of p) will be 4321.
In this particular case we need to clarify what is d3.geo.jsonSink and d3.geo.jsonSource, but I hope that I helped you to see the meaning of pipeline function.

Related

How to copy a formula from "Parent Tab" to "Child Tab" on Google Sheet

I'm doing RolePlay Character Sheets on a "Parent tab" I've called "MODEL", where I masterize my formulas.
I've created a second tab "Character1" and a third one "Character2". But when I try to use =QUERY or =TEXTFORMULA or whatever. It doesn't make the formulas to calculate on the actual spreadsheet, it just get the data from the "MODEL" tab.
My only way is actually to copy/past all my formulas, but if I do a mistake, I'll have to correct it in every spreadsheet every time.
Is that possible to have a formula which take the cell at:
MODELE!AE58
And automatically generate the same formulas in every tabs:
CHARACTER1!AE58
CHARACTER2!AE58
etc...
Sorry if its blur, I'm doing my best to explain.
simple
Try
function onEdit(e) {
var sh = e.source.getActiveSheet()
var rng = e.source.getActiveRange()
if (rng.getFormula() != '' && sh.getName() == 'MODEL') {
var excl = ['MODEL', 'OTHER'];//excluded sheets
SpreadsheetApp.getActiveSpreadsheet().getSheets().forEach(sh => {
if (!~excl.indexOf(sh.getSheetName())) {
sh.getRange(rng.getA1Notation()).setFormula(rng.getFormula())
}
})
}
}
when you change a formula in MODEL, this will also change in other tabs excepts excluded ones
multiple
If you edit the formulas by dragging them into the MODEL sheet, use this one which allows you to edit all the formulas at once
function onEdit(e) {
var sh = e.source.getActiveSheet()
if (sh.getName() != 'MODEL') return;
for (var i = e.range.rowStart; i <= e.range.rowEnd; i++) {
for (var j = e.range.columnStart; j <= e.range.columnEnd; j++) {
if (sh.getRange(i, j).getFormula() != '') {
var excl = ['MODEL', 'OTHER'];//excluded sheets
SpreadsheetApp.getActiveSpreadsheet().getSheets().forEach(child => {
if (!~excl.indexOf(child.getSheetName())) {
child.getRange(sh.getRange(i, j).getA1Notation()).setFormula(sh.getRange(i, j).getFormula())
}
})
}
}
}
}
global
Il you need to reset all formulas, enable google sheets api and try
function onOpen() {
SpreadsheetApp.getUi().createMenu('⇩ M E N U ⇩')
.addItem('👉 Apply all formulas from MODEL to all tabs', 'spreadFormulas')
.addToUi();
}
function spreadFormulas() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('MODEL')
if (sh.getName() != 'MODEL') return;
var data = [];
var formulas = sh.getRange(1, 1, sh.getLastRow(), sh.getLastColumn()).getFormulas()
for (var i = 0; i < formulas.length; i++) {
for (var j = 0; j < formulas[0].length; j++) {
if (formulas[i][j] != '') {
var excl = ['MODEL', 'OTHER'];//excluded sheets
SpreadsheetApp.getActiveSpreadsheet().getSheets().forEach(child => {
if (!~excl.indexOf(child.getSheetName())) {
data.push({
range: `${child.getName()}!${columnToLetter(+j + 1) + (+i + 1)}`,
values: [[`${formulas[i][j]}`]],
})
}
})
}
}
}
if (data.length) {
var resource = {
valueInputOption: 'USER_ENTERED',
data: data,
};
try { Sheets.Spreadsheets.Values.batchUpdate(resource, ss.getId()); } catch (e) { console.log(JSON.stringify(e)) }
}
}
function columnToLetter(column) {
var temp, letter = '';
while (column > 0) {
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
if your sheet is called MODELE try on some other sheet just:
=MODELE!AE58
for array it would be:
={MODELE!AE58:AE100}
also take a look into "Named Ranges" - maybe you will find it more handy

model.fit() never ends or shows me the loss

I'm trying to train a model and never pass the fit().
In the console doesn't show the loss result, it gets stuck there.
Already changed the async to a promise, but it's the same.
To see the entire code, click here!
function train() {
trainModel().then(result => {
console.log(result.history.loss[0]);
setTimeout(train, 100);
});
}
// entrena modelo~ params = train_xs(input) y train_ys(output)
async function trainModel() {
//Create the input data
for (let i = 0; i < 5; i++) {
train_xs = tf.tensor2d(ins.pixels[i], [28, 28], 'int32');
train_ys = tf.tensor2d(outs.coords[i], [3, 2], 'int32');
const h = await model.fit(train_xs, train_ys, {
epochs: 1
});
console.log("Loss after Epoch " + i + " : " + h.history.loss[0]);
}
console.log('end fitness model');
}
//never shows end fitness model
no error messages, the console keeps just clean
There are a couple of issues (the console is clean because it was not logging out the errors):
the shape of xs and ys does not match the input and output of the model.ins.pixels[i]
xs and ys should have the same batch size. Since in all iteration of the for loop, only one feature and one label is used, therefore the batchsize is 1.
Here is a fix of the model
let model;
let xs;
let train_xs;
let train_ys;
let inAndOut;
let resolution = 20;
let cols;
let rows;
var ins;
var outs;
function setup() {
createCanvas(400, 400);
/// visualization
ins = new Inputs13(); // ins.pixels;
outs = new Outputs13(); // outs.coords;
inAndOut = new InputsAndOutputsToTest();
///crear modelo
model = tf.sequential();
let hidden = tf.layers.dense({
inputShape: [784],
units: 28,
activation: 'sigmoid'
});
let output = tf.layers.dense({
units: 6,
activation: 'sigmoid'
});
model.add(hidden);
model.add(output);
const optimizer = tf.train.adam(0.1);
model.compile({
optimizer: optimizer,
loss: 'meanSquaredError'
})
xs = tf.tensor2d(inAndOut.pixelsToTest[0],[28,28]);
//console.log('xs');
//console.log(xs);
//xs.print();
//entrena modelo
setTimeout(train, 10);
}
//promesa, llama a entrenar modelo y muestra de losss
function train() {
console.log("im in train!");
trainModel().then(result => {
console.log(result.history.loss[0]);
setTimeout(train, 100);
});
}
// entrena modelo~ params = train_xs(input) y train_ys(output)
async function trainModel() {
let h;
//Create the input data
for (let i = 0; i < 5; i++) {
train_xs = tf.tensor(ins.pixels[i], [1, 784]); //[tens], [shape]
console.log('xs.shape', train_xs.shape)
train_ys = tf.tensor(outs.coords[i]).reshape([1, 6]);
console.log('ys.shape', train_ys.shape)
/* console.log('train_xs');
train_xs.print();
console.log("train_ys");
train_ys.print();*/
h = await model.fit(train_xs, train_ys, {
// shuffle: true,
epochs: 1
});
console.log("Loss after Epoch " + i + " : " + h.history.loss[0]);
}
console.log('end fitness model');
return h;
}
//muestra visual!
function draw() {
background(220);
//Get the predictions params xs = inputs para pruebas
tf.tidy(() => {
let ys = model.predict(xs);
//console.log("ys");
//console.log(ys);
let y_values = ys.dataSync();
// console.log("y_values");
// console.log(y_values);
});
}
However, it is possible to use all the 13 features and 13 labels all at once. The for loop will no longer be useful.
train_xs = tf.tensor(ins.pixels, [13, 784]);
console.log('xs.shape', train_xs.shape)
train_ys = tf.tensor(outs.coords).reshape([13, 6]);

How to capture reject on Q.all() when querying mongoose?

I finally figured out how properly to use Q.all() in my code and it works as expected, but I don't know how to detect the reject if error comes from database in my specific code. I googled a lot but the problem is that in this particular case I can't relate the information I find by google to my own problem! Now with code, I have:
function username(user) {
var deferred = Q.defer();
var queryu = User.find();
queryu.where({_id: user});
queryu.exec(function(err, results) {
if (err) { //system level error
deferred.reject(err);
} else {
var nameAndFB = extractinfo(results[0]);
deferred.resolve(nameAndFB);
}
});
return deferred.promise;
}
later, I have another method that uses this one:
function masterUserObj(user, curstate) {
var p1 = username(user);
var p2 = getState(curstate);
return Q.spread([p1, p2], function(userinfo, pairstate) {
var obj1 = {};
obj1.username = userinfo[0];
obj1.fbid = userinfo[1];
obj1.idprovider = userinfo[2];
obj1.state = pairstate;
return obj1;
});
}
finally, a for loop puts all above to use:
function exposePairs(results, res) {
var plist = [];
for (var i = 0, m = results.length; i < m; i++) {
plist[i] = masterUserObj(results[i].user, results[i].state);
}
Q.all(plist).then(function(theArr) {
return res.jsonp({pairs: theArr});
});
}
Code works, but I don't know where and how best to detect the reject case: deferred.reject(err);
Mainly I'm confused because I just learned to put promises in use together with a loop.
Please point me to the best practices.
Edit:
Also, please comment on code if I should use .done() after the final Q.all() or it is not necessary here.

Calling sort on slickgrid

In the slickgrid I'm able to set the sort column and it's sort direction using the grid.SetSortColumn(colName,true/false). This only sets the sorting glyph but does no sorting. Is there a way to call the sort event handler. I've defined the sort handler like grid.onSort.subscribe(function(){});
The behavior you are observing is correct.
grid.setSortColumn(columnId, isAsc);
only updates the glyph on the sort column.
In your case, you will initially need to sort the data, and then use setSortColumn to update the glyph on sortColumn. You can reuse sorter used in onSort event like this:
var gridSorter = function(columnField, isAsc, grid, gridData) {
var sign = isAsc ? 1 : -1;
var field = columnField
gridData.sort(function (dataRow1, dataRow2) {
var value1 = dataRow1[field], value2 = dataRow2[field];
var result = (value1 == value2) ? 0 :
((value1 > value2 ? 1 : -1)) * sign;
return result;
});
grid.invalidate();
grid.render();
}
var grid = new Slick.Grid($gridContainer, gridData, gridColumns, gridOptions);
//These 2 lines will sort you data & update glyph while loading grid
//columnField is field of column you want to sort initially, isAsc - true/false
gridSorter(columnField, isAsc, grid, gridData);
//I had the columnField, columnId same else used columnId below
grid.setSortColumn(columnField, isAsc);
grid.onSort.subscribe(function(e, args) {
gridSorter(args.sortCol.field, args.sortAsc, grid, gridData);
});
How I arrived on this solution?
Read comments here. https://github.com/mleibman/SlickGrid/issues/325
dataView.fastSort does the job. You can then use setSortColumn to set the sorting glyph.
I have multiple column sort enabled, I had to change the function to pass the correct sort column.
grid.onSort.subscribe(function(e, args) {
gridSorter(**args.sortCols[0].sortCol.field**, **args.sortCols[0].sortAsc**, grid, gridData);
});
You can trigger click event on the column header...which does sorting
I fixed the issue like this...
$('.slick-header-columns').children().eq(0).trigger('click'); // for first column
I was inspired by Mr.Hunts answer but I took a slightly different approach to extend the current grid.setSortColumn(columnId, isAsc) to grid.setInitialSortColumn(columnId, isAsc). This will apply the sort and do everything grid.setSortColumn does.
var thisGrid = { //Your grid obj
columns: , // Your columns object
grid: , // new Slick.Grid....
}
thisGrid.grid.onSort.subscribe(function (e, args) { // ar var cols = args.sortCols;]
thisGrid.grid.customSort(args);
});
thisGrid.grid.customSort = function (args) {
var cols = args.sortCols;
thisGrid.dataView.sort(function (dataRow1, dataRow2) {
if (cols) {
for (var i = 0, l = cols.length; i < l; i++) {
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field],
value2 = dataRow2[field];
var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result != 0) {
return result;
}
}
}
return 0;
});
}
thisGrid.grid.setInitialSortColumn = function (columnId, ascending) {
thisGrid.grid.setInitialSortColumns([{
columnId: columnId,
sortAsc: ascending
}
]);
};
thisGrid.grid.setInitialSortColumns = function (cols) {
sortColumns = cols;
$.each(sortColumns, function (i, col) {
var columnIndex = thisGrid.grid.getColumnIndex(col.columnId);
var column = thisGrid.columns[columnIndex];
if (col.sortAsc == null) {
col.sortAsc = true;
}
var args = {
grid: thisGrid.grid,
multiColumnSort: true,
sortCols: [{
sortCol: column,
sortAsc: col.sortAsc
}
]
}
thisGrid.grid.setSortColumn(col.columnId, col.sortAsc);
thisGrid.grid.customSort(args);
});
};
// Trigger
thisGrid.grid.setInitialSortColumn("dateDue", true);
I can't leave comments due to reputation, which is where this would be most appropriate, however, my answer is in regard to #Premshankar Tiwari and #Siddharth answers.
I preferred the dataView.fastSort option in Siddharth's answer, which works for me in all browsers except IE7 and 8. I didn't test it in IE9 or above. Unfortunately, most on my network run IE7 or 8 due to compatibility issues for legacy applications. BUT, Premshankar's answer works in IE7 and 8.
So, I ended up doing something like this:
if (msie > 0) {
$(".slick-header-columns").children().eq(5).trigger("click");
$(".slick-header-columns").children().eq(4).trigger("click");
} else {
dataView.fastSort('process','unit');
}
where column index (5) = 'unit' and column index (4) = 'process'. Notice that is the reverse order in dataView.fastSort method. I am also using a function that detects IE browser version and assigns it to msie.
My only complaint about utilizing the .trigger method is that if you set up your grid to dynamically hide/show columns, the indexed feature would potentially sort on unintended columns unless you are only calling it on initialization when hide/show capabilities are present.
Maybe it will help you. Looks like SlickGrid is triggering sort to self - so You can trigger it manually if You want.
I'm using multicolumn sorting, and loading saved sort data when initialising the grid.
As expected, setSortColumns set the sorting, but didnt actually apply it, and dataView.reSort() or .fastSort() didnt seem to help, regardless of what point in loading I called them
(I must have missed something, but just couldnt get it to work).
In the end, this worked for me. I call it immediately after populating my dataView from an ajax call.
Its probably not the slickest, so happy to take feedback on board!
function forceResort() {
var sortColumns = grid.getSortColumns();
var cols = [];
$.each(sortColumns, function(index, value) {
var columnId = value.columnId;
var sortAsc = value.sortAsc;
var sortCol = { field: columnId };
var col = { sortCol: sortCol, sortAsc : sortAsc};
cols.push(col);
});
dataView.sort(function (dataRow1, dataRow2) {
var sortResult = 0;
for (var i = 0, l = cols.length; i < l; i++) {
if (sortResult !== 0) {
break;
}
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field] || ''; //handle nulls - otherwise erratic sorting
var value2 = dataRow2[field] || ''; //handle nulls - otherwise erratic sorting
if ($.inArray(field, dateTypeColumns) > -1) {
sortResult = compareDates(value1, value2) * sign;
} else {
if ($.inArray(field, numericColumns) > -1) {
sortResult = compareSimple(value1, value2) * sign;
} else {
sortResult = compareAlphaNumeric(value1, value2) * sign;
}
}
}
return sortResult;
});
grid.invalidate();
grid.render();
}
A more clean solution is not to rely on the arguments to onSort but call getSortColumns instead:
function gridSorter() {
var scol=grid.getSortColumns();
if (scol.length===0) return;
var scolId=scol[0].columnId, asc=scol[0].sortAsc;
data.sort(function(a, b) {
var result = a[scolId] > b[scolId] ? 1 : a[scolId] < b[scolId] ? -1 : 0;
return asc ? result : -result;
});
grid.invalidate();
}
Then do:
grid.onSort.subscribe(gridSorter);
This will allow to reestablish sorting anytime you want (from example after reloading the data with ajax) simply by calling gridSorter()
If you want multiple column sorting:
function grid_sorter(args, grid, dataView) {
let cols = args.sortCols;
console.log(cols)
dataView.sort(function (dataRow1, dataRow2) {
for (let i = 0, l = cols.length; i < l; i++) {
let field = cols[i].sortCol.field;
let sign = cols[i].sortAsc ? 1 : -1;
let value1 = dataRow1[field], value2 = dataRow2[field];
let result = (value1 === value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result !== 0) {
return result;
}
}
return 0;
});
grid.invalidate();
grid.render();
}
grid_sorter(default_sorting, grid_2, dataView_2);
cols is an object like this:
- sortCols {
- length: 2
- 0 : {
"sortAsc: true,
"sortCol": {
"field: column_id
}
}
- 1: {..}
}

How can I stringify a BSON object inside of a MongoDB map function?

I have documents with field xyz containing
{ term: "puppies", page: { skip: 1, per_page: 20 } } // not useful as a composite key...
{ page: { skip: 1, per_page: 20 }, term: "puppies" } // different order, same contents
For the sake of determining the "top" values in xyz, I want to map them all to something like
emit('term="puppies",page={ skip: 1, per_page: 20 }', 1); // composite key
but I can't get the embedded objects into a meaningful strings:
emit('term="puppies",page=[object bson_object]', 1); // not useful
Any suggestions for a function to use instead of toString()?
# return the top <num> values of <field> based on a query <selector>
#
# example: top(10, :xyz, {}, {})
def top(num, field, selector, opts = {})
m = ::BSON::Code.new <<-EOS
function() {
var keys = [];
for (var key in this.#{field}) {
keys.push(key);
}
keys.sort ();
var sortedKeyValuePairs = [];
for (i in keys) {
var key = keys[i];
var value = this.#{field}[key];
if (value.constructor.name == 'String') {
var stringifiedValue = value;
} else if (value.constructor.name == 'bson_object') {
// this just says "[object bson_object]" which is not useful
var stringifiedValue = value.toString();
} else {
var stringifiedValue = value.toString();
}
sortedKeyValuePairs.push([key, stringifiedValue].join('='));
}
// hopefully we'll end up with something like
// emit("term=puppies,page={skip:1, per_page:20}")
// instead of
// emit("term=puppies,page=[object bson_object]")
emit(sortedKeyValuePairs.join(','), 1);
}
EOS
r = ::BSON::Code.new <<-EOS
function(k, vals) {
var sum=0;
for (var i in vals) sum += vals[i];
return sum;
}
EOS
docs = []
collection.map_reduce(m, r, opts.merge(:query => selector)).find({}, :limit => num, :sort => [['value', ::Mongo::DESCENDING]]).each do |doc|
docs.push doc
end
docs
end
Given that MongoDB uses SpiderMonkey as its internal JS engine, can't you use JSON.stringify (will work even if/when MongoDB switches to V8) or SpiderMonkey's non-standard toSource method?
(sorry, can't try it ATM to confirm it'd work)
toSource method will do the work, but it adds also brackets.
for a clean document use:
value.toSource().substring(1, value.toSource().length - 1)

Resources