Giter Club home page Giter Club logo

pandoc-pyplot's Introduction

DEPRECATED. The pandoc-plot project replaces pandoc-pyplot by extending its capabilities to other plotting toolkits. pandoc-pyplot will not received any new features.

pandoc-pyplot - A Pandoc filter to generate Matplotlib/Plotly figures directly in documents

Hackage version Stackage version (LTS) Windows Build status macOS and Linux Build Status GitHub

pandoc-pyplot turns Python code present in your documents into embedded figures via Matplotlib or Plotly.

Usage

Markdown

The filter recognizes code blocks with the .pyplot or .plotly classes present in Markdown documents. It will run the script in the associated code block in a Python interpreter and capture the generated Matplotlib/Plotly figure.

Here is a basic example using the scripting matplotlib.pyplot API:

```{.pyplot}
import matplotlib.pyplot as plt

plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
```

Putting the above in input.md, we can then generate the plot and embed it:

pandoc --filter pandoc-pyplot input.md --output output.html

or

pandoc --filter pandoc-pyplot input.md --output output.pdf

or any other output format you want.

LaTeX

The filter works slightly differently in LaTeX documents. In LaTeX, the minted environment must be used, with the pyplot class.

\begin{minted}{pyplot}
import matplotlib.pyplot as plt

plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
\end{minted}

Note that you do not need to have minted installed.

Examples

There are more examples in the source repository, in the \examples directory.

Features

Captions

You can also specify a caption for your image. This is done using the optional caption parameter.

Markdown:

```{.pyplot caption="This is a simple figure"}
import matplotlib.pyplot as plt

plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
```

LaTex:

\begin{minted}[caption=This is a simple figure]{pyplot}
import matplotlib.pyplot as plt

plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
\end{minted}

Caption formatting is either plain text or Markdown. LaTeX-style math is also support in captions (using dollar signs $...$).

Link to source code and high-resolution figure

In case of an output format that supports links (e.g. HTML), the embedded image generated by pandoc-pyplot will be a link to the source code which was used to generate the file. Therefore, other people can see what Python code was used to create your figures. A high resolution image will be made available in a caption link.

(New in version 2.1.3.0) For cleaner output (e.g. PDF), you can turn this off via the links=false key:

Markdown:

```{.pyplot links=false}
...
```

LaTex:

\begin{minted}[links=false]{pyplot}
...
\end{minted}

or via a configuration file.

Including scripts

If you find yourself always repeating some steps, inclusion of scripts is possible using the include parameter. For example, if you want all plots to have the ggplot style, you can write a very short preamble style.py like so:

import matplotlib.pyplot as plt
plt.style.use('ggplot')

and include it in your document as follows:

```{.pyplot include=style.py}
plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
```

Which is equivalent to writing the following markdown:

```{.pyplot}
import matplotlib.pyplot as plt
plt.style.use('ggplot')

plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
```

The equivalent LaTeX usage is as follows:

\begin{minted}[include=style.py]{pyplot}

\end{minted}

This include parameter is perfect for longer documents with many plots. Simply define the style you want in a separate script! You can also import packages this way, or define functions you often use.

Customization of figures beyond what is available in pandoc-pyplot can also be done through the include script. For example, if you wanted to figures with a black background, you can do so via matplotlib.pyplot.rcParams:

import matplotlib.pyplot as plt

plt.rcParams['savefig.facecolor'] = 'k'
...

You can take a look at all available matplotlib parameters here.

Multiple backends

(new in version 2.2.0.0) Both Matplotlib and Plotly are supported!

To render Plotly figures in Markdown:

```{.plotly caption="This is a Plotly figure"}
import plotly.graph_objects as go
figure = go.Figure(
    data=[go.Bar(y=[2, 1, 3])],
)

Here is the LaTeX equivalent:

\begin{minted}[caption=This is a Plotly figure]{plotly}
import plotly.graph_objects as go
figure = go.Figure(
    data=[go.Bar(y=[2, 1, 3])],
)
\end{minted}

pandoc-pyplot will render and capture your figure automagically.

No wasted work

pandoc-pyplot minimizes work, only generating figures if it absolutely must. Therefore, you can confidently run the filter on very large documents containing dozens of figures --- like a book or a thesis --- and only the figures which have changed will be re-generated.

Compatibility with pandoc-crossref

pandoc-crossref is a pandoc filter that makes it effortless to cross-reference objects in Markdown documents.

You can use pandoc-crossref in conjunction with pandoc-pyplot for the ultimate figure-making pipeline. You can combine both in a figure like so:

```{#fig:myexample .pyplot caption="This is a caption"}
# Insert figure script here
```

As you can see in @fig:myexample, ...

If the above source is located in file myfile.md, you can render the figure and references by applying pandoc-pyplot first, and then pandoc-crossref. For example:

pandoc --filter pandoc-pyplot --filter pandoc-crossref -i myfile.md -o myfile.html

Configurable

(New in version 2.1.0.0) To avoid repetition, pandoc-pyplot can be configured using simple YAML files. pandoc-pyplot will look for a .pandoc-pyplot.yml file in the current working directory:

# You can specify any or all of the following parameters
interpreter: python36
directory: mydirectory/
include: mystyle.py
format: jpeg
links: false
dpi: 150            # Matplotlib only
tight_bbox: true    # Matplotlib only
transparent: false  # Matplotlib only
flags: [-O, -Wignore]

These values override the default values, which are equivalent to:

# Defaults if no configuration is provided.
# Note that the default interpreter name on MacOS and Unix is 'python3'
# and 'python' on Windows.
interpreter: python
flags: []
directory: generated/
format: png
links: true
dpi: 80
tight_bbox: false
transparent: false

Using pandoc-pyplot --write-example-config will write the default configuration to a file .pandoc-pyplot.yml, which you can then customize.

Configuration-only parameters

There are a few parameters that are only available via the configuration file .pandoc-pyplot.yml:

  • interpreter is the name of the interpreter to use. For example, interpreter: python36;
  • flags is a list of strings, which are flags that are passed to the python interpreter. For example, flags: [-O, -Wignore];
  • (New in version 2.1.5.0) tight_bbox is a boolean that determines whether to use bbox_inches="tight" or not when saving Matplotlib figures. For example, tight_bbox: true. See here for details. This is ignored for Plotly figures.
  • (New in version 2.1.5.0) transparent is a boolean that determines whether to make Matplotlib figure background transparent or not. This is useful, for example, for displaying a plot on top of a colored background on a web page. High-resolution figures are not affected. For example, transparent: true. This is ignored for Plotly figures.

Installation

Binaries

Windows binaries are available on GitHub. Place the executable in a location that is in your PATH to be able to call it.

If you can show me how to generate binaries for other platform using e.g. Azure Pipelines, let me know!

Installers (Windows)

Windows installers are made available thanks to Inno Setup. You can download them from the release page.

From Hackage/Stackage

pandoc-pyplot is available on Hackage. Using the cabal-install tool:

cabal update
cabal install pandoc-pyplot

Similarly, pandoc-pyplot is available on Stackage:

stack update
stack install pandoc-pyplot

From source

Building from source can be done using stack or cabal:

git clone https://github.com/LaurentRDC/pandoc-pyplot
cd pandoc-pylot
stack install # Alternatively, `cabal install`

Running the filter

Requirements

This filter requires a Python interpreter and at least Matplotlib or Plotly installed. The name of the Python interpreter to use can be specified in a .pandoc-pyplot.yml file; by default, pandoc-pyplot will use the "python" name on Windows, and "python3" otherwise.

Use the filter with Pandoc as follows:

pandoc --filter pandoc-pyplot input.md --output output.html

in which case, the output is HTML. Another example with PDF output:

pandoc --filter pandoc-pyplot input.md --output output.pdf

Python exceptions will be printed to screen in case of a problem.

pandoc-pyplot has a limited command-line interface. Take a look at the help available using the -h or --help argument:

pandoc-pyplot --help

Usage as a Haskell library

To include the functionality of pandoc-pyplot in a Haskell package, you can use the makePlot :: Block -> IO Block function (for single blocks) or plotTransform :: Pandoc -> IO Pandoc function (for entire documents). Variations of these functions exist for more advanced configurations. Take a look at the documentation on Hackage.

Usage with Hakyll

This filter was originally designed to be used with Hakyll. In case you want to use the filter with your own Hakyll setup, you can use a transform function that works on entire documents:

import Text.Pandoc.Filter.Pyplot (plotTransform)

import Hakyll

-- Unsafe compiler is required because of the interaction
-- in IO (i.e. running an external Python script).
makePlotPandocCompiler :: Compiler (Item String)
makePlotPandocCompiler =
  pandocCompilerWithTransformM
    defaultHakyllReaderOptions
    defaultHakyllWriterOptions
    (unsafeCompiler . plotTransform)

The plotTransformWithConfig is also available for a more configurable set-up.

Warning

Do not run this filter on unknown documents. There is nothing in pandoc-pyplot that can stop a Python script from performing evil actions.

pandoc-pyplot's People

Contributors

laurentrdc avatar

Stargazers

 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

pandoc-pyplot's Issues

Limited to matplotlib

Hello!

Let me first thank you for such an amaizing filter!!! I was looking especially for this kind of functionality and run into this repo.

But the only disadvantage of the approach you are using is that with this filter I am limited to matplotlib and can't use any other, like Plotly. Matplotlib is great, but it lacks some features and it would be awesome to add support for other libraries, too.

Instead of adding support for each library individually, I suggest to implement more general functionality (I can't make a PR as I do not know Haskell yet) by providing exporter argument that should be a function that takes filename and does whatever stuff that should be done to save figure. For matplotlib it will look something like following:

```{.pyplot type="svg" exporter="lambda filename: plt.savefig(filename)')"}
plt.figure()
plt.plot([0,1,2,3,4], [1,2,3,4,5])
plt.title('This is an example figure')
```

This approach allows you to use any library and any format that you want:

```{.pyplot type="html" exporter="lambda filename: py.offline.plot(hg_hist_fig, filename=filename"}
import plotly as py
import scipy as sp

hg = sp.stats.hypergeom(xi.N,xi.m,xi.n)
hg_hist_x = range(*map(int, sp.stats.hypergeom.interval(1, 80, 15, 20)))
hg_hist_fig = go.Figure(,
    data=(go.Scatter(,
        name='histogram',
        x=list(hg_hist_x),
        y=list(map(hg.pmf, hg_hist_x)),
        mode='markers',
    ),),
    layout=go.Layout(,
        title=go.layout.Title(,
            text=r'$$\\xi \\sim HG(80, 15, 20)$$',
            x=.5,
        ),
        yaxis=go.layout.YAxis(,
            title=go.layout.yaxis.Title(,
                text=r'$$\\mathbb{P}(\\xi=k)$$',
            ),
        ),
        xaxis=go.layout.XAxis(,
            title=go.layout.xaxis.Title(,
                text=r'$$k$$',
            ),
        ),
    ),
)
```

In this example we embed html in final document (it may me useful for converting your markdown to html and then to pdf).

P.S.: Take a look at this repo it may contain some great ideas as well.

Add Additional Parameters for saveFig

Sometimes you'd like to be able to pass in additional parameters to saveFig. The usual one here, for me at least, is bbox_inches='tight' to get around issues with plots being cut off. Here's one example on SO to that effect.

It would be nice if, like dpi, there were configuration options for most (or all?) of the parameters that a user might want to pass to saveFig.

I don't think there's currently a way to do this, judging by how the saveFig call is constructed.

Unrelated, thanks for this library. ๐Ÿ‘ Making the transition from "LaTeX + Makefiles running miscellaneous figure scripts" to Pandoc a lot easier.

[FEATURE REQUEST]: configuration and include scripts in YAML metadata block

Hello again :)

It is possible to read metadata of document within filter. It is much more convenient to define configurations for this filter in YAML metadata block within markdown document that is going to be processed. Moreover, pandoc has support for providing this information in separate file. This is inconvenient to have another file especially for this filter.

It will be convenient to add include scripts to this YAML metadata block as well.

So, the final example will look as following (with #6 implemented):

---
title: How to model Hypergeometric Distribution?
pyplot_interpreter: python3
pyplot_format: svg
pyplot_links: false
pyplot_includes:
  plotly: |
    ```
    import plotly as py
    import plotly.graph_objs as go
    ```
  hg: |
    ```
    import scipy as sp

    # HG implements hypergeometric distribution
    class HG(object):
        def __init__(self, N: int, m: int, n: int):
            self.N = N
            self.m = m
            self.n = n

        def p(self, k: int) -> float:
            return sp.special.comb(self.m, k) \
                   * sp.special.comb(self.N - self.m, self.n - k) \
                   / sp.special.comb(self.N, self.n)

        def __str__(self) -> str:
            return f'HG({self.N}, {self.m}, {self.n})'
    
    xi = HG(30, 15, 20)
    hist_data_x = np.arange(xi.n+1)
    ```
---

# Try plotly

<!-- In this code block I need only to include `mpl` 
as I do not use any things from `data`:-->
```{.pyplot includes="plotly"}
fig = go.Figure(
    data=(go.Scatter(
        x=list(range(10)),
        y=list(map(lambda x: x**3, range(10))),
        mode='markers',
    ),)
)
```

# Histogram

Here is a histogram of $HG(30,15,20)$:
```{.pyplot includes="plotly,hg"}
hg_hist_fig = go.Figure(
    data=(go.Scatter(
        x=list(hist_data_x),
        y=list(map(xi.p, hist_data_x)),
        mode='markers',
    ),),
    layout=go.Layout(
        title=go.layout.Title(
            text=r'$\xi \sim ' + str(xi) + '$',
            x=.5,
        ),
        yaxis=go.layout.YAxis(title=go.layout.yaxis.Title(
            text=r'$\mathbb{P}(\xi=k)$',
        )),
        xaxis=go.layout.XAxis(title=go.layout.xaxis.Title(
            text=r'$k$',
        )),
    ),
)
```

Also, it will be very nice if I can include scripts that were already in document and reference them by their name without executing them separately. Look at this example:

# Modelling

Let's firstly define class `HG` which would contain information about
parameters $N$, $m$ and $n$ and method `p(k)` for calculating
the probability of event when $\xi = k$:
```{.python .pyplot .noExec name="class"}
import scipy as sp

class HG(object):
    def __init__(self, N: int, m: int, n: int):
        self.N = N
        self.m = m
        self.n = n

    def p(self, k: int) -> float:
        return sp.special.comb(self.m, k) \
               * sp.special.comb(self.N - self.m, self.n - k) \
               / sp.special.comb(self.N, self.n)

    def __str__(self) -> str:
        return f'HG({self.N}, {self.m}, {self.n})'
```
Then create object of random variable $\xi \sim HG(30, 15, 20)$:
```{.python .pyplot .noExec name="xi"}
xi = HG(30, 15, 20)
```
Next step is to define interval $\overline{0, n}$ for $k$ where we will draw our histogram:
```{.python .pyplot .noExec name="data"}
import numpy as np

hist_data_x = np.arange(xi.n+1)
```
Finally, draw the plot:
```{.python .pyplot .noExec name="plot"}
import plotly
import plotly.graph_objs as go
hg_hist_fig = go.Figure(
    data=(go.Scatter(
        x=list(hist_data_x),
        y=list(map(xi.p, hist_data_x)),
        mode='markers',
    ),),
    layout=go.Layout(
        title=go.layout.Title(
            text=r'$\xi \sim ' + str(xi) + '$',
            x=.5,
        ),
        yaxis=go.layout.YAxis(title=go.layout.yaxis.Title(
            text=r'$\mathbb{P}(\xi=k)$',
        )),
        xaxis=go.layout.XAxis(title=go.layout.xaxis.Title(
            text=r'$k$',
        )),
    ),
)
```
The result would be following:
```{.pyplot include="class,xi,data,plot"}
# I do not have to copy-paste anything here.
```

It would be awesome if you can implement this!

Problem with pandoc 2.8

I'm getting the following error, when using pandoc-pyplot as a filter:

pandoc-pyplot: Error in $: Incompatible API versions: encoded with [1,20] but attempted to decode with [1,17,5,4].
CallStack (from HasCallStack):
  error, called at .\Text\Pandoc\JSON.hs:111:48 in pandoc-types-1.17.5.4-1YIMZftZkkF55zFQfWBdlu:Text.Pandoc.JSON
Error running filter pandoc-pyplot:
Filter returned error status 1
make: *** [Makefile:89: result.pdf] Error 83

pandoc-pyplot: 2.2.0.0
Pandoc version: 2.8
System: Windows 10 Pro x64, Polish language

[FEATURE REQUEST]: store state between executions of blocks

How about storing state of python objects between executions of different blocks?

The idea is following: imagine we have two code blocks and in the second one we want to have variable available, which was defined in previous block. At the moment each code block executes in isolated (in terms of global objects available) python enviroment. So, if you are unable to do the following:

# First
```{.pyplot capture="a,b"}
import matplotlib.pyplot as plt

a = list(range(5))
b = list(range(10))

plt.figure()
plt.plot(a, list(map(lambda x: x**2, a)))
plt.title('This is an example figure')
```
# Second
```{.pyplot needs="a,b"}
import matplotlib.pyplot as plt

plt.figure()
plt.plot(a, list(map(lambda x: x**3, a)))
plt.plot(a, list(map(lambda x: x**2, b)))
plt.title('This is an example figure')
```

Sure, we can simply copy-paste a = list(range(5)) and b = list(range(10))or put it in separate file and include each time we want to have it available in our code. But this could result in overhead in performance if it something harder than list(range(10)). For example, we might need to preprocess out data, show the density histogram, then normalize the preprocessed data and show new density plot.

I can think of following approach:

Firstly, we need to launch only one instance of python process while filter is working. And feed it with our scripts. It should store our variables and function definitions and they will persist in memory during work of filter. Look how it could be achieved with bash:

# generate script files
for ((i = 0 ; i < 10 ; i++)); do echo "print($i)" > "file${i}.py"; done

# feed them to python
for ((i = 0 ; i < 10 ; i++)); do cat "file${i}.py"; done | python3

Secondly, I don't know yet how, but it would be cool if we can externally provide python process to this filter to maintain state between running of pandoc with this filter. Consider this example:

# Example
Here is only one plot
```{.pyplot capture="a"}
import matplotlib.pyplot as plt

a = list(range(5)) # there will be much harder task in real-life example
plt.figure()
plt.plot(a, list(map(lambda x: x**3, a)))
plt.title('This is an example figure')
```
Here a two plots:
```{.pyplot needs="a"}
import matplotlib.pyplot as plt

b = list(range(10))

plt.figure()
plt.plot(a, list(map(lambda x: x**3, a)))
plt.plot(a, list(map(lambda x: x**2, b)))
plt.title('This is an example figure')
```

Then I convert it with pandoc:

$ pandoc --filter pandoc-pyplot -t html5 example.md

And imagine if I slightly change the code:

  Here a two plots:
  ```{.pyplot needs="a"}
  import matplotlib.pyplot as plt
  
  b = list(range(10))

  plt.figure()
- plt.plot(a, list(map(lambda x: x**3, a)))
+ plt.plot(a, list(map(lambda x: x**4, a)))
  plt.plot(a, list(map(lambda x: x**2, b)))
  plt.title('This is an example figure')
  ```

If I try to convert it again, first code block will have to be executed as well:

$ pandoc --filter pandoc-pyplot -t html5 example.md

Maybe we can borrow some ideas from here:

dir=`mktemp -d /tmp/temp.XXX`
keep_pipe_open=$dir/keep_pipe_open
pipe=$dir/pipe

mkfifo $pipe
touch $keep_pipe_open

# Read from pipe:
python3 < $pipe &

# Keep the pipe open:
while [ -f $keep_pipe_open ]; do sleep 1; done > $pipe &

# Write to pipe:
for ((i = 0 ; i < 10 ; i++)); do cat "file${i}.py"; done > $pipe

# close the pipe:
rm $keep_pipe_open
wait

rm -rf $dir

Error in $: Failed reading: not a valid json value

Hey there,

I've been trying to run the example from usage, but it's throwing an error:

$ pandoc --filter pandoc-pyplot input.md --output output.pdf
Error running filter pandoc-pyplot:
Error in $: Failed reading: not a valid json value

Any idea what might cause this?

My system is x86_64 Arch Linux with pandoc version 2.7.2. I've installed pandoc-pyplot via stack.

Thanks in advance.

An error breaks the installation of pandoc-pyplot

when i install pandoc-pyplot with cabal install, i catch an error:
`
[1 of 3] Compiling Text.Pandoc.Filter.Scripting ( src/Text/Pandoc/Filter/Scripting.hs, dist/build/Text/Pandoc/Filter/Scripting.o )

src/Text/Pandoc/Filter/Scripting.hs:45:50: error:
* Variable not in scope:
(<>) :: m0 ExitCode -> String -> IO ExitCode
* Perhaps you meant one of these:
</>' (imported from System.FilePath), <$>' (imported from Prelude), *>' (imported from Prelude) Perhaps you want to add <>' to the import list in the import of
`Data.Monoid' (src/Text/Pandoc/Filter/Scripting.hs:27:1-37).

src/Text/Pandoc/Filter/Scripting.hs:57:32: error:
* Variable not in scope: (<>) :: [Char] -> String -> t0
* Perhaps you meant one of these:
</>' (imported from System.FilePath), <$>' (imported from Prelude), *>' (imported from Prelude) Perhaps you want to add <>' to the import list in the import of
`Data.Monoid' (src/Text/Pandoc/Filter/Scripting.hs:27:1-37).

src/Text/Pandoc/Filter/Scripting.hs:57:46: error:
* Variable not in scope: (<>) :: t0 -> [Char] -> PythonScript
* Perhaps you meant one of these:
</>' (imported from System.FilePath), <$>' (imported from Prelude), *>' (imported from Prelude) Perhaps you want to add <>' to the import list in the import of
Data.Monoid' (src/Text/Pandoc/Filter/Scripting.hs:27:1-37). cabal: Leaving directory '.' cabal: Error: some packages failed to install: pandoc-pyplot-1.0.2.0 failed during the building phase. The exception was: ExitFailure 1

on Ubuntu 18.04 gcc-7 ghc-8.0.2 cabal-install version 1.24.0.2

Support for interactive Plotly figures when possible

Based on a comment in #4 :

Pandoc filters only work on the intermediate document representation.

As I see here, getting the output format it possible (I am not sure, because I don't know Haskell)

The idea is to support interactive Plotly plots (via HTML), but fall back to static images when the output format is not HTML, (e.g. PDF)

From Pandoc toJSONFilter documentation:

An alternative is to use the type Maybe Format -> a -> a. This is appropriate when the first argument of the script (if present) will be the target format, and allows scripts to behave differently depending on the target format. The pandoc executable automatically provides the target format as argument when scripts are called using the --filter option.

[FEATURE REQUEST]: Include multiple scripts

This filter has a nice option to include a file before the code:

# file.py
x = list(range(5))
# Include in markdown
```{.pyplot include=file.py}
import matplotlib.pyplot as plt

plt.figure()
plt.plot(x, list(map(lambda x: x**2, x)))
plt.title('This is an example figure')
```

But often it may be useful to include more than one script:

# file1.py
x = list(range(5))
# file2.py
y = list(map(lambda a: a**2, x))
# Include multiple files in markdown
```{.pyplot include=file1.py include=file2.py}
import matplotlib.pyplot as plt

plt.figure()
plt.plot(x, y)
plt.title('This is an example figure')
```

If pandoc does not allow repeating attributes (include=file1.py include=file2.py) then it can be achieved with include="file1.py,file2.py"

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.