Coder Social home page Coder Social logo

mplcyberpunk's Introduction

mplcyberpunk

Latest PyPI version Build Status Python 3.9

A Python package on top of matplotlib to create 'cyberpunk' style plots with 3 additional lines of code.

Installation

pip install mplcyberpunk

Usage

After importing the package, the cyberpunk stylesheet (dark background etc.) is available via plt.style.use. The line glow and 'underglow' effects are added via calling add_glow_effects:

    import matplotlib.pyplot as plt
    import mplcyberpunk

    plt.style.use("cyberpunk")

    plt.plot([1, 3, 9, 5, 2, 1, 1], marker='o')
    plt.plot([4, 5, 5, 7, 9, 8, 6], marker='o')

    mplcyberpunk.add_glow_effects()

    plt.show()

Result:

This effect is currently only implemented for lines.

The individual steps are described here in more detail.

Add effects individually

Instead of add_glow_effects, you can add the line glow and underglow effects separately:

mplcyberpunk.make_lines_glow()
mplcyberpunk.add_underglow()

You can also add the effect to a specific axis object explicitly:

fig, ax = plt.subplots()
...
mplcyberpunk.make_lines_glow(ax)

To activate the glow effect only for specific lines, pass a Line2D object or a list of Line2Ds to make_lines_glow.

Gradient glow

Gradient underglow effect can be added with

mplcyberpunk.add_glow_effects(gradient_fill=True)

or independently of line glow with

mplcyberpunk.add_gradient_fill(alpha_gradientglow=0.5)

add_gradient_fill takes a gradient_start argument for different gradient starting values:

gradient_start

Different glow configurations:

underglows

Scatter plots

Glow effect can be added to scatter plots via mplcyberpunk.make_scatter_glow():

Colormap

The default colormap is cool:

colormap

Others:

colormaps

Bar charts

import matplotlib.pyplot as plt
import mplcyberpunk

plt.style.use('cyberpunk')

categories = ['A', 'B', 'C', 'D', 'E']
values = [25, 67, 19, 45, 10]
colors = ["C0", "C1", "C2", "C3", "C4"]

bars = plt.bar(categories, values, color=colors, zorder=2)

mplcyberpunk.add_bar_gradient(bars=bars)

plt.show()

colormap

Gallery

europe world

gradient-fill_1 gradient-fill_2

Some images can be bought as posters here.

Requirements

Depends only on matplotlib.

mplcyberpunk's People

Contributors

cedrichol avatar dhaitz avatar diceroll123 avatar justxd22 avatar mitch-ml avatar story645 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

mplcyberpunk's Issues

how to turn on the grid?

First and foremost - AWESOME project.
Working in data-analysis world I'm always missing some aesthetics to spice up the work.

However I'm trying to get closer to examples displayed on readme.md and I can't find any elegant way to show the grid. I can add it manually of course but the examples are so well balanced I wish I could achieve the same. Also what size and DPI were used for achieving this beauty?
And again, thank you for taking care of something missing in this world ;).

Fill gradients for log axes

This is really low priority, but for log-data the gradient fades out quickly, as it is a linear gradient. It looks pretty cool but an option for a log-gradient would be a nice touch.

PS. this style is so killer...

Proposal for a new histogram function

Hello,

I'm happy to share a new cyberpunk histogram style I've created. Feel free to give me any feedback.

import numpy as np
import mplcyberpunk
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from typing import Optional, Union, Tuple

# Define the gradient histogram function
def add_gradient_bar_glow(ax: Optional[plt.Axes] = None, bars=None) -> None:
    
    # Number of shades for the gradient
    n_shades = 150  

    # Get the current axis if no axis is provided
    if not ax:
        ax = plt.gca()

    # Loop through each bar in the bar plot
    for bar in bars:

        # Get the x and y coordinates of the bar
        bar_x, bar_y = bar.get_xy()

        # Get the width and height of the bar
        bar_width, bar_height = bar.get_width(), bar.get_height()

        # Get the fill color of the bar
        fill_color = bar.get_facecolor()

        # Convert the fill color to RGB format
        rgb = mcolors.colorConverter.to_rgb(fill_color)

        # Calculate the height step for the gradient
        height_step = bar_height / n_shades

        # Loop through each shade in the gradient
        for i in range(n_shades):

            # Calculate the alpha value for the current shade
            alpha = (i + 1) / n_shades

            # Calculate the height of the current shade
            current_height = height_step * i

            # Create a gradient rectangle for the current shade
            gradient_rect = plt.Rectangle((bar_x, bar_y + current_height), bar_width, height_step,
                                          linewidth=0, alpha=alpha, color=fill_color, zorder=bar.get_zorder() - 1)

            # Add the gradient rectangle to the plot
            ax.add_patch(gradient_rect)

        # Make the original bar transparent
        bar.set_alpha(0)

# Use a cyberpunk background style
plt.style.use("cyberpunk")

# Generate colors for the bars
colors = ["#08F7FE", "#FE53BB", "#F5D300", "#00ff41", "#FF2C55"]

# Sample data
categories = ['A', 'B', 'C', 'D', 'E']
values = [10, 45, 12, 67, 10]

# Create bar plot
bars = plt.bar(categories, values, color=colors, zorder=2)

# Add gradient glow inside bars
add_gradient_bar_glow(bars=bars)

# Add labels and title
plt.xlabel("Products")
plt.ylabel("Total Sales")
plt.title("Gradient Glow Bar Plot")

# Add grid with custom color
plt.grid(color='#2A3459')
plt.show()

Best wishes,
Maxime Jabarian

`make_scatter_glow` picks wrong collection and also does not update all scatters with correct color

Hello,

I'm reporting this for Python 3.9.13 with matplotlib 3.8.1

Problem Description

I tried to run the command make_scatter_glow on a plot where I plotted a few scatter points before I also added lines, a fill_between, and many more. When I ran make_scatter_glow at the end, I got the following error:

      File "...", line 222, in make_scatter_glow
        x, y = scatterpoints.get_offsets().data.T
    AttributeError: 'memoryview' object has no attribute 'T'

Besides, I noticed that it only makes the first scatter series glow (if it detects one) and that the color of the glow does not match the color of the scatter series if it does not originate from a colormap, but a static color. When I get the plotting of multiple series right, it still looks like:
grafik
for

# Imports
import numpy as np
import matplotlib.pyplot as plt

# Create a figure and axes
fig, ax = plt.subplots()
# scattering three random series
_ = ax.scatter(x=[1, 2, 3], y=[1, 2, 3]) # has default blue glow and thus works out by first look
_ = ax.scatter(x=[1, 2, 3], y=[3, 2, 1]) # glow did not show at first; when fixed the glow was blue as well
_ = ax.scatter( # glow did not show at first; when fixed the glow had the correct colors for each point
    x = np.random.rand(10),
    y = np.random.rand(10),
    c = np.arange(10),
)

Suspected Cause

I'm not 100% familiar with Axes.collection in matplotlib, but I think the error is coming from the way how the collection is picked because it by default takes the last one. I marked the problematic line together with the ones causing the wrong color and the missing glows with a ⚠️.

def make_scatter_glow(
    ax: Optional[plt.Axes] = None,
    n_glow_lines: int = 10,
    diff_dotwidth: float = 1.2,
    alpha: float = 0.3,
) -> None:
    """Add glow effect to dots in scatter plot.

    Each plot is redrawn 10 times with increasing width to create glow effect."""
    if not ax:
        ax = plt.gca()

    scatterpoints = ax.collections[-1] # <-- ⚠️ I ASSUME THIS CAUSES THE ISSUE OF THE CRASH OF THE NEXT LINE
    x, y = scatterpoints.get_offsets().data.T
    dot_color = scatterpoints.get_array() # <-- ⚠️ THIS RETURNS None IF NOT FROM A COLORMAP AND THUS CAUSES WRONG COLOR
    dot_size = scatterpoints.get_sizes()

    alpha = alpha/n_glow_lines

    for i in range(1, n_glow_lines):
        plt.scatter(x, y, s=dot_size*(diff_dotwidth**i), c=dot_color, alpha=alpha)
        # ⚠️ THIS DOES NOT SEEM TO PLOT ANY SERIES BUT THE FIRST ONE (IF ONE CAN BE DETECTED)

Proposed Fix

I could work out a solution that first collects the information of all scatter series and then plots the glows one by one:

def make_scatter_glow(
    ax: Optional[plt.Axes] = None,
    n_glow_lines: int = 10,
    diff_dotwidth: float = 1.2,
    alpha: float = 0.3,
) -> None:
    """Add glow effect to dots in scatter plot.

    Each plot is redrawn 10 times with increasing width to create glow effect."""
    if not ax:
        ax = plt.gca()


    # first, the details of each scatter series are stored in a list
    alpha = alpha / n_glow_lines
    scatter_series = []
    for collection in ax.collections:
        if isinstance(collection, mpl.collections.PathCollection): # True for scatter series
            dot_colors = collection.get_array()
            if dot_colors is None: # the "if not" does not work because this can be a NumPy-Array which has no such comparison
                dot_colors = collection.get_facecolors()
                
            scatter_details = {
                'offsets': collection.get_offsets().data.T,
                'dot_colors': dot_colors,
                'sizes': collection.get_sizes()
            }
            scatter_series.append(scatter_details)

    # then, the scatter series are augmented by the glow effect
    for series in scatter_series:
        x, y = series['offsets']
        dot_colors = series['dot_colors']
        dot_size = series['sizes']

        # Apply glow
        for i in range(0, n_glow_lines):
            ax.scatter(x, y, s=dot_size*(diff_dotwidth**i), c=dot_colors, alpha=alpha)

Now, with this function, there are no crashes even though multiple collections are present (lines, scatter, etc. at arbitrary order), the color is correct, and all glows are applied.
grafik

I did not start a pull request since matplotlib details are a bit outside my domain 😅

Support Step plots

Step plots would be nice to have working with underglow.

image

I may get around to making a PR for this when I'm free to do so, but I don't know when I'd get around to it. In any case, or if anyone else were up for it, for now I'll make this issue.

ModuleNotFoundError: No module named 'pkg_resources'

Traceback (most recent call last):
  File "C:\Users\Raj Dave\Desktop\New folder\main.py", line 5, in <module>
    import mplcyberpunk
  File "C:\Users\Raj Dave\AppData\Local\Programs\Python\Python312\Lib\site-packages\mplcyberpunk\__init__.py", line 4, in <module>
    import pkg_resources
ModuleNotFoundError: No module named 'pkg_resources'

python 3.12

happens when I'm trying to import it

grid toggle side effect

Using the theme toggles the grid, not really a bug, but something users should be aware of when putting in those lines to make the effects work.

Put differently, I used the style and wondered where my grid had gone.

Multiple problems involving `ax.axhline` with mplcyberpunk

Consider the following code:

plt.style.use("cyberpunk")
ax = plt.gca()

ax.axhline(y=1, color="#C20000")  # draw a red line across the X axis, y=1
ax.step(range(7), [0, 1, 2, 3, 2, 1, 0], marker="o")
mplcyberpunk.add_glow_effects()

plt.show()

image

The area filled in with red underneath the blue is inconsistent, showing where x <= 1, I'd expect to see it at x >= 5 here.

That wasn't even why I started writing the issue, I found it while making my test case to showcase the error I found -- but I figured I'd bring it up.


The initial reason I was making an issue was because using ax.axhline with mplcyberpunk.add_glow_effects(gradient_fill=True) throws an AttributeError.

plt.style.use("cyberpunk")
ax = plt.gca()

ax.axhline(y=1, color="#C20000")  # draw a red line across the X axis, y=1
ax.step(range(7), [0, 1, 2, 3, 2, 1, 0], marker="o")
mplcyberpunk.add_glow_effects(gradient_fill=True) # this is the only line changed from the above example

plt.show()

Traceback:

Traceback (most recent call last):
  File "test.py", line 17, in <module>
    main()
  File "test.py", line 12, in main
    mplcyberpunk.add_glow_effects(gradient_fill=True)
  File ".../mplcyberpunk/core.py", line 18, in add_glow_effects
    add_gradient_fill(ax=ax)
  File ".../mplcyberpunk/core.py", line 126, in add_gradient_fill
    xmin, xmax = x.min(), x.max()
AttributeError: 'list' object has no attribute 'min'

This is because the line is a list of tuples, rather than a numpy array.

The tests for mplcyberpunk could be more strict I suppose.

Glow effect may mutate tick labels

Without calling mplcyberpunk.add_glow_effects():

Screenshot from 2020-03-30 20-17-36

With calling mplcyberpunk.add_glow_effects():

Screenshot from 2020-03-30 20-17-58

The tick label text changed unexpectedly. Let me know if you need more detail w.r.t. context.

Found an error in when dealing with step plots

Hi! Your current module does only support step plots with the variable where = ''pre" (which is the default value, as indicated in the documentation. I've changed the code so that the user can input the parameter themselves in order to fix it, but this is not the optimal solution. Could you fix that in the next version?

def make_lines_glow(ax=None, n_glow_lines=10, diff_linewidth=1.05, alpha_line=0.3, where=None):
"""Add a glow effect to the lines in an axis object.

            Each existing line is redrawn several times with increasing width and low alpha to create the glow effect.
            """
            if not ax:
                ax = plt.gca()
        
            lines = ax.get_lines()
        
            alpha_value = alpha_line / n_glow_lines
        
            for line in lines:
                data = line.get_data()
                linewidth = line.get_linewidth()
                
                try:
                    step_type = line.get_drawstyle().split('-')[1]
                except:
                    step_type = None
        
                for n in range(1, n_glow_lines + 1):
                    if step_type:
                        # Use the 'where' parameter if it's provided
                        if where is not None:
                            glow_line, = ax.step(*data, where=where)
                        else:
                            glow_line, = ax.step(*data)
                    else:
                        glow_line, = ax.plot(*data)
                    glow_line.update_from(line)
        
                    glow_line.set_alpha(alpha_value)
                    glow_line.set_linewidth(linewidth + (diff_linewidth * n))
                    glow_line.is_glow_line = True

`

Suggestion: lotting individual points

When plotting individual points in matplotlib, e.g:

plt.plot(x, y, marker="x")
mplcyberpunk.add_glow_effects(gradient_fill=True)
plt.show()

The module returns three warnings:

\mplcyberpunk\core.py:172: RuntimeWarning: divide by zero encountered in divide
  moment = lambda y : (scaler(y)-scaler(ya)) / (scaler(yb)-scaler(ya))
\mplcyberpunk\core.py:172: RuntimeWarning: invalid value encountered in divide
  moment = lambda y : (scaler(y)-scaler(ya)) / (scaler(yb)-scaler(ya))
\mplcyberpunk\core.py:186: UserWarning: Attempting to set identical low and high xlims makes transformation singular; automatically expanding.
  im = ax.imshow(z,

Would it be possible for compatibility with single point plots to be implemented, with the same effects as the make_scatter_glow() function?

Savefig saves only background color

Is there a fix for it? I'd like to be able to save my figs in higher dpi. But for some reason it only saves as a dark rectangle of the background color.

Suggestion: have the gradient fill mimic the underglow fill

As in, have the effect go all the way to the bottom of the chart. See below:

IMPORTANT: for the sake of this argument, the Y axis of the line in question never reaches the minimum Y value of the chart, which for me in the examples below is 0.

With underglow:
image


With gradient:
image

Unsure on the feasibility of this, though.

How to change colormaps?

It's written that we can use 5 colormaps (cool, spring, winter, tab20, coolwarm), but I cannot understand how to change it.
Could you help with it, please?

Installing from the sdist doesn't work

I'm currently in the process of packaging this into a conda-forge package. There, we prefer to build/install from the sdist on PyPI. Sadly, this runs into the following hurdles:

  1. better-setuptools-git-version doesn't work with Python>=3.10
  2. It actually assumes that it always runs from within a git repository. This is not the case for an sdist. Alternatives like setuptools-scm thus place the "rendered" version into the static metadata.

I'll propose to move to setuptools-scm like most other packages using setuptools and git versioning have done. I'm going to open a PR, but wanted to give more context here first.

Add colormaps

Absolutely loving this stylesheet!

One thing I've found that I wanted to do was use some of the cyberpunk colors as linear colormaps, and basically implement some functions that return LinearSegmentedColormap objects for others to use (example attached), in the same kind of way as how palettable gives access to a bunch of colormaps.

c_ramp

In this example I'm using cyan to violet (which actually makes a good sequential colormap), but some of the others like pink and matrix_green in the stylesheet looks like they'd be good candidates too. To make the plot above, I took the hex codes and converted them into normalized RGB values, and used matplotlib.colors.LinearSegementedColormap.from_list to create them.

If you are open to contributions, I could fork/implement this and submit a PR :)

Not compatible with LaTeX mode.

Dear dhaitz,

Congratulations for the package, it's very nice!

However, it doesn't seem to be compatible with LaTeX mode. For example, when doing:

import matplotlib.pyplot as plt
import mplcyberpunk

from matplotlib import rc
rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
rc('text', usetex=True)

plt.style.use("cyberpunk")

plt.plot([1, 3, 9, 5, 2, 1, 1], marker='o')
plt.plot([4, 5, 5, 7, 9, 8, 6], marker='o')
plt.title(r'$\mathrm{test}$')

mplcyberpunk.add_glow_effects()

plt.show()

The code crashes. It would be nice to update it for scientific uses :)

cheers,
Eduardo

Option for changing colors

Could this feature please be added?

Custom colors for plot and custom gradient colors for graph area

Thankyou

add_underglow not working on errorbars

Hi, just started getting this error when trying to add underglow to an errorbar. Was working fine a few weeks ago.

TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

Full output:
image

Value-vs-text graph gives error

Hi, nice theme

I have integral values on y axis and string on x axis, and when i try to apply your theme, it throws this error.

  File "D:\PycharmProjects\Main_Project\My_progs\card_transaction_management\card_transaction_management.py", line 708, in graph
    plt_theme.add_glow_effects(gradient_fill=True)
  File "D:\PycharmProjects\VENV_project\lib\site-packages\mplcyberpunk\core.py", line 15, in add_glow_effects
    add_gradient_fill(ax=ax)
  File "D:\PycharmProjects\VENV_project\lib\site-packages\mplcyberpunk\core.py", line 123, in add_gradient_fill
    xmin, xmax = x.min(), x.max()
  File "D:\PycharmProjects\VENV_project\lib\site-packages\numpy\core\_methods.py", line 44, in _amin
    return umr_minimum(a, axis, None, out, keepdims, initial, where)
numpy.core._exceptions._UFuncNoLoopError: ufunc 'minimum' did not contain a loop with signature matching types (dtype('<U14'), dtype('<U14')) -> None

I think because x axis has text, it is unable to find minimum and maximum values on this axis (as per error message, line 123 of core.py)

Is there a workaround until it gets fixed?

Using colormaps with plot() function

I'm a real fan of this library but I still cannot understand how to use colormaps (cool, spring, winter, tab20, coolwarm) when I use a plt.plot() function. I want to plot a continuous line like in your examples, that's why I need to use matplotlib.pyplot.plot() function, but the problem is that you cannot pass cmap parameter to plot().
I tried to set a default colormap but couldn't reach it.
I tried to do like this, but it didn't work

`
import matplotlib.pyplot as plt
import numpy as np
import mplcyberpunk

plt.style.use("cyberpunk")

plt.rcParams['image.cmap'] = 'winter'

x = np.linspace(-5, 5, 100)
y = np.sin(x)

plt.plot(x, y)

mplcyberpunk.add_glow_effects()
plt.show()
`
Please, could you write a code to plot a simple continuous function using a colormap?

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.