Git Product home page Git Product logo

d4's Introduction

D4

D4 is a friendly charting DSL for D3. The goal of D4 is to allow developers to quickly build data-driven charts with little knowledge of the internals of D3.

Quick Start


For the bleeding edge version of d4 download it directly from the github repository. If you prefer a more stable release you can install the latest released tag using a package manager like bower.

$ bower install d4
or
$ npm install d4

Once you have a local copy of d4 simply include it after d3 in your source file.

<!DOCTYPE html>
<html>
<head>
  <!-- sensible defaults for styles -->
  <link href="d4.css" rel="stylesheet" />
</head>
<body>
  ...
<script src="d3.js"></script>
<script src="d4.js"></script>
</body>
</html>
Hello World

Here is the most basic example, which uses many of the preset defaults provided by d4.

var data = [
  { x : '2010', y : 5 },
  { x : '2011', y : 15 },
  { x : '2012', y : 20 }
];
var columnChart = d4.charts.column();

d3.select('someDomElement')
  .datum(data)
  .call(columnChart);
Getting Fancy

d4 allows you to quickly build up sophisticated charts using a declarative and highly contextual API that allows you to mixin or mixout features from your chart.

var data = [
  { x : '2010', y : 5 },
  { x : '2011', y : 15 },
  { x : '2012', y : 20 }
];

// Create a column chart without a yAxis, but with a grid in the background.
var columnChart = d4.charts.column()
.mixout('yAxis')
.mixin({ 'name' : 'grid', 'feature' : d4.features.grid, 'index' : 0 })

d3.select('someDomElement')
  .datum(data)
  .call(columnChart);
Additional Examples

There are many more examples of d4 in the examples site inside the source code repository. Simply clone the repo and open the examples/ folder in your favorite web browser.

You can find a hosted version of the example site here: http://visible.io/

You can find a quick-start presentation on d4 here.

Philosophy


Many charting libraries do a poor job when it comes to separations of concerns. They attempt to be an all-in-one tool, which is at odds with how modern applications are built. Developers do not want a monolith that owns the data transformation, visual aesthetics, and interactivity. This leads to enormous libraries with huge config files, where every minutia about the chart must be decided upon beforehand. This typically means developers must first learn a specialized API in order to control even the most basic aspects of the chart. d4 believes many of these responsibilities would be better delegated to other technologies. If developers were woodworkers then d4 would be a jig, which allows complex cuts to be made in fraction of the time it would normally take.

CSS is for styling

Many charting libraries make internal decisions on visual aesthetics, which may remove control from the graphic designer, who may or may not understand JavaScript let alone a specialized charting API. Choices on visual design like the colors for data series and font sizes are best made in CSS. d4 exposes convenient hooks in the generated markup to allow visual designer to get precise control over the look and feel without needing deep knowledge of d4.

The chart does not own the data

Data is a stand-alone object, which can be relied upon by many other scripts on the page. Therefore, a charting library should not change the data object. It can make non-permanent transformations.

Context over configuration

There is a software design concept called "convention over configuration," which states that software should specify a collection of opinionated defaults for the developer. The goal of this approach is to lessen the number of obvious choices a developer must make before they are able to use the software. Instead, configuration should be saved for instances where the defaults do not apply. d4 extends this concept a bit and suggests that configuration should also be highly contextual to the object the developer needs changed. Instead of making choices in some abstract config file, developers instead use a highly declarative API to make changes directly to the object they want augment.

Terminology


d4 uses specific terms to describe the components of a chart.

Chart - The data rendered by d3 into a graphical representation.

Feature - A visual component of a chart, which helps convey meaning in the data.

Dimension - A segment of the data described by the chart.

Parser - A parser prepares the data for a chart.

Base Charts

Chart Features (Mixins)

Contributing

If you make improvements to d4, please share with others.

Fork the project on GitHub.

Make your feature addition or bug fix.

Commit with Git.

Send @heavysixer a pull request.

Inspiration

Where possible d4 follows existing d3 community best-practices. The inspiration of D4's modular and declarative structure came from Mike Bostock's article on writing reusable charts in d3. d4 also follows the general update pattern too. (mostly)

Other Projects using d4

d4-rails

d4's People

Contributors

bryant1410 avatar gouravtiwari avatar heavysixer avatar jefffriesen avatar nsonnad avatar slashdotdash avatar yanofsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d4's Issues

Order of execution of features.

There seems to be a disconnect between when the order of execution of the rendering cycle.

For example consider this example where bars is rendered before the labels:

var total = 0;
chart.using('bars', function(bars){
    bars.x(function(){
        total+=1;
    });
})
.using('labels', function(labels){
    labels.x(function(){
          // => 0 instead of 1;
          console.log(total);
    });
});

Throws reference error when using Browserify (assuming will also throw error with other bundlers?)

Looks like d4 variable isn't available inside subsequent closures after the first when compiling with Browserify. As a quick-fix, I declared d4 variable outside all closures. Thoughts?

var d4;

(function() {
  'use strict';

  var root = this;
  var breaker = {};

  // Create a safe reference to the d4 object.
  d4 = function(obj) {
    if (obj instanceof d4) {
      return obj;
    }
    if (!(this instanceof d4)) {
      return new d4(obj);
    }
    this.d4Wrapped = obj;
  };
...

BarLabels for Column Charts

Is there a way to change the figure displayed on the bars of a column chart? E.g. if my data object looks like:

var dataObj = [
{ x : '2010', y : 5 },
{ x : '2011', y : 15 },
{ x : '2012', y : 20 },
{ x : '2013', y : 0 },
{ x : '2014', y : 0 },
];

Then instead of showing "0" for x = 2013/2014 I want to show "-" on top. Possible?

Legend

A lot of Chart's libraries have Legend's. D4 is very light and tricky, but may be it's a good idea to have a feature with a legend that can be mixed in, when it needed?
screen shot 2014-05-12 at 8 42 40 pm

API or documentation for scale nicing

The fix to #32 disabled auto-nicing. The (undocumented) way of nicing scales I found is:

chart.using('lineSeries', function(line) {
  line.beforeRender(function(chart) {
    this.y.nice();
  });
});

I'd appreciate if this could be done declaratively, like setting the Key for the Axis.
Should I take the time to implement this and send a pull request, or should I just add the workaround to the documentation?

Add Padding method make distinct from Margin

apples-oranges_chartbuilder 77

We use margin to define the space between the edge of the chart and the next closest item: the credit line the legend, the axis etc.

We use padding to define the space between the margin and the chart area.

We also have a method of extraPadding so that we can add or subtract from the default padding without modifying it.

Better series indexing

When you use beforeRender to filter data and plot different features in the same chart, the classes applied to the series containers are linked to the index in relation to the filtered data not in the original data.

For instance both the bars and the line are classed series0 in this http://visible.io/charts/column/multi-dimension.html

We've been binding an index to the data and accessing it in the afterRender which is less than ideal

line.afterRender(function() {
    this.container.selectAll(".lines g.line")
        .each(function(d,i) {
            d3.select(this).classed("mixed-series-" + d.index, true);
        });
})

wrong order punch-card data result wrong graph for punch-card example

if the order in punch-card data is not order by week,hour ,the graph is wrong.
example data:

[[0,0,0],[0,1,0],[0,2,0],[0,3,0],[0,4,0],[0,5,0],[0,6,0],[1,0,0],[1,1,0],[1,2,0],[1,3,0],[1,4,0],[1,5,0],[1,6,0],[2,0,0],[2,1,0],[2,2,0],[2,3,1],[2,4,0],[2,5,0],[2,6,0],[3,0,0],[3,1,0],[3,2,0],[3,3,3],[3,4,1],[3,5,0],[3,6,0],[4,0,0],[4,1,1],[4,2,0],[4,3,1],[4,4,0],[4,5,0],[4,6,0],[5,0,0],[5,1,0],[5,2,0],[5,3,0],[5,4,0],[5,5,0],[5,6,2],[6,0,0],[6,1,2],[6,2,0],[6,3,0],[6,4,0],[6,5,0],[6,6,0],[7,0,0],[7,1,2],[7,2,0],[7,3,0],[7,4,0],[7,5,0],[7,6,1],[8,0,0],[8,1,1],[8,2,0],[8,3,1],[8,4,0],[8,5,0],[8,6,0],[9,0,0],[9,1,6],[9,2,0],[9,3,1],[9,4,0],[9,5,1],[9,6,0],[10,0,0],[10,1,1],[10,2,0],[10,3,0],[10,4,0],[10,5,0],[10,6,0],[11,0,0],[11,1,0],[11,2,0],[11,3,0],[11,4,0],[11,5,4],[11,6,0],[12,0,0],[12,1,0],[12,2,0],[12,3,0],[12,4,0],[12,5,0],[12,6,0],[13,0,0],[13,1,0],[13,2,0],[13,3,0],[13,4,0],[13,5,0],[13,6,0],[14,0,0],[14,1,0],[14,2,0],[14,3,1],[14,4,0],[14,5,0],[14,6,0],[15,0,0],[15,1,0],[15,2,0],[15,3,0],[15,4,0],[15,5,0],[15,6,0],[16,0,0],[16,1,0],[16,2,0],[16,3,0],[16,4,0],[16,5,0],[16,6,0],[17,0,0],[17,1,0],[17,2,0],[17,3,0],[17,4,0],[17,5,1],[17,6,0],[18,0,0],[18,1,0],[18,2,0],[18,3,0],[18,4,0],[18,5,0],[18,6,0],[19,0,0],[19,1,1],[19,2,0],[19,3,0],[19,4,1],[19,5,0],[19,6,1],[20,0,0],[20,1,0],[20,2,1],[20,3,0],[20,4,0],[20,5,0],[20,6,1],[21,0,0],[21,1,0],[21,2,0],[21,3,0],[21,4,1],[21,5,0],[21,6,0],[22,0,0],[22,1,0],[22,2,0],[22,3,0],[22,4,0],[22,5,0],[22,6,0],[23,0,0],[23,1,0],[23,2,0],[23,3,0],[23,4,0],[23,5,0],[23,6,0]]

Axis ticks dash's

Is it planned to create an axis ticks (like in Excel).'Corse sometimes it's hard to see what tic is complemented to what bar.
screen shot 2014-05-12 at 8 23 27 pm

Tooltips on Column Chart bars

Hey,

Tried using this for getting tooltips on the column bars but didnt work. How should I proceed?

d4.charts.column()
,mixout('yAxis')
.using('bars', function(bar) {
// Note: d4 proxies the "on" function to d3, so it will work just like
// it does in d3
bar.on('mouseover', function(d) {
var title = 'X: ' + d.x + '
' + 'Y: ' + d.y;
$(this).tooltip({
container: 'body',
placement: 'auto',
html: true,
title: title
});
});

I already have the bootstrap lib included.

Transitions broken on the donut chart example

@nsonnad just noticed that the refactoring you did to the arc series seems to have broken the transitions between pie slices. If you notice the donut chart example now snaps to the next location where it used to animate nicely between states. This is a minor defect but I worry that it is a symptom of a larger problem. I've not looked at the issue yet it might be minor, I just thought i'd run it by you first.

http://visible.io/charts/donut/basic.html

Please note that the text labels still nicely transition between states.

Dual-axis capability on a basic chart

I've been working on trying to figure out the best way to allow for a left axis and a right axis on different scales. (and thus series on different scales) Like this:

apples-oranges_chartbuilder 76

At the moment d4 provides no way to override the chart-wide scale on a feature by feature basis.

So far I've tried the following,

  • add alt_scale to the yAxis feature's accessors with a default of null
  • change the first line of yAxis.render from scope.scale(this.y) to scope.scale(scope.alt_scale() ? scope.alt_scale() : this.y);
    • in other words: "if there is a custom scale, use that, otherwise use the default"

but there is no way to access a custom scale (that I can see) from inside of a feature (such as lineSeries) since the accessor is called in the context of the chart not the context of the feature. (e.g. d4.functor(scope.accessors.y).bind(this))

If you want to keep this functionality, perhaps it's better adding alt_y on the chart object instead—a clone of chart.y. Then there can be alt_y accessors on each feature and logic on when to use it in the render based on a use_alt_y accessor boolean on the feature. A similar thing could also be added to the x methods.

Of course once there are two axes...why not three, four, etc? That type of functionality would require the chart.x and chart.y methods to return arrays of scales and features to have something like y_scale_index (defaulting to 0). It could also really mess up the API.

What are your thoughts on how to achieve this @heavysixer?

Support SVG filters on chart elements

Something like this is the proposed API:

// create a chart
var columnChart = d4.baseChart()

// mix in a feature to the chart
.mixin([{name: 'bars', feature: d4.features.stacked-shapes-series}])

// when using the feature apply a filter to the rects.
.using('bars',function(bar){
      bar.svgFilter('feGaussianBlur', function(filter){
            filter
            .in("SourceGraphic")
            .stdDeviation("5");
      });
 });

Thoughts?

Live updates

I am creating something like chart's constructor using d4. And now i need a lot of live updates features. Am i need to recreate all chart with d3, each time when i change something, or play with native css\svg data? May be it's possible to make some setter's like in https://github.com/curran/model ? It could be very useful for dynamic charts.
What do you think?

Thanks.

d4.js:1172 Uncaught ReferenceError: d4 is not defined

Trying to use d4 with reactjs::

Getting: d4.js:1172 Uncaught ReferenceError: d4 is not defined


import React from 'react';
import ReactDOM from 'react-dom';
import ReactFauxDOM from 'react-faux-dom';

import d3 from 'd3';
import d4 from 'd4';

class App extends React.Component {

render () {
const data =
[
{x: "For Investment", y: 13.27},
{x: "For Distribution", y: 8.66},
{x: "Outstanding Principal", y: 5.22},
{x: "Accrued Interest", y: 5}
];

const myChart = d4.charts.column().width(500)

const d = d3.select(ReactFauxDOM.createElement('div'))
.datum(data).call(myChart);

return d.node().toReact()

}
}
export default App;

License Missing

Hi Mark -

Would it be possible for you to add an appropriate license to D4 here on Github?

Thanks!

Grid offset

If i create a grid for bar chat, the grid line goes throw the group. But i want to set it between the lines. How could it be done in a more elegant way?
screen shot 2014-05-12 at 8 20 56 pm

Transitions on stacked column charts

It looks like you've got transitions built in based on this example:
http://visible.io/charts/donut/basic.html

I tried to recreate this with the stacked column charts with no luck. Not only is the transition instant, the mixins don't follow.

What approach would I take to make this transition nicely as in the donut chart example?

'use strict';

$(document).ready(function(){

  var generateData = function() {
    var data = [
      { year: '2010', unitsSold: 0, salesman : 'Bob' },
      { year: '2011', unitsSold: 0, salesman : 'Bob' },
      { year: '2012', unitsSold: 0, salesman : 'Bob' },
      { year: '2013', unitsSold: 0, salesman : 'Bob' },
      { year: '2014', unitsSold: 0, salesman : 'Bob' },
      { year: '2010', unitsSold: 0, salesman : 'Gina' },
      { year: '2011', unitsSold: 0, salesman : 'Gina' },
      { year: '2012', unitsSold: 0, salesman : 'Gina' },
      { year: '2013', unitsSold: 0, salesman : 'Gina' },
      { year: '2014', unitsSold: 0, salesman : 'Gina' },
      { year: '2010', unitsSold: 0, salesman : 'Average' },
      { year: '2011', unitsSold: 0, salesman : 'Average' },
      { year: '2012', unitsSold: 0, salesman : 'Average' },
      { year: '2013', unitsSold: 0, salesman : 'Average' },
      { year: '2014', unitsSold: 0, salesman : 'Average' }
    ]
    _.each(data, function(obj){
      obj.unitsSold = _.random(-500, 500)
    })
    return data;
  }

  var chart = d4.charts.stackedColumn()
                .outerWidth($('#example').width())
                .x(function(x){ return x.key('year') })
                .y(function(y){ return y.key('unitsSold') })
                .mixin({
                  'name' : 'zeroLine',
                  'feature' : d4.features.referenceLine
                })
                .using('zeroLine', function(zero) {
                  zero
                    .x1(0)
                    .x2(function(){ return this.width })
                    .y1(function(){ return this.y(0) })
                    .y2(function(){ return this.y(0) });
                });
  var redraw = function() {
    var data = generateData()
    var parsedData = d4.parsers.nestedStack()
      .x('year')
      .y('salesman')
      .value('unitsSold')(data);

    d3.select('#example')
      .datum(parsedData.data)
      .call(chart);
  };

  (function loop() {
    redraw();
    setTimeout(loop, 3000);
  })();

});

(this screenshot taken after several cycles)
screen shot 2014-05-16 at 8 18 53 am

(btw, I love the approach d4 is taking. Really excited to have found this)

Refactor as node module

Hello again! Implementation-wise this is not too different from #14, but I'm starting to realize that the implications reach far beyond browserify. One amazing and under-used feature of d3 is its ability to run on node, which means you can pre-render charts on the server. Would be excellent if d4 could do the same.

I don't think there's anything major blocking it except code structure and the dependency on a browser global, as discussed in the other issue, as d3 is fully node-compatible. Thoughts @heavysixer ?

when margin is changed outerWidth and outerHeight need to be recalculated

in this example outerWidth will return the wrong value because it applies margins which are then removed in the next step. Since outerWidth and outerHeight are dependent on the margin value we need to recalculate them once margin is updated.

var chart = d4.charts.donut()
    .outerWidth($('#pie').width())
    .margin({
      left: 0,
      top: 0,
      right: 0,
      bottom: 0
   })

Missing bar in simple collumn graph

Hello. I'm using d4 and my graph missing one bar:
example

Still it's present in data model:
data model

Code:

for(var i = window.data.length - 1; i >= 0; i --){
    graphData.push({
        x: i + 1,
        y: window.data[i].count
    });
}

var columnChart = d4.charts.column().outerWidth(900);
d3.select('#graph').datum(graphData).call(columnChart);

Any help is appreciated ;)

Cannot read property 'ordinal' of undefined

When I try to run the sample script on my machine. I get this error:
d4.js:95 Uncaught Error: [d4] The scale type: "ordinal" is unrecognized. D4 only supports these scale types: time, time.utc
When I log supportedScales in line 175 (function validateScale); d3.scale is undefined.

I load d3.js first, then d4.js.

Any idea why this happens?

Publish on npm

We've completely moved our client-side dependencies from Bower to npm and use Browserify for dependency management. Browserify allows you to include npm modules on the client. It's great.

There are ways to include Bower modules within Browserify. Debowerify works sometimes but can cause conflicts depending on what other transforms you have.

For reference for others coming here, this is a way to get d4 workings within Browserify:

package.json:
  "browser": {
    "bootstrap": "./src/javascript/vendor/bootstrap.js",
    "d4": "./src/javascript/vendor/d4.js",
  },
  "browserify": {
    "transform": [
      "browserify-shim",
      "coffeeify",
    ]
  },
  "browserify-shim": {
    "bootstrap": {
      "exports": "bootstrap",
      "depends": [
        "jquery:$"
      ]
    },
    "d4": {
      "exports": "d4",
      "depends": [
        "d3:d3"
      ]
    },

Then you can require it in your modules:

d4 = require('d4')

But it's really nice to just be able to do this and be done:

npm install d4 --save

Ordinal rangePoints are ignored

Ordinal rangePoints are ignored when being applied in this manner:

.x(function(x){
    x
    .domain(data)
    .rangePoints([0, chart.width()],1);
    // this produces the correct range but is reverted when the chart is rendered.
    console.log(x.rangePoints([0, chart.width()],1).range())
  })

More than likely this is also a problem with other scale setters as well.
You can see an example of the reverted scale range using this simple feature:

  d4.feature('pathSeries', function(name) {
    return {
      accessors: {
        classes: function(){
          return 'path';
        }
      },
      render: function(scope, data, selection) {
        selection.append('g').attr('class', name);
        var group = selection.select('.' + name).selectAll('g').data(data);
        group.exit().remove();
        group.enter().append('g');
        var path = group.selectAll('path')
        .data(function(d){
          return d.values;
        });
        path.enter().append('path')
        .attr('class', d4.functor(scope.accessors.classes).bind(this))
        .attr('transform', function(d) {

          // produces the wrong value
          console.log(this.x(d))
          this.x.rangePoints([0, this.width],1);

          // produces the correct value
          console.log(this.x(d))

          return 'translate(' + this.x(d) + ',' + (this.height / 2) + ')';
        }.bind(this))
        .attr('d', d3.svg.symbol().type(String).size(this.height));
      }
    };
  }).call(this);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.