From e9179c379202d23bbc67bacab7cd05f6e6936a6d Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 14 Dec 2021 13:22:37 +0100 Subject: [PATCH 1/8] DOC: explain too many ticks --- doc/users/faq/howto_faq.rst | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index 0076cb335ab4..9902d20e5d12 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -9,6 +9,68 @@ How-to .. contents:: :backlinks: none + +_how-to-too-many-ticks + +Why do I have so many ticks, and/or why are they out of order? +-------------------------------------------------------------- + +Sometimes Matplotlib will unexpectedly plot with a tick for each data point, +and/or the ticks will be out of numerical order. This is usually a sign that you +have passed in a list of strings rather than a list or array of floats or +datetime objects. This will often happen when reading in a comma-delimited text +file. Matplotlib treats lists of strings as "categorical" variables +(:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default +puts one tick per "category", and plots them in the order in which they are +supplied. + +In the example below, the upper row plots are plotted using strings for *x*; +note that each string gets a tick, and they are in the order of the list passed +to Matplotlib. In the lower row the data is converted to either floats or +datetime64; note that the ticks are now ordered and spaced numerically. + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(2, 2, constrained_layout=True, figsize=(6, 6)) + x = ['1', '5', '2', '3'] + y = [1, 4, 2, 3] + ax[0, 0].plot(x, y, 'd') + ax[0, 0].set_xlabel('Categories') + # convert to numbers: + x = np.asarray(x, dtype='float') + ax[1, 0].plot(x, y, 'd') + ax[1, 0].set_xlabel('Floats') + + x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-10-04'] + y = [0, 2, 3, 1] + ax[0, 1].plot(x, y, 'd') + ax[0, 1].tick_params(axis='x', labelrotation=45) + # convert to datetime64 + x = np.asarray(x, dtype='datetime64[s]') + ax[1, 1].plot(x, y, 'd') + ax[1, 1].tick_params(axis='x', labelrotation=45) + +If *x* has 100 elements, all strings, then we would have 100 (unreadable) +ticks: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(figsize=(6, 2.5)) + x = [f'{xx}' for xx in np.arange(100)] + y = np.arange(100) + ax.plot(x, y) + + .. _howto-figure-empty: Check whether a figure is empty From bda88df5f4aac9de34c10edc1d98e959ca94ce16 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Wed, 15 Dec 2021 10:11:15 +0100 Subject: [PATCH 2/8] DOC: artist extent --- doc/users/faq/howto_faq.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index 9902d20e5d12..38629c0ec271 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -71,6 +71,18 @@ ticks: ax.plot(x, y) +.. _howto-determine-artist-extent: + +Determine the extent of Artists on the Figure +--------------------------------------------- + +Sometimes we want to know the extent of an Artist. Matplotlib `.Artist` objects +have a method `.Artist.get_window_extent` that will usually return the extent of +the artist in pixels. However, some artists, in particular text, must be +rendered at least once before their extent is known. Matplotlib supplies +`.Figure.draw_without_rendering`, which should be called before calling +``get_window_extent``. + .. _howto-figure-empty: Check whether a figure is empty From c8ce36a4e192a64524647750b39aaeaa55962bf4 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Thu, 6 Jan 2022 09:28:37 +0100 Subject: [PATCH 3/8] Update doc/users/faq/howto_faq.rst Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- doc/users/faq/howto_faq.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index 38629c0ec271..e85925eebfe4 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -15,11 +15,10 @@ _how-to-too-many-ticks Why do I have so many ticks, and/or why are they out of order? -------------------------------------------------------------- -Sometimes Matplotlib will unexpectedly plot with a tick for each data point, -and/or the ticks will be out of numerical order. This is usually a sign that you -have passed in a list of strings rather than a list or array of floats or -datetime objects. This will often happen when reading in a comma-delimited text -file. Matplotlib treats lists of strings as "categorical" variables +One common cause for unexpected tick behavior is passing a list of strings +instead of numbers or datetime objects. This can easily happen without notice +when reading in a comma-delimited text file. Matplotlib treats lists of strings +as "categorical" variables (:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default puts one tick per "category", and plots them in the order in which they are supplied. From a8d278d84370341d21261e7d8d5b6827a14fb2e9 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Thu, 6 Jan 2022 09:30:27 +0100 Subject: [PATCH 4/8] Apply suggestions from code review Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- doc/users/faq/howto_faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index e85925eebfe4..52aa73a12df2 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -72,7 +72,7 @@ ticks: .. _howto-determine-artist-extent: -Determine the extent of Artists on the Figure +Determine the extent of Artists in the Figure --------------------------------------------- Sometimes we want to know the extent of an Artist. Matplotlib `.Artist` objects From d3e002566b9f02254dd1d44c3bbc3274adee7bc2 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Thu, 6 Jan 2022 10:49:29 +0100 Subject: [PATCH 5/8] FIX: streamline examples --- doc/users/faq/howto_faq.rst | 68 +++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index 52aa73a12df2..a9407ad8d7f2 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -10,7 +10,7 @@ How-to :backlinks: none -_how-to-too-many-ticks +.. _how-to-too-many-ticks: Why do I have so many ticks, and/or why are they out of order? -------------------------------------------------------------- @@ -25,50 +25,66 @@ supplied. In the example below, the upper row plots are plotted using strings for *x*; note that each string gets a tick, and they are in the order of the list passed -to Matplotlib. In the lower row the data is converted to either floats or -datetime64; note that the ticks are now ordered and spaced numerically. +to Matplotlib. If this is not desired, we need to change *x* to an array of +numbers. .. plot:: :include-source: :align: center - import matplotlib.pyplot as plt - import numpy as np - - fig, ax = plt.subplots(2, 2, constrained_layout=True, figsize=(6, 6)) + fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2.5)) x = ['1', '5', '2', '3'] y = [1, 4, 2, 3] - ax[0, 0].plot(x, y, 'd') - ax[0, 0].set_xlabel('Categories') + ax[0].plot(x, y, 'd') + ax[0].tick_params(axis='x', color='r', labelcolor='r') + ax[0].set_xlabel('Categories') + ax[0].set_title('Ticks seem out of order / misplaced') + # convert to numbers: x = np.asarray(x, dtype='float') - ax[1, 0].plot(x, y, 'd') - ax[1, 0].set_xlabel('Floats') - - x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-10-04'] - y = [0, 2, 3, 1] - ax[0, 1].plot(x, y, 'd') - ax[0, 1].tick_params(axis='x', labelrotation=45) - # convert to datetime64 - x = np.asarray(x, dtype='datetime64[s]') - ax[1, 1].plot(x, y, 'd') - ax[1, 1].tick_params(axis='x', labelrotation=45) + ax[1].plot(x, y, 'd') + ax[1].set_xlabel('Floats') + ax[1].set_title('Ticks as expected') If *x* has 100 elements, all strings, then we would have 100 (unreadable) -ticks: +ticks, and again the solution is to convert the strings to floats: .. plot:: :include-source: :align: center - import matplotlib.pyplot as plt - import numpy as np - - fig, ax = plt.subplots(figsize=(6, 2.5)) + fig, ax = plt.subplots(1, 2, figsize=(6, 2.5)) x = [f'{xx}' for xx in np.arange(100)] y = np.arange(100) - ax.plot(x, y) + ax[0].plot(x, y) + ax[0].tick_params(axis='x', color='r', labelcolor='r') + ax[0].set_title('Too many ticks') + ax[0].set_xlabel('Categories') + ax[1].plot(np.asarray(x, float), y) + ax[1].set_title('x converted to numbers') + ax[1].set_xlabel('Floats') + +A common case is when dates are read from a CSV file, they need to be +converted from strings to datetime objects to get the proper date locators +and formatters. + +.. plot:: + :include-source: + :align: center + + fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 3.5)) + x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-10-04'] + y = [0, 2, 3, 1] + ax[0].plot(x, y, 'd') + ax[0].tick_params(axis='x', labelrotation=90, color='r', labelcolor='r') + ax[0].set_title('Dates out of order') + + # convert to datetime64 + x = np.asarray(x, dtype='datetime64[s]') + ax[1].plot(x, y, 'd') + ax[1].tick_params(axis='x', labelrotation=90) + ax[1].set_title('x converted to datetimes') .. _howto-determine-artist-extent: From 6355d1b09f74cbac4210a1f0b8611b067cb72ec9 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 9 Jan 2022 11:39:42 +0100 Subject: [PATCH 6/8] DOC: add new example --- doc/users/faq/howto_faq.rst | 68 ++++++++------------------------ examples/ticks/ticks_too_many.py | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 51 deletions(-) create mode 100644 examples/ticks/ticks_too_many.py diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index a9407ad8d7f2..03313bcee2b8 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -23,68 +23,34 @@ as "categorical" variables puts one tick per "category", and plots them in the order in which they are supplied. -In the example below, the upper row plots are plotted using strings for *x*; -note that each string gets a tick, and they are in the order of the list passed -to Matplotlib. If this is not desired, we need to change *x* to an array of -numbers. - .. plot:: :include-source: :align: center - fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2.5)) - x = ['1', '5', '2', '3'] - y = [1, 4, 2, 3] - ax[0].plot(x, y, 'd') - ax[0].tick_params(axis='x', color='r', labelcolor='r') - ax[0].set_xlabel('Categories') - ax[0].set_title('Ticks seem out of order / misplaced') - - # convert to numbers: - x = np.asarray(x, dtype='float') - ax[1].plot(x, y, 'd') - ax[1].set_xlabel('Floats') - ax[1].set_title('Ticks as expected') + import matplotlib.pyplot as plt + import numpy as np -If *x* has 100 elements, all strings, then we would have 100 (unreadable) -ticks, and again the solution is to convert the strings to floats: + fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2)) -.. plot:: - :include-source: - :align: center + ax[0].set_title('Ticks seem out of order / misplaced') + x = ['5', '20', '1', '9'] # strings + y = [5, 20, 1, 9] + ax[0].plot(x, y, 'd') + ax[0].tick_params(axis='x', labelcolor='red', labelsize=14) - fig, ax = plt.subplots(1, 2, figsize=(6, 2.5)) - x = [f'{xx}' for xx in np.arange(100)] + ax[1].set_title('Many ticks') + x = [str(xx) for xx in np.arange(100)] # strings y = np.arange(100) - ax[0].plot(x, y) - ax[0].tick_params(axis='x', color='r', labelcolor='r') - ax[0].set_title('Too many ticks') - ax[0].set_xlabel('Categories') - - ax[1].plot(np.asarray(x, float), y) - ax[1].set_title('x converted to numbers') - ax[1].set_xlabel('Floats') + ax[1].plot(x, y) + ax[1].tick_params(axis='x', labelcolor='red', labelsize=14) -A common case is when dates are read from a CSV file, they need to be -converted from strings to datetime objects to get the proper date locators -and formatters. +The solution is to convert the list of strings to numbers or +datetime objects (often ``np.asarray(['2', '5', '1'], dtype='float')`` or:: -.. plot:: - :include-source: - :align: center + np.asarray(['2021-10-01', '2021-11-02', '2021-12-03'], + dtype='datetime64[s]') - fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 3.5)) - x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-10-04'] - y = [0, 2, 3, 1] - ax[0].plot(x, y, 'd') - ax[0].tick_params(axis='x', labelrotation=90, color='r', labelcolor='r') - ax[0].set_title('Dates out of order') - - # convert to datetime64 - x = np.asarray(x, dtype='datetime64[s]') - ax[1].plot(x, y, 'd') - ax[1].tick_params(axis='x', labelrotation=90) - ax[1].set_title('x converted to datetimes') +For more information see :doc:`/gallery/ticks/ticks_too_many`. .. _howto-determine-artist-extent: diff --git a/examples/ticks/ticks_too_many.py b/examples/ticks/ticks_too_many.py new file mode 100644 index 000000000000..8756f07802b9 --- /dev/null +++ b/examples/ticks/ticks_too_many.py @@ -0,0 +1,68 @@ +""" +===================== +Fixing too many ticks +===================== + +One common cause for unexpected tick behavior is passing a list of strings +instead of numbers or datetime objects. This can easily happen without notice +when reading in a comma-delimited text file. Matplotlib treats lists of strings +as "categorical" variables +(:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default +puts one tick per "category", and plots them in the order in which they are +supplied. If this is not desired, the solution is to convert the strings to +a numeric type as in the following examples. + +""" + +import matplotlib.pyplot as plt +import numpy as np + +fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2.5)) +x = ['1', '5', '2', '3'] +y = [1, 4, 2, 3] +ax[0].plot(x, y, 'd') +ax[0].tick_params(axis='x', color='r', labelcolor='r') +ax[0].set_xlabel('Categories') +ax[0].set_title('Ticks seem out of order / misplaced') + +# convert to numbers: +x = np.asarray(x, dtype='float') +ax[1].plot(x, y, 'd') +ax[1].set_xlabel('Floats') +ax[1].set_title('Ticks as expected') + +############################################################################ +# If *x* has 100 elements, all strings, then we would have 100 (unreadable) +# ticks, and again the solution is to convert the strings to floats: + +fig, ax = plt.subplots(1, 2, figsize=(6, 2.5)) +x = [f'{xx}' for xx in np.arange(100)] +y = np.arange(100) +ax[0].plot(x, y) +ax[0].tick_params(axis='x', color='r', labelcolor='r') +ax[0].set_title('Too many ticks') +ax[0].set_xlabel('Categories') + +ax[1].plot(np.asarray(x, float), y) +ax[1].set_title('x converted to numbers') +ax[1].set_xlabel('Floats') + +############################################################################ +# A common case is when dates are read from a CSV file, they need to be +# converted from strings to datetime objects to get the proper date locators +# and formatters. + +fig, ax = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 2.75)) +x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-09-01'] +y = [0, 2, 3, 1] +ax[0].plot(x, y, 'd') +ax[0].tick_params(axis='x', labelrotation=90, color='r', labelcolor='r') +ax[0].set_title('Dates out of order') + +# convert to datetime64 +x = np.asarray(x, dtype='datetime64[s]') +ax[1].plot(x, y, 'd') +ax[1].tick_params(axis='x', labelrotation=90) +ax[1].set_title('x converted to datetimes') + +plt.show() From f49ca610824c93f3573495eccb74dbeaaf8ca1b3 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 10 Jan 2022 11:07:10 +0100 Subject: [PATCH 7/8] Update doc/users/faq/howto_faq.rst Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- doc/users/faq/howto_faq.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index 03313bcee2b8..5ac0c1773b92 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -45,10 +45,8 @@ supplied. ax[1].tick_params(axis='x', labelcolor='red', labelsize=14) The solution is to convert the list of strings to numbers or -datetime objects (often ``np.asarray(['2', '5', '1'], dtype='float')`` or:: - - np.asarray(['2021-10-01', '2021-11-02', '2021-12-03'], - dtype='datetime64[s]') +datetime objects (often ``np.asarray(numeric_strings, dtype='float')`` or +``np.asarray(datetime_strings, dtype='datetime64[s]')``). For more information see :doc:`/gallery/ticks/ticks_too_many`. From 2fc33946a0e7a76700e47fdc13bed07e497a257b Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 10 Jan 2022 11:10:09 +0100 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- doc/users/faq/howto_faq.rst | 8 ++++---- examples/ticks/ticks_too_many.py | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/users/faq/howto_faq.rst b/doc/users/faq/howto_faq.rst index 5ac0c1773b92..409b9e04e713 100644 --- a/doc/users/faq/howto_faq.rst +++ b/doc/users/faq/howto_faq.rst @@ -15,12 +15,12 @@ How-to Why do I have so many ticks, and/or why are they out of order? -------------------------------------------------------------- -One common cause for unexpected tick behavior is passing a list of strings -instead of numbers or datetime objects. This can easily happen without notice +One common cause for unexpected tick behavior is passing a *list of strings +instead of numbers or datetime objects*. This can easily happen without notice when reading in a comma-delimited text file. Matplotlib treats lists of strings -as "categorical" variables +as *categorical* variables (:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default -puts one tick per "category", and plots them in the order in which they are +puts one tick per category, and plots them in the order in which they are supplied. .. plot:: diff --git a/examples/ticks/ticks_too_many.py b/examples/ticks/ticks_too_many.py index 8756f07802b9..c1f05bf4c17e 100644 --- a/examples/ticks/ticks_too_many.py +++ b/examples/ticks/ticks_too_many.py @@ -6,14 +6,18 @@ One common cause for unexpected tick behavior is passing a list of strings instead of numbers or datetime objects. This can easily happen without notice when reading in a comma-delimited text file. Matplotlib treats lists of strings -as "categorical" variables +as *categorical* variables (:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default -puts one tick per "category", and plots them in the order in which they are +puts one tick per category, and plots them in the order in which they are supplied. If this is not desired, the solution is to convert the strings to a numeric type as in the following examples. """ +############################################################################ +# Example 1: Strings can lead to an unexpected order of number ticks +# ------------------------------------------------------------------ + import matplotlib.pyplot as plt import numpy as np @@ -32,6 +36,8 @@ ax[1].set_title('Ticks as expected') ############################################################################ +# Example 2: Strings can lead to very many ticks +# ---------------------------------------------- # If *x* has 100 elements, all strings, then we would have 100 (unreadable) # ticks, and again the solution is to convert the strings to floats: @@ -48,6 +54,8 @@ ax[1].set_xlabel('Floats') ############################################################################ +# Example 3: Strings can lead to an unexpected order of datetime ticks +# -------------------------------------------------------------------- # A common case is when dates are read from a CSV file, they need to be # converted from strings to datetime objects to get the proper date locators # and formatters.