Skip to content

Showcase example: (kind of mandatory) Mandelbrot set #7447

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 16, 2016

Conversation

rougier
Copy link
Member

@rougier rougier commented Nov 12, 2016

This is a rendering of the Mandelbrot set. I think it is reasonably good looking (using normalized recount, adapted normalized colormap and shading).

mandelbrot

@stonebig
Copy link
Contributor

stonebig commented Nov 12, 2016

I get these error message testing under python-3.6.0b3, don't know if it's important:

C:/WinPython/basedir36/build/winpython-64bit-3.6.x.0/notebooks/mandelbrot.py:41: RuntimeWarning: invalid value encountered in log
M = np.nan_to_num(N + 1 - np.log(np.log(abs(Z)))/np.log(2) + log_horizon)
C:\WinPython\basedir36\build\winpython-64bit-3.6.x.0\python-3.6.0b3.amd64\lib\site-packages\matplotlib\font_manager.py:1288: UserWarning: findfont: Font family ['Source Sans Pro Light'] not found. Falling back to Bitstream Vera Sans
(prop.get_family(), self.defaultFamily[fontext]))

image seems generated ok anyway

.... oups! you warned of the error in comment.. sorry for the noise

@rougier
Copy link
Member Author

rougier commented Nov 12, 2016

@stonebig I removed the second warning (font specification).

@tacaswell tacaswell added this to the 2.0.1 (next bug fix release) milestone Nov 12, 2016
# Some advertisement for matplotlib
ax.text(xmin+0.025, ymin+0.025,
"The Mandelbrot fractal set\n"
"Rendered with matplotlib 2.0, 2016 — http://www.matplotlib.org",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamically fetch the version and the year here, perhaps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good point.

@NelleV
Copy link
Member

NelleV commented Nov 12, 2016

Hi Nicolas,

Can you also please add a docstring with a title and a description of the example? It should be formatted as follow:

"""
===============================
Colormaps alter your perception
===============================

Here I plot the function

.. math:: f(x, y) = \sin(x) + \cos(y)

with different colormaps. Look at how colormaps alter your perception!
"""

@rougier
Copy link
Member Author

rougier commented Nov 13, 2016

Hi Nelle,

Sure.

return Z, N


if __name__ == '__main__':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this conditional block is necessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't hurt and allow to import mandelbrot if you want to experience with it.

Z, N = mandelbrot_set(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon)

# Normalized recount as explained in:
# http://linas.org/art-gallery/escape/smooth.html
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https


# This line will generate warnings for null values but it is faster to
# process them afterwards using the nan_to_num
M = np.nan_to_num(N + 1 - np.log(np.log(abs(Z)))/np.log(2) + log_horizon)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with np.errstate(invalid='ignore'):
    M = np.nan_to_num(...)

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

# process them afterwards using the nan_to_num
M = np.nan_to_num(N + 1 - np.log(np.log(abs(Z)))/np.log(2) + log_horizon)

dpi = 72
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is changing dpi important?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More or less. I wanted to be sure of the approximate pixel resolution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand. The dataset is 3000 samples wide (xn) and the figure is 10 inches wide (width), giving an effective resolution of 300 dpi (if corresponding 1-to-1.) With dpi=72, the data needs to be resampled at a ratio of 25:6 = ~4.166667, which seems odd.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, just realized I missed the /2, so it's really 1500 samples wide with effective resolution of 150 dpi, and resampling ratio of 25:12 = ~2.083333. But in that case, how about making it exactly 2 by setting dpi=75?


# Shaded rendering
light = colors.LightSource(azdeg=315, altdeg=10)
M = light.shade(M, cmap=plt.cm.hot, vert_exag=1.5,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use our fancy new colour maps (magma, inferno, or plasma)? I'm not sure how well they work with shading though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried but output was not as nice as with the hot colormap.


# Some advertisement for matplotlib
year = time.strftime("%Y")
major, minor, micro = matplotlib.__version__.split('.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You never know what might be in the local version identifier, so you should add maxsplit=3 here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know about this one, thanks.

% (major, minor, year))
ax.text(xmin+.025, ymin+.025, text, color="white", fontsize=12, alpha=0.5)

# plt.savefig("mandelbrot.png")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.


# Some advertisement for matplotlib
year = time.strftime("%Y")
major, minor, micro = matplotlib.__version__.split('.', maxsplit=3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, got the number wrong; should be 2.

@NelleV NelleV changed the title Showcase example: (kind of mandatory) Mandelbrot set [MRG+1] Showcase example: (kind of mandatory) Mandelbrot set Nov 15, 2016
@NelleV
Copy link
Member

NelleV commented Nov 15, 2016

Thanks @rougier ! This looks goot to me!

@tacaswell tacaswell merged commit 631e079 into matplotlib:master Nov 16, 2016
tacaswell added a commit that referenced this pull request Nov 16, 2016
DOC: Showcase example: (kind of mandatory) Mandelbrot set
@tacaswell
Copy link
Member

backported to v2.x as 1209e70

Thanks @rougier

@QuLogic QuLogic changed the title [MRG+1] Showcase example: (kind of mandatory) Mandelbrot set Showcase example: (kind of mandatory) Mandelbrot set Nov 16, 2016
@jniediek
Copy link

I think there are several bugs in this example, which make it impossible to run it with python2.7, which breaks the documentation build process.

As I'm unfamiliar with matplotlib's policy for examples, I'll just list here what I consider to be bugs (and fixes):

python2.7 mandelbrot.py
File "mandelbrot.py", line 71
SyntaxError: Non-ASCII character '\xe2' in file mandelbrot.py on line 71, but no encoding declared;     see http://python.org/dev/peps/pep-0263/ for details

Adding # -*- coding: utf-8 -*- as first line:

python2.7 mandelbrot.py 
Traceback (most recent call last):
File "mandelbrot.py", line 70, in <module>
major, minor, micro = matplotlib.__version__.split('.', maxsplit=2)
TypeError: split() takes no keyword arguments

Changing to

major, minor, micro = matplotlib.__version__.split('.')[:3]

And changing the next line to

text = ("The Mandelbrot fractal set\n"
        "Rendered with matplotlib %s.%s, %s — http://matplotlib.org".decode('utf-8')
        % (major, minor, year))

It finally works with python2.7.

I have a feeling that using six and from __future__ import would be appropriate here, but as I said, I'm unfamiliar with the policies.

@tacaswell
Copy link
Member

@jniediek already fixed: #7473

I strongly encourage you to move to python3 as soon as possible.

See https://python-3-for-scientists.readthedocs.io/en/latest/ and https://python3statement.github.io/

TLDR: You get nice stuff for switching and you are going start losing access to new versions of libraries in 2018-2020 time frame.

@QuLogic QuLogic modified the milestones: 2.0.1 (next bug fix release), 2.0 (style change major release) Dec 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants