Code Monkey home page Code Monkey logo

jenkspy's Introduction

Jenkspy: Fast Fisher-Jenks breaks for Python

Compute "natural breaks" (Fisher-Jenks algorithm) on list / tuple / array / numpy.ndarray of integers/floats.

The algorithm implemented by this library is also sometimes referred to as Fisher-Jenks algorithm, Jenks Optimisation Method or Fisher exact optimization method. This is a deterministic method to calculate the optimal class boundaries.

Intended compatibility: CPython 3.7+

Wheels are provided via PyPI for Windows / MacOS / Linux users - Also available on conda-forge channel for Anaconda users.

Usage

Two ways of using jenkspy are available:

  • by using the jenks_breaks function which takes as input a list / tuple / array.array / numpy.ndarray of integers or floats and returns a list of values that correspond to the limits of the classes (starting with the minimum value of the series - the lower bound of the first class - and ending with its maximum value - the upper bound of the last class).
>>> import jenkspy
>>> import json

>>> with open('tests/test.json', 'r') as f:
...     # Read some data from a JSON file
...     data = json.loads(f.read())
...
>>> jenkspy.jenks_breaks(data, n_classes=5) # Asking for 5 classes
[0.0028109620325267315, 2.0935479691252112, 4.205495140049607, 6.178148351609707, 8.09175917180255, 9.997982932254672]
# ^                      ^                    ^                 ^                  ^                 ^
# Lower bound            Upper bound          Upper bound       Upper bound        Upper bound       Upper bound
# 1st class              1st class            2nd class         3rd class          4th class         5th class
# (Minimum value)                                                                                    (Maximum value)
  • by using the JenksNaturalBreaks class that is inspired by scikit-learn classes.

The .fit and .group behavior is slightly different from jenks_breaks, by accepting value outside the range of the minimum and maximum value of breaks_, retaining the input size. It means that fit and group will use only the inner_breaks_. All value below the min bound will be included in the first group and all value higher than the max bound will be included in the last group.

>>> from jenkspy import JenksNaturalBreaks

>>> x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

>>> jnb = JenksNaturalBreaks(4) # Asking for 4 clusters

>>> jnb.fit(x) # Create the clusters according to values in 'x'
>>> print(jnb.labels_) # Labels for fitted data
... print(jnb.groups_) # Content of each group
... print(jnb.breaks_) # Break values (including min and max)
... print(jnb.inner_breaks_) # Inner breaks (ie breaks_[1:-1])
[0 0 0 1 1 1 2 2 2 3 3 3]
[array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8]), array([ 9, 10, 11])]
[0.0, 2.0, 5.0, 8.0, 11.0]
[2.0, 5.0, 8.0]

>>> print(jnb.predict(15)) # Predict the group of a value
3

>>> print(jnb.predict([2.5, 3.5, 6.5])) # Predict the group of several values
[1 1 2]

>>> print(jnb.group([2.5, 3.5, 6.5])) # Group the elements into there groups
[array([], dtype=float64), array([2.5, 3.5]), array([6.5]), array([], dtype=float64)]

Installation

  • From pypi
pip install jenkspy
  • From source
git clone http://github.com/mthh/jenkspy
cd jenkspy/
pip install .
  • For anaconda users
conda install -c conda-forge jenkspy

Requirements

  • Numpy

  • Only for building from source: C compiler, Python C headers, setuptools and Cython.

Motivation:

  • Making a painless installing C extension so it could be used more easily as a dependency in an other package (and so learning how to build wheels using appveyor / travis at first - now it uses GitHub Actions).
  • Getting the break values! (and fast!). No fancy functionality provided, but contributions/forks/etc are welcome.
  • Other python implementations are currently existing but not as fast or not available on PyPi.

jenkspy's People

Contributors

filip-komarzyniec avatar mgomesq avatar mthh avatar plming avatar yasirroni 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

jenkspy's Issues

cannot import name 'JenksNaturalBreaks' from 'jenkspy'

Installed jenkspy using conda:

conda install -c conda-forge jenkspy

Importing JenksNaturalBreaks as shown in the README:

from jenkspy import JenksNaturalBreaks

This gives an error:

ImportError: cannot import name 'JenksNaturalBreaks' from 'jenkspy' (/anaconda3/lib/python3.7/site-packages/jenkspy/__init__.py)

This Import Error is not seen when doing

import jenkspy

breaks = jenkspy.jenks_breaks(X), nb_class=3)

I tried jenkspy.JenksNaturalBreaks() and that didn't work too.

Python version: Python 3.7.7

JenksNaturalBreaks(1) fails

group() doesn't handle the edge case of n_classes=1 as inner_breaks_ is empty:
groups_ = [arr[arr <= self.inner_breaks_[0]]]

Boundary problem involving zeros

Description

I have a data set that I break into 7 bins. The results include the zero value, twice. Here is an example.

bin_count = 7
bins = jenkspy.jenks_breaks(df['value'], nb_class=bin_count)
print(f'BINS {bins}')

BINS [0.0, 0.0, 10435.0, 11643.0, 12476.0, 13337.0, 16116.0, 27125.0]

If the data is broken into 6 bins it works. Here is the output.

BINS [0.0, 10191.0, 11529.0, 12405.0, 13286.0, 16073.0, 27125.0]

I created a GitHub repo with a test Python file and the CSV data.

I presume it's a problem in my code but I can't spot it.

Unable to install Jenkspy

I am trying to install Jenskpy==0.2.0 using pip. It is failing with the below error is there something I can do ?

 /apps/orasw/miniconda/envs/tap_data_dist_env/compiler_compat/ld: skipping incompatible /lib/libpthread.so.0 when searching for /lib/libpthread.so.0
    /apps/orasw/miniconda/envs/tap_data_dist_env/compiler_compat/ld: skipping incompatible /lib/libpthread.so.0 when searching for /lib/libpthread.so.0
    /apps/orasw/miniconda/envs/tap_data_dist_env/compiler_compat/ld: cannot find /lib/libpthread.so.0
    /apps/orasw/miniconda/envs/tap_data_dist_env/compiler_compat/ld: skipping incompatible /usr/lib/libpthread_nonshared.a when searching for /usr/lib/libpthread_nonshared.a
    /apps/orasw/miniconda/envs/tap_data_dist_env/compiler_compat/ld: skipping incompatible /usr/lib/libpthread_nonshared.a when searching for /usr/lib/libpthread_nonshared.a
    /apps/orasw/miniconda/envs/tap_data_dist_env/compiler_compat/ld: cannot find /usr/lib/libpthread_nonshared.a
    collect2: error: ld returned 1 exit status
    error: command 'gcc' failed with exit status 1

Generating more than 1 date in the same break

breaks_jkp = []
for v in breaks:

idx = ts.index[ts == v]
breaks_jkp.append(idx)
breaks_jkp

[DatetimeIndex(['2021-04-18'], dtype='datetime64[ns]', name='date', freq=None),
DatetimeIndex(['2021-05-14', '2021-06-11', '2021-09-27'], dtype='datetime64[ns]', name='date', freq=None),
DatetimeIndex(['2021-07-13'], dtype='datetime64[ns]', name='date', freq=None),
DatetimeIndex(['2021-07-04'], dtype='datetime64[ns]', name='date', freq=None),
DatetimeIndex(['2021-09-03'], dtype='datetime64[ns]', name='date', freq=None)]

Issue

install error: jenkspy for python2

I'm trying to install jenkspy to work with python2. I'm using the pip install jenkspy==0.1.1. When I run this install, it fails. Is anyone else experiencing this?

~/Desktop$ pip install jenkspy==0.1.1

Collecting jenkspy==0.1.1
  Using cached jenkspy-0.1.1.tar.gz (19 kB)
    ERROR: Command errored out with exit status 1:
     command: /home/simulator/anaconda3/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-vfns27rk/jenkspy_1c16cdd575934fbdb04c887234a13d9d/setup.py'"'"'; __file__='"'"'/tmp/pip-install-vfns27rk/jenkspy_1c16cdd575934fbdb04c887234a13d9d/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-pip-egg-info-udmd59el
         cwd: /tmp/pip-install-vfns27rk/jenkspy_1c16cdd575934fbdb04c887234a13d9d/
    Complete output (11 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-vfns27rk/jenkspy_1c16cdd575934fbdb04c887234a13d9d/setup.py", line 22, in <module>
        ext_modules=cythonize(exts) if USE_CYTHON else exts,
      File "/home/simulator/anaconda3/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 965, in cythonize
        module_list, module_metadata = create_extension_list(
      File "/home/simulator/anaconda3/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 815, in create_extension_list
        for file in nonempty(sorted(extended_iglob(filepattern)), "'%s' doesn't match any files" % filepattern):
      File "/home/simulator/anaconda3/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 114, in nonempty
        raise ValueError(error_msg)
    ValueError: 'jenkspy/src/jenks.pyx' doesn't match any files
    ----------------------------------------
WARNING: Discarding https://files.pythonhosted.org/packages/82/ba/74e83afbef266b277f9fa293eb6e61e30c6d8515ab3c517beeeec71ad966/jenkspy-0.1.1.tar.gz#sha256=0cc09b9e0e9c7c99f00b834cac4e14a9e59d1f1a98ef5e014397f39c486310e5 (from https://pypi.org/simple/jenkspy/). Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
ERROR: Could not find a version that satisfies the requirement jenkspy==0.1.1
ERROR: No matching distribution found for jenkspy==0.1.1

Are breaks inclusive or exclusive?

In your example:

jenks_breaks(
[1.3, 7.1, 7.3, 2.3, 3.9, 4.1, 7.8, 1.2, 4.3, 7.3, 5.0, 4.3],
nb_class = 3) # Should output (1.2, 2.3, 5.0, 7.8)

Are the classes/groups:
[1.2, 1.3] [2.3, 4.1, 4.3, 4.3] [5.0, 7.1, 7.3, 7.3, 7.8]
or?
[1.2, 1.3, 2.3] [4.1, 4.3, 4.3, 5.0] [7.1, 7.3, 7.3, 7.8]

My interpretation is that it is the first. However, the second seems like a better grouping.

Is Jenkspy available for Python 3?

Hi,

I would like to know whether there is a version of jenkspy for Python 3 and, if so, how can I install it from pip/conda? When I use the commands suggested in the documentation, it tries to downgrade my Python 3 to 2, and I would prefer just to stick to Python 3.

Thanks for the effort!
Irene

error in two-dimensional arrays

When I run the function using a two-dimensional array(size are (902, 1219)),
I received an error:
IndexError: index 902 is out of bounds for axis 0 with size 902
Does this function not support such two-dimensional arrays?

How to get gvf using jenks object?

I am trying to get the goodness of variance fit using jenks example given in github readme.

I tried the below

  print(jnb.nb_class)
  print(jnb.group)
  print(jnb.goodness_of_variance_fit)

but this doesn't work.

How can I know what is the best number of groups that is chosen by jnb and what is the gvf for that split when compared to other splits?

Check Overflow -> Segfault|Incorrect

Can lead to a segfault:

>>> jenkspy.jenks_breaks([-14838, -126, -21471, 68, -113958128770812511394573913949783646572, 11252, 5757, 18665, 363470513890101146, -3621371149760118654], 8)
Segmentation fault (core dumped)

Or can lead to an incorrect result by silently dropping values without raising an error:

>>> jenkspy.jenks_breaks([1, 2, 3, float("inf")], 2)
<stderr warning>
[1.0, 2.0, 3.0]

Jenkspy 64-bit version?

I am very thankful that I have found JenksPy - it has helped me greatly and likely will in the future as well. My only concern \ question is will there be a 64-bit version available?
Layne

conda-forge wincertstore==0.2=py37_1002 error

Getting the following error when trying to install from conda-forge:

conda install -c conda-forge jenkspy
Solving environment: failed

UnsatisfiableError: The following specifications were found to be in conflict:
  - conda-forge/win-64::wincertstore==0.2=py37_1002
  - jenkspy
Use "conda info <package>" to see the dependencies for each package.

Apparently it has to do with the conda-forge migration: https://stackoverflow.com/questions/52973918/unsatisfiableerror-conda-forge-3-7

pip install failed

Hi @mthh:

Today I updated the Python version to 3.7.1, with using
pip install jenkspy
Occurs

ImportError: cannot import name 'pythran_is_numpy_func_supported' from 'Cython.Compiler.Pythran' (C:\Users\Administrator\Anaconda3\lib\site-packages\Cython\Compiler\Pythran.cp37-win_amd64.pyd)

I did some googling, but unfortunately, there is no solution for this issue! So I'm opening this issue to see if you are going to update this lib? Thanks for your working :)

Duplicate breaks returned for given use-case

Hi,

I have a sequence of numbers (note that the value 1 appears twice) that when passed to jenks_breaks (with nb_class=9) returns a list of breaks with one of the breaks duplicated (i.e. 1 is returned twice).

Is this expected behavior?

import jenkspy

a=[10,9,8,7,6,5,4,3,2,1,1]

print(jenkspy.jenks_breaks(a, nb_class=9))

[1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0]

Thank you!

Cython not specified as a dependency but implicitly required

I'd like to ask what are the reasons behind the change to the setup.py introduced in one of the latest commits: a289197#diff-60f61ab7a8d1910d86d9fda2261620314edcae5894d5aaa236b821c7256badd7

This change requires having Cython preinstalled in the environment while building jenkspy (e.g. during pip installing src distribution).

Having one's own library dependant on jenkspy, one has to now specify Cython as his own dependency or have it somehow already installed in the environment (usually by a workaround and dirty hacks to one's CI-workflows). Either way is not desired.

Why cannot Cython be specified as a dependency / build-only dependency to the jenkspy project? Or why the previous approach of try...except block cannot be preserved?

Segfault for `nan` and `inf` values

But only sometimes. This is on MacoS:

[ins] In [1]: import jenkspy, numpy as np

[ins] In [2]: jenkspy.__version__
Out[2]: '0.1.4'

[ins] In [7]: jenkspy.jenks_breaks([0, 1, np.nan], 2)
Out[7]: [0.0, 0.0, nan]

[ins] In [9]: jenkspy.jenks_breaks([0, 1, 2, 3, 4, np.nan], 4)
[1]    60639 segmentation fault  ipython

Feature request: return goodness of variance fit metric

Thanks for the implementation!

It's hard to decide how many breaks to use without seeing the comparing goodness of fit metrics.

Am I missing something? I don't think this implementation does that. I'm currently calculating this myself after finding breaks using your implementation.

(Also, I also tried modifying your code, but it's a little hard to read. It looks like you're calculating goodness of variance fit in lines 38-61 in _jenks.c? I'm not fluent in c, and so without comments, and with variable names like s1, s2, i3, i4, m1, and m2, it's a heavier lift to figure out what's going on than I can pull off at the moment. Comments or more descriptive variable identifiers would help a lot!)

Thanks again!

Which algorithm? Or incorrect results?

Which algorithm is this? The heuristics Jenks, the total minimum distance Jenks, or the optimized and total minimum distance Fischer-Jenks? Yours deviates from my pure-python Jenks implementation and I suspect off-by-one errors:

import statistics

def distance(l, m):
    if not l:
        raise ValueError
    return sum((i-m)**2 for i in l)

ls = [0, 1, -2]
b = 2
jpc = break_list(ls, jenkspy.jenks_breaks(ls, b))
jmc = jenks(ls, b)[1]
jpd = [distance(c, statistics.mean(c)) for c in jpc]
jmd = [distance(c, statistics.mean(c)) for c in jmc]
jps = sum(jpd)
jms = sum(jmd)

print("jenkspy:")
print(f"\tsum distance: {jps}")
print(f"\tdistances:    {jpd}")
print(f"\tclusters:     {[sorted(c) for c in jpc]}")
print()
print("pure-python:")
print(f"\tsum distance: {jms}")
print(f"\tdistances:    {jmd}")
print(f"\tclusters:     {[sorted(c) for c in jmc]}")
jenkspy:
        sum distance: 2
        distances:    [2, 0]
        clusters:     [[-2, 0], [1]]

pure-python:
        sum distance: 0.5
        distances:    [0, 0.5]
        clusters:     [[-2], [0, 1]]

Even intuitively the jenkspy result is incorrect. My implementation and unit tests are here, see jenks and test_jenks_breaks.

Resources:

[1] Is Jenks' Natural Breaks deterministic?
[2] Fisher's Natural Breaks Classification complexity proof
[3] Re: Jenk's Optimization for Natural Breaks Classification

breaks including min and max

Hi found the behavior to include the min and max values into the breaks very unintuitive. I had to go into your code, because I thought there is something faulty. Right there I found your comment, which made everything clear.

So I think you should either communicate this behavior in the README or remove it. Apart from that, nice tool!

The return type of `jenks_breaks`

The return type of jenks_breaks is a tuple. But it's a list actually.
The doc or return type should be updated.

jenkspy/jenkspy/core.py

Lines 79 to 93 in a4ee57e

Returns
-------
breaks : tuple of floats
The computed break values, including minimum and maximum, in order
to have all the bounds for building `nb_class` class,
so the returned tuple has a length of `nb_class` + 1.
Examples
--------
Using nb_class = 3, expecting 4 break values , including min and max :
>>> jenks_breaks(
[1.3, 7.1, 7.3, 2.3, 3.9, 4.1, 7.8, 1.2, 4.3, 7.3, 5.0, 4.3],
nb_class = 3) # Should output (1.2, 2.3, 5.0, 7.8)

>>> from jenkspy import jenks_breaks
>>> jenks_breaks([1.3, 7.1, 7.3, 2.3, 3.9, 4.1, 7.8, 1.2, 4.3, 7.3, 5.0, 4.3], 3)
[1.2, 2.3, 5.0, 7.8]

Submit Zenodo

What do you think about making a Zenodo publication, and give this repo a DOI for better and simpler citation.

How many datapoints can this work with?

I am using this library to create bins on 1-d data with around 35 million datapoints. It takes forever (4+ hours) and I had to kill it without results. If I try it with around 10,000 datapoints it works fine and returns results in few seconds.

Is this library only meant for datasets with smaller sizes?

KeyError: 20

Hi everyone. I am trying to get the natural breaks of a geodataframe column. Even though the data type is float64, I am getting a KeyError: 20. Does anyone know what might be wrong ? Thanks in advance

error

Speed with large arrays

Thanks for this package, @mthh. This is more of a question than issue. I'm using your code in a raster GIS context and trying to get natural breaks for large rasters (> 1,000,000 cells). I've got equal interval and quantile classification modes built in for comparison. I'm curious if you've ever done speed comparisons on large datasets and if you'd advocate sampling to reduce classification times.

Here's a sample of what I'm seeing:

import numpy as np
import jenkspy

# Equal interval
def equal_intervals(arr, bin_count):
    return np.linspace(arr.min(), arr.max(), num=bin_count + 1)

# Quantile
def approx_quantiles(arr, bin_count):
    if arr.size <= bin_count:
        return np.sort(arr)
    q = np.linspace(0, 1, bin_count + 1)
    bins = np.quantile(arr, q)
    uniq, counts = np.unique(bins, return_counts=True)
    dup = uniq[counts > 1]
    if len(dup):
        new = arr[arr != dup[0]]
        return np.sort(
            np.hstack((dup[0], approx_quantiles(new, bin_count - 1)))
        )
    return bins

# Natural breaks
def jenks(arr, bin_count):
    return jenkspy.jenks_breaks(arr, bin_count)

# Sample array (n=10000)
arr = np.random.randint(0, 100, 10000)

When timing these, jenks is slower than I would expect, but I admit to not studying the algorithm carefully enough to know if this is expected.

%timeit equal_intervals(arr, 6)
61.5 µs ± 4.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit approx_quantiles(arr, 6)
431 µs ± 30.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit jenks(arr, 6)
514 ms ± 54.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Sampling the original array obviously helps speeds, but the bin boundaries aren't exact:

import math
arr = np.random.randint(0, 1000, 100000)
for i in range(10, 0, -1):
    size = math.floor(arr.size * i / 10)
    sample = np.random.choice(arr, size=size)
    %timeit jenks(sample, 6)
    print(f"{i*10}% sample", jenks(sample, 6))

49.6 s ± 864 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
100% sample [0.0, 167.0, 335.0, 503.0, 670.0, 835.0, 999.0]

39.6 s ± 661 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
90% sample [0.0, 165.0, 331.0, 497.0, 664.0, 831.0, 999.0]

30.8 s ± 1.03 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
80% sample [0.0, 167.0, 333.0, 500.0, 667.0, 834.0, 999.0]

23.8 s ± 686 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
70% sample [0.0, 170.0, 339.0, 507.0, 672.0, 834.0, 999.0]

17.7 s ± 708 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
60% sample [0.0, 168.0, 335.0, 500.0, 665.0, 831.0, 999.0]

12.1 s ± 392 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
50% sample [0.0, 169.0, 334.0, 500.0, 665.0, 831.0, 999.0]

7.28 s ± 158 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
40% sample [0.0, 168.0, 335.0, 502.0, 668.0, 833.0, 999.0]

4.31 s ± 221 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
30% sample [0.0, 168.0, 337.0, 503.0, 667.0, 832.0, 999.0]

2.05 s ± 48.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
20% sample [0.0, 170.0, 338.0, 507.0, 674.0, 837.0, 999.0]

458 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
10% sample [0.0, 167.0, 334.0, 500.0, 664.0, 830.0, 999.0]

Thanks for any pointers you may have on how to use jenkspy more efficiently.

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.