28 votes

Requête Crossfilter

Est-il possible de filtrer un crossfilter jeu de données qui a un tableau comme valeur?

Par exemple, dire que j'ai le dataset suivant:

var data = [
  {
    bookname: "the joy of clojure",
    authors: ["Michael Fogus", "Chris Houser"],
    tags: ["clojure", "lisp"]
  },
  {
    bookname: "Eloquent Ruby",
    authors: ["Russ Olsen"],
    tags: ["ruby"]
  },
  {
    bookname: "Design Patterns in Ruby",
    authors: ["Russ Olsen"],
    tags: ["design patterns", "ruby"]
  }
];

Est-il un moyen facile d'accéder à des livres qui sont marqués par une étiquette spécifique? Et aussi les livres qui ont un auteur particulier? La façon dont je le comprends comment utiliser crossfilter jusqu'à présent, a moi de faire quelque chose comme ceci:

var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});
var tag = tags.group();

Et puis quand j'ai accès au groupement (comme):

tag.all()

J'obtiens ceci:

[{key: ["clojure", "lisp"], value: 1}, 
 {key: ["design patterns", "ruby"], value: 1}, 
 {key: ["ruby"], value: 1}]

Quand je préférerais avoir ceci:

[{key: "ruby", value: 2}, 
 {key: "clojure", value: 1}, 
 {key: "lisp", value: 1},
 {key: "design patterns", value: 1}]

31voto

numan salati Points 4684

J'ai ajouté des commentaires au code ci-dessous. Vue d'ensemble: utilisez la fonction de réduction.

 var data = ...
var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});

tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial).value()
 

Remarquez comment j'ai utilisé groupAll () au lieu de group () b / c, nous voulons que nos fonctions de réduction (définies ci-dessous) fonctionnent sur un groupe plutôt que sur 3 groupes.

Maintenant, les fonctions de réduction devraient ressembler à ceci:

 /*
 v is the row in the dataset

 p is {} for the first execution (passed from reduceInitial). 
 For every subsequent execution it is the value returned from reduceAdd of the prev row
*/
function reduceAdd(p, v) {
  v.tags.forEach (function(val, idx) {
     p[val] = (p[val] || 0) + 1; //increment counts
  });
  return p;
}

function reduceRemove(p, v) {
   //omitted. not useful for demonstration
}

function reduceInitial() {
  /* this is how our reduce function is seeded. similar to how inject or fold 
   works in functional languages. this map will contain the final counts 
   by the time we are done reducing our entire data set.*/
  return {};  
}
 

1voto

ZachB Points 1322

Je n'ai jamais utilisé de "crossfilter" (je suppose que c'est une bibliothèque JS). Voici cependant quelques méthodes JS pures.

Cette...

 data.filter(function(d) {
  return d.authors.indexOf("Michael Fogus") !== -1;
})
 

renvoie ceci:

 [{bookname:"the joy of clojure", authors:["Michael Fogus", "Chris Houser"], tags:["clojure", "lisp"]}]
 

Cette...

 var res = {};
data.forEach(function(d) {
  d.tags.forEach(function(tag) {
    res.hasOwnProperty(tag) ? res[tag]++ : res[tag] = 1
  });
})
 

renvoie ceci:

 ({clojure:1, lisp:1, ruby:2, 'design patterns':1})
 

Pour l'un ou l'autre, vous pouvez appliquer d3.entries pour obtenir votre format {key:"ruby", value: 2} .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X