Giter VIP home page Giter VIP logo

pyribs's Introduction

pyribs

Discord Mailing List Twitter
Google Groups Twitter
Website Source Docs Paper
pyribs.org GitHub docs.pyribs.org pyribs.org/paper
PyPI Conda CI/CD Docs Status
PyPI Conda Recipe Tests Documentation Status

A bare-bones Python library for quality diversity (QD) optimization. Pyribs implements the highly modular Rapid Illumination of Behavior Space (RIBS) framework for QD optimization. Pyribs is also the official implementation of Covariance Matrix Adaptation MAP-Elites (CMA-ME), Covariance Matrix Adaptation MAP-Elites via a Gradient Arborescence (CMA-MEGA), Covariance Matrix Adaptation MAP-Annealing (CMA-MAE), and scalable variants of CMA-MAE.

Community

Join the Pyribs Announcements mailing list for infrequent updates (less than 1/month) on the status of the project and new releases.

Need some help? Want to ask if pyribs is right for your project? Have a question that is not quite a bug and not quite a feature request?

Join the community Discord!

Overview

Types of Optimization

Quality diversity (QD) optimization is a subfield of optimization where solutions generated cover every point in a measure space while simultaneously maximizing (or minimizing) a single objective. QD algorithms within the MAP-Elites family of QD algorithms produce heatmaps (archives) as output where each cell contains the best discovered representative of a region in measure space.

In the QD literature, measure function outputs have also been referred to as "behavioral characteristics," "behavior descriptors," or "feature descriptors."

Recent years have seen the development of a large number of QD algorithms. To represent these and future algorithms, we have developed the highly modular RIBS framework. RIBS divides a QD algorithm into three components:

  • An archive, which saves generated solutions within measure space.
  • One or more emitters, where each emitter is an algorithm which generates new candidate solutions and responds to feedback about how the solutions were evaluated and how they were inserted into the archive.
  • A scheduler that controls the interaction of the archive and emitters. The scheduler also provides an interface for requesting new candidate solutions and telling the algorithm how candidates performed.

By interchanging these components, a user can compose a large number of QD algorithms.

Pyribs is an implementation of the RIBS framework designed to support a wide range of users, from beginners entering the field to experienced researchers seeking to develop new algorithms. Pyribs achieves these goals by embodying three principles:

  • Simple: Centered only on components that are absolutely necessary to run a QD algorithm, allowing users to combine the framework with other software frameworks.
  • Flexible: Capable of representing a wide range of current and future QD algorithms, allowing users to easily create or modify components.
  • Accessible: Easy to install and learn, particularly for beginners with limited computational resources.

In contrast to other QD libraries, pyribs is "bare-bones." For example, like pycma, pyribs focuses solely on optimizing fixed-dimensional continuous domains. Focusing on this one commonly-occurring problem allows us to optimize the library for performance as well as ease of use. Refer to the list of additional QD libraries below if you need to handle additional use cases.

Following the RIBS framework (shown in the figure below), a standard algorithm in pyribs operates as follows:

  1. The user calls the ask() method on the scheduler. The scheduler requests solutions from each emitter by calling the emitter's ask() method.
  2. The user evaluates solutions to obtain the objective and measure values.
  3. The user passes the evaluations to the scheduler's tell() method. The scheduler adds the solutions into the archive and receives feedback. The scheduler passes this feedback along with the evaluated solutions to each emitter's tell() method, and each emitter then updates its internal state.

The RIBS framework

Installation

pyribs supports Python 3.8 and above. The vast majority of users can install pyribs by running:

# If you are on Mac, you may need to use quotations, e.g., pip install "ribs[visualize]"
pip install ribs[visualize]

The command above includes the visualize extra. If you will not be using the pyribs visualization tools, you can install the base version of pyribs with:

pip install ribs

For more specific installation commands (e.g., installing from Conda or installing from source), visit the installation selector on our website.

To test your installation, import pyribs and print the version with this command:

python -c "import ribs; print(ribs.__version__)"

You should see a version number in the output.

Usage

Here we show an example application of CMA-ME in pyribs. To initialize the algorithm, we first create:

  • A 2D GridArchive where each dimension contains 20 cells across the range [-1, 1].
  • Three instances of EvolutionStrategyEmitter, all of which start from the search point 0 in 10-dimensional space and a Gaussian sampling distribution with standard deviation 0.1.
  • A Scheduler that combines the archive and emitters together.

After initializing the components, we optimize (pyribs maximizes) the negative 10-D Sphere function for 1000 iterations. Users of pycma will be familiar with the ask-tell interface (which pyribs adopted). First, the user must ask the scheduler for new candidate solutions. After evaluating the solution, they tell the scheduler the objectives and measures of each candidate solution. The algorithm then populates the archive and makes decisions on where to sample solutions next. Our toy example uses the first two parameters of the search space as measures.

import numpy as np

from ribs.archives import GridArchive
from ribs.emitters import EvolutionStrategyEmitter
from ribs.schedulers import Scheduler

archive = GridArchive(
    solution_dim=10,
    dims=[20, 20],
    ranges=[(-1, 1), (-1, 1)],
)
emitters = [
    EvolutionStrategyEmitter(
        archive,
        x0=[0.0] * 10,
        sigma0=0.1,
    ) for _ in range(3)
]
scheduler = Scheduler(archive, emitters)

for itr in range(1000):
    solutions = scheduler.ask()

    # Optimize the 10D negative Sphere function.
    objective_batch = -np.sum(np.square(solutions), axis=1)

    # Measures: first 2 coordinates of each 10D solution.
    measures_batch = solutions[:, :2]

    scheduler.tell(objective_batch, measures_batch)

To visualize this archive with Matplotlib, we then use the grid_archive_heatmap function from ribs.visualize.

import matplotlib.pyplot as plt
from ribs.visualize import grid_archive_heatmap

grid_archive_heatmap(archive)
plt.show()

Sphere heatmap

Documentation

The documentation is available online here. We suggest that new users start with the tutorials.

Paper and Citation

Two years after the initial release of pyribs, we released a paper that elaborates on the RIBS framework and the design decisions behind pyribs. For more information on this paper, see here. If you use pyribs in your research, please consider citing this paper as follows. Also consider citing any algorithms you use as shown below.

@inproceedings{10.1145/3583131.3590374,
  author = {Tjanaka, Bryon and Fontaine, Matthew C and Lee, David H and Zhang, Yulun and Balam, Nivedit Reddy and Dennler, Nathaniel and Garlanka, Sujay S and Klapsis, Nikitas Dimitri and Nikolaidis, Stefanos},
  title = {Pyribs: A Bare-Bones Python Library for Quality Diversity Optimization},
  year = {2023},
  isbn = {9798400701191},
  publisher = {Association for Computing Machinery},
  address = {New York, NY, USA},
  url = {https://doi.org/10.1145/3583131.3590374},
  doi = {10.1145/3583131.3590374},
  abstract = {Recent years have seen a rise in the popularity of quality diversity (QD) optimization, a branch of optimization that seeks to find a collection of diverse, high-performing solutions to a given problem. To grow further, we believe the QD community faces two challenges: developing a framework to represent the field's growing array of algorithms, and implementing that framework in software that supports a range of researchers and practitioners. To address these challenges, we have developed pyribs, a library built on a highly modular conceptual QD framework. By replacing components in the conceptual framework, and hence in pyribs, users can compose algorithms from across the QD literature; equally important, they can identify unexplored algorithm variations. Furthermore, pyribs makes this framework simple, flexible, and accessible, with a user-friendly API supported by extensive documentation and tutorials. This paper overviews the creation of pyribs, focusing on the conceptual framework that it implements and the design principles that have guided the library's development. Pyribs is available at https://pyribs.org},
  booktitle = {Proceedings of the Genetic and Evolutionary Computation Conference},
  pages = {220–229},
  numpages = {10},
  keywords = {framework, quality diversity, software library},
  location = {Lisbon, Portugal},
  series = {GECCO '23}
}

Contributors

pyribs is developed and maintained by the ICAROS Lab at USC. For information on contributing to the repo, see CONTRIBUTING.

We thank Amy K. Hoover and Julian Togelius for their contributions deriving the CMA-ME algorithm.

Users

pyribs users include:

Publications

For the list of publications that use pyribs, refer to our Google Scholar entry.

Software

See the GitHub dependency graph for the public GitHub repositories which depend on pyribs.

Citing Algorithms in pyribs

If you use the following algorithms, please consider citing their relevant papers:

  • CMA-ME: Fontaine 2020
    @inproceedings{10.1145/3377930.3390232,
      author = {Fontaine, Matthew C. and Togelius, Julian and Nikolaidis, Stefanos and Hoover, Amy K.},
      title = {Covariance Matrix Adaptation for the Rapid Illumination of Behavior Space},
      year = {2020},
      isbn = {9781450371285},
      publisher = {Association for Computing Machinery},
      address = {New York, NY, USA},
      url = {https://doi.org/10.1145/3377930.3390232},
      doi = {10.1145/3377930.3390232},
      booktitle = {Proceedings of the 2020 Genetic and Evolutionary Computation Conference},
      pages = {94–102},
      numpages = {9},
      location = {Canc\'{u}n, Mexico},
      series = {GECCO '20}
    }
    
  • CMA-MEGA: Fontaine 2021
    @inproceedings{NEURIPS2021_532923f1,
     author = {Fontaine, Matthew and Nikolaidis, Stefanos},
     booktitle = {Advances in Neural Information Processing Systems},
     editor = {M. Ranzato and A. Beygelzimer and Y. Dauphin and P.S. Liang and J. Wortman Vaughan},
     pages = {10040--10052},
     publisher = {Curran Associates, Inc.},
     title = {Differentiable Quality Diversity},
     url = {https://proceedings.neurips.cc/paper/2021/file/532923f11ac97d3e7cb0130315b067dc-Paper.pdf},
     volume = {34},
     year = {2021}
    }
    
  • CMA-MAE: Fontaine 2022
    @misc{cmamae,
      doi = {10.48550/ARXIV.2205.10752},
      url = {https://arxiv.org/abs/2205.10752},
      author = {Fontaine, Matthew C. and Nikolaidis, Stefanos},
      keywords = {Machine Learning (cs.LG), Artificial Intelligence (cs.AI), FOS: Computer and information sciences, FOS: Computer and information sciences},
      title = {Covariance Matrix Adaptation MAP-Annealing},
      publisher = {arXiv},
      year = {2022},
      copyright = {arXiv.org perpetual, non-exclusive license}
    }
    
  • Scalable CMA-MAE: Tjanaka 2022
    @ARTICLE{10243102,
      author={Tjanaka, Bryon and Fontaine, Matthew C. and Lee, David H. and Kalkar, Aniruddha and Nikolaidis, Stefanos},
      journal={IEEE Robotics and Automation Letters},
      title={Training Diverse High-Dimensional Controllers by Scaling Covariance Matrix Adaptation MAP-Annealing},
      year={2023},
      volume={8},
      number={10},
      pages={6771-6778},
      keywords={Covariance matrices;Training;Neural networks;Legged locomotion;Reinforcement learning;Evolutionary robotics;Evolutionary robotics;reinforcement learning},
      doi={10.1109/LRA.2023.3313012}
    }
    

Additional QD Libraries

  • QDax: Implementations of QD algorithms in JAX. QDax is suitable if you want to run entire QD algorithms on hardware accelerators in a matter of minutes, and it is particularly useful if you need to interface with Brax environments.
  • qdpy: Python implementations of a wide variety of QD algorithms.
  • sferes: Contains C++ implementations of QD algorithms; can also handle discrete domains.

License

pyribs is released under the MIT License.

Credits

The pyribs package was initially created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.

pyribs's People

Contributors

btjanaka avatar henrychen4 avatar itsdawei avatar ld-ing avatar lunjohnzhang avatar ndennler avatar nivedit24 avatar robbycostales avatar snokpok avatar sujaygarlanka avatar svott03 avatar vbhatt-cs avatar yutaizhou 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

pyribs's Issues

[FEATURE REQUEST] Add full support for Python 3.9

Description

Currently, numba does not work with Python 3.9 (numba/numba#6345), so we cannot support it yet.

This is due to numba/llvmlite#669. Since Python 3.9 wheels are not available for llvmlite, CI/CD is trying to build the wheels, but is unable to due to lack of llvm. This should be resolved in a few weeks. For now, Python 3.9 will only work if users can build the llvmlite wheel on their own -- for this, they will need to have the llvm libraries installed.

Default to stable version for documentation

The current default version is latest. This may be confusing as latest has some features that stable does not. Switching the default will require:

  • grep existing documentation and documentation configuration for "latest"
  • check the website for instances of "latest"
  • switch the default on readthedocs

[FEATURE REQUEST] Add data parameter to heat map functions

Description

A data parameter would contain an earlier snapshot of the archive as a Pandas dataframe. This parameter would allow one to plot an earlier version of the archive -- this is useful if one has multiple snapshots of an archive lying around and wants to plot heatmaps of them after the fact.

Decide on and set up project styles and tools.

Styes:

  • flake8 vs pylint
  • numpy style vs google style vs pep8 style

Tools:

  • restview (for automatically viewing reST files)
  • yapf
  • sphinx-rtd-theme (nice Sphinx theme for ReadTheDocs)
  • sphinx-autobuild (automatically reloads Sphinx documentation)
  • pytest-cov
  • LGTM? (https://lgtm.io)

[FEATURE REQUEST] Multi-dim heatmaps

Description

Following the plots in Mouret and Clune 2015 (the MAP-Elites paper), we could extend heatmap functions to plot multi-dimensional heatmaps. Users would be able to specify a dimension ordering, and we would plot all the heatmaps. This would work on all heatmaps (sliding boundaries heatmap may be a bit tricky though).

[FEATURE REQUEST] Archive size attributes

Description

Currently, the number of bins occupied in the archive is found by taking the length of archive.as_pandas(include_solutions=False). This is very indirect and slow. We should add a length method (either overload __len__ or add a property like occupied) that makes it easy to check how many bins are occupied.

We could also add a bins property to all archives with the total number of bins in the archive.

[FEATURE REQUEST] Make Colab links more flexible

Description

Currently, Colab links are created in docs/_templates/sourcelink.html. However, this assumes the notebooks are either 1) under the docs directory or 2) under the examples/tutorials directory. Furthermore, the links only work for notebooks on the master branch. In the future, we may want to make this more flexible.

[BUG] Lunar Lander Example does not work

  • pyribs version: 0.2.1
  • Python version: 3.8
  • Operating System: Ubuntu 20.04

Description

The seaborn dependency is not installed when using pip install -e .[examples]

This leads to the following error:

Traceback (most recent call last):
  File "lunar_lander.py", line 15, in <module>
    import seaborn as sns
ModuleNotFoundError: No module named 'seaborn'

Steps to Reproduce

  1. Clone repository
  2. run pip install -e .[examples] to install depedencies for examples
  3. cd examples
  4. python lunar_lander.py

Make archive be the first parameter to all emitters

Currently, we have emitter constructors like:

IsoLineEmitter(x0, archive, ...)
GaussianEmitter(x0, sigma0, archive, ...)
ImprovementEmitter(x0, sigma0, archive, ...)

It would be more consistent if archive was always the first parameter, as it is the one thing that all emitters must have. This would require changing the library code as well as fixing the tests and examples.

[FEATURE REQUEST] Logging Capabilities

Description

Similar to pycma, we can add a logger that, when given an archive, records several metrics about it. I don't think we would want to tie it in too closely to the archives, as people may want to calculate their own metrics.

This may require adding some methods to the archives (preferable ArchiveBase) to compute metrics on them.

Metrics include archive size, archive fitness (mean, max, min, and maybe median), QD score.

[FEATURE REQUEST] Test Mac and Windows in CI/CD

Description

Many users use Mac or Windows (not just Linux), so we should start testing on those platforms too. GitHub Actions is completely free for public repos, so this would not cost anything on our end.

[FEATURE REQUEST] Make emitters easy to extend

Description

Basically, this is a clean-up of EmitterBase and its children to make it as easy as possible to extend. We should also add a tutorial on how to extend it, or at least refer to existing code. We can link to this tutorial or add some notes in the EmitterBase docstring.

Set up GitHub Actions for CI/CD

Currently, we are using Travis CI, which does not give free CI/CD for private repos. GitHub Actions will give us a limited amount of compute per month. If we run out, we will have to test locally.

Create Lunar Lander CMA-ME Example

Typical QD algorithms have to be run on large clusters; it is very hard to run them locally and then see their results. To help solve this, we will create a CMA-ME example that runs in the OpenAI Gym LunarLander-v2 environment. This example will show what QD algorithms are able to accomplish; furthermore, since it will only take a few hours to train (perhaps even less if we use multiprocessing and other optimizations), it will be highly accessible.

[INFO] scipy cKDTree is faster than sklearn NN for single queries

In CVTArchive, we need to use a k-D tree for nearest neighbor searches when there are a lot of centroids. There are many implementations of k-D tree to choose from. Two notable implementations are scipy.spatial.cKDTree and sklearn.neighbors.NearestNeighbors. Both implementations are optimized for batched nearest-neighbor queries, but in _get_index, we query the nearest neighbor of a single point. If we run the following code, we can compare the performance of each implementation on single and batch queries.

import time

import numpy as np
from scipy.spatial import cKDTree
from sklearn.neighbors import NearestNeighbors
from tqdm import trange

samples = np.random.uniform(
    np.array([-1, -1]),
    np.array([1, 1]),
    size=(100_000, 2),
)

points = np.random.uniform(
    np.array([-1, -1]),
    np.array([1, 1]),
    size=(100, 2),
)

print("scipy cKDTree (batch)")
nn = cKDTree(points)
start = time.time()
nn.query(samples)
print(time.time() - start)

print("scipy cKDTree (single)")
nn = cKDTree(points)
start = time.time()
for i in trange(len(samples)):
    nn.query(samples[i])
print(time.time() - start)

print("sklearn NN with kd_tree (batch)")
nn = NearestNeighbors(n_neighbors=1, algorithm="kd_tree").fit(points)
start = time.time()
nn.kneighbors(samples)
print(time.time() - start)

print("sklearn NN with kd_tree (single)")
nn = NearestNeighbors(n_neighbors=1, algorithm="kd_tree").fit(points)
start = time.time()
for i in trange(len(samples)):
    nn.kneighbors(np.expand_dims(samples[i], axis=0))
print(time.time() - start)

I got the following output, which shows that NearestNeighbors is ~10x slower on single queries.

scipy cKDTree (batch)                                                                                     
0.036180734634399414                                                                                      
scipy cKDTree (single)                                                                                    
100%|██████████████████████████████████████████████████████████| 100000/100000 [00:03<00:00, 27718.94it/s]
3.6090946197509766   # cKDTree is fast                                                                                     
sklearn NN with kd_tree (batch)                                                                           
0.052004098892211914
sklearn NN with kd_tree (single)
100%|███████████████████████████████████████████████████████████| 100000/100000 [00:41<00:00, 2397.46it/s$
41.71117091178894    # NearesNeighbors is slow

In short, we should definitely use cKDTree.

[FEATURE REQUEST] Extended ask-tell interface

Description

To facilitate more calls to ask-tell within a single loop, add a mode parameter to ask and tell. Different types of solutions are returned depending on what mode is used. Example usage:

for i in range(n):
  sols1 = opt.ask("mode1")
  ...
  opt.tell(objs1, bcs1)

  sols2 = opt.ask("mode2")
  ...
  opt.tell(objs2, bcs2)

Notes:

  • This change would involve the Optimizer as well as all emitters.
  • For simplicity, the emitter should throw an error if it is passed an unknown mode.
  • The default mode would be called "default".

[FEATURE REQUEST] Archive metadata

Description

Sometimes, we would like to store information about solutions in the archive, and this data is not necessarily a BC or objective. For instance, in the Lunar Lander example, it would be nice if we could store the total number of timesteps of the run, or the final x position. To do this, we can modify the archive API to support storing metadata. For simplicity, metadata will take the form of arbitrary objects (often a dict). Metadata will always be present in the archive, but it will default to None, such that users do not need to provide it. The following API changes are proposed:

  • __init__ gains a use_metadata parameter where the user can specify whether or not to use metadata. This defaults to False for backward compatibility and simplicity.
  • initialize allocates an object array for storing the metadata
  • add gains a parameter for passing in a metadata object. This defaults to None and is ignored unless metadata is turned on.
  • elite_with_behavior and get_random_elite additionally return metadata (only if applicable)
  • as_pandas gets a parameter for including metadata (defaults to False) and adds a metadata column onto the dataframe. The resulting dataframe cannot be saved with to_csv, but that is something we can leave to users. Users may also just save with to_pickle

The ask and tell methods in the emitters and Optimizer must also be modified to support the metadata by taking in an array/list of objects.

Usage example:

archive = GridArchive([10,10], [(-1,1)] * 2)
archive.initialize(10)  # In addition to the regular arrays, allocates a metadata array
archive.add(
    solutions=np.ones(10),
    objective_value=1.0,
    behavior_values=np.ones(2),
    metadata={  # An arbitrary metadata object (in this case a dict).
        "metadata1": np.ones(12), 
        "metadata2": 1,
    },
)

[FEATURE REQUEST] Ability to save and reload optimizer state

Description

In some cases, QD algorithms may run for a certain amount of time but then need to be paused. We should add the ability to save the state of a running algorithm's components along with its config, and to then reload that algorithm.

This could be implemented in several ways. We could try to make the algorithm components pickle-able, and then save the whole algorithm to a pickle file. Perhaps a simpler way would be to have a function that, given an Optimizer, saves all the optimizer's variables to a file along with all the archive and emitter variables. Then a separate function would recreate everything from this file.

A challenge would be dealing with changes to our API -- some arguments may not always be compatible across versions. To solve this, we can ensure that we have good versioning practices and only allow reconstructing certain files with certain versions of the library.

References

[FEATURE REQUEST] Grid archive heatmap

Description

Just as we have a heatmap function for CVTArchive and SlidingBoundaryArchive, we should have one for GridArchive. The current method of using Seaborn's heatmap directly does not work well because not all cells are shown.

In order to implement this method, we could leverage Seaborn's heatmap, though that would mean adding another dependency. It would be better to use a method like imshow or pcolormesh from matplotlib.

Make archive as_pandas method use snake_case in column names

Description

Currently, we are using kebab-case in our column names in as_pandas. When we iterate over the columns of the dataframe with itertuples(), Pandas tries to convert these names to namedtuple attributes. But since dashes are not allowed, it just uses a generic name like _1, _2, etc. We need to switch this name so that itertuples() makes sense.

This will involve:

  • Fixing the naming in ArchiveBase and other archive components
  • Fixing examples, tutorials, and other documentation pages that use as_pandas()

Develop a logo and social image

Description

A logo would be great for making things look professional. We should also have a "Social Preview image" so that the repository has a nice image when shared on places like Facebook. Also, the logo should be made into a favicon.

Change Optimizer to Scheduler

"Optimizer" can be a bit confusing, especially since we have optimizers in the emitters. Hence, we would like to rename everything to "Scheduler". The current "Optimizer" would be renamed "BasicScheduler"

Changes Required

  • Rename Optimizer in the library
  • Fix ribs.factory
  • Fix tutorials
  • Fix examples

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.