ASKSAGE: Sage Q&A Forum - RSS feedhttps://ask.sagemath.org/questions/Q&A Forum for SageenCopyright Sage, 2010. Some rights reserved under creative commons license.Wed, 29 Apr 2020 14:49:40 +0200Adapt 2D-plot tick label attributes to dateshttps://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/Is there a way to access ticks labels to rotate them? I've tried `Graphics.matplotlib()` and `set_rotation()`, but this doesn't seem to produce changes. Am I doing wrong things?
In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?
![Plot with dates as tick labels on the x-axis](https://i.imgur.com/14VIFv4.png)Mon, 27 Apr 2020 20:59:05 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/Comment by slelievre for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51141#post-id-51141Suggestion: format dates following the international standard ISO 8601.
- [ISO 8601 (Wikipedia)](https://en.wikipedia.org/wiki/ISO-8601)
- [ISO 8601 (ISO site)](https://www.iso.org/iso-8601-date-and-time-format.html)Wed, 29 Apr 2020 14:49:40 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51141#post-id-51141Comment by slelievre for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51086#post-id-51086A minimal example that can by copy-pasted in a fresh Sage session helps others get started on an answering a question, therefore increasing the chances of getting an answer. In this case,
- include the appropriate import statements so that `dates.DateFormatter` will work
- provide an actual `Z` (with just enough dates to illustrate the problem)Mon, 27 Apr 2020 23:35:46 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51086#post-id-51086Comment by MrDvbnhbq for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51095#post-id-51095Hello, thank you for your response. I've added the information needed to comply.Tue, 28 Apr 2020 12:33:34 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51095#post-id-51095Comment by Sébastien for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51121#post-id-51121Following what is explained in this [link](https://www.delftstack.com/howto/matplotlib/how-to-rotate-x-axis-tick-label-text-in-matplotlib/), it seems the following should work:
sage: figure = p.matplotlib()
sage: figure.autofmt_xdate(rotation=45)
sage: figure.savefig('a.png')
but the last line returns an error because `figure.canvas` is NoneTue, 28 Apr 2020 20:26:40 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51121#post-id-51121Comment by MrDvbnhbq for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51123#post-id-51123Yes, thanks for this answer. I've already tried that with the same result. But this example is for pure matplotlib, not the sage `Graphics` object.
I would like to know, what does that `matplotlib()` exactly do? I even tried:
from matplotlib.pyplot import figure
...
fig = figure()
fig = p.matplotlib(fig)
...
to draw the plot on the new figure provided, but this doesn't work for me.
So, the question is, is this `matplotlib()` function supposed to give the access to `p` attributes, which were previosly set when the plot was created, and should the changes made on the figure effect onto the plot? And is the plot is supposed to be redrawn on the new figure when the plot is passed as an argument to matplotlib()?Tue, 28 Apr 2020 21:35:07 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51123#post-id-51123Answer by dsejas for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?answer=51127#post-id-51127Hello, @MrDvbnhbq! I can see that you already got an answer for your question. However, I have an alternative approach.
As far as I know, there is no direct way to do this in SageMath, so we are going to use a mix of Sage plots and Matplotlib plots, i.e., we will use the `matplotlib()` command. All the arguments that you passed to `list_plot()` can be passed to the `matplotlib()` command, except for the `thickness` and the `plotjoined` parameters, which are exclusive of the former.
Consider the following instructions:
%matplotlib inline
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
import matplotlib.pyplot as plt
data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20' ), (20, 40, 80, 160, 320, 640) ]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, plotjoined=true, thickness=2)
G = p.matplotlib(figure=plt.gcf(),scale='semilogy',tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None], axes_labels=[ 'Days', '$\\log \\;{N}$' ], ticks=[1, None], figsize=4)
G.autofmt_xdate(rotation=45)
G.show()
I have made only three changes to your code:
1. I have used the magic `%matplotlib inline` as the first line, so that Matplotlib figures are shown in SageCell or Jupyter(lab).
2. I have imported `pyplot` under the name `plt` on line 6.
3. I have moved all the arguments you used in `list_plot` to the `matplotlib()` command, including the additional argument `figure=plt.gcf()`.
4. Concerning the rotation of the xticks, I used the `G.autofmt_xdate(rotation=45)` command. However, you can also use the `plt.xticks(rotation=45)` instruction; since Matplotlib already knows you are working with the current figure (that's the `plt.gcf()`), it will apply the changes directly to it, in that case.
That's it!
By the way, you can remove the `G.show()` line, which I included for completeness.
The problem with your previous approach, and @Sébastien's is that the `matplotlib()` command does not preserve the semilog y-axis, nor the ticks formatter, etc. So, instead of using those options in `list_plot` where they will be overwritten later by `matplotlib()`, I passed them to the latter, so it can't ignore them.
The final result should look like this:
![image description](/upfiles/1588112108282001.png)
I hope this helps!Wed, 29 Apr 2020 00:12:50 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?answer=51127#post-id-51127Comment by Sébastien for <p>Hello, <a href="/users/27827/mrdvbnhbq/">@MrDvbnhbq</a>! I can see that you already got an answer for your question. However, I have an alternative approach.</p>
<p>As far as I know, there is no direct way to do this in SageMath, so we are going to use a mix of Sage plots and Matplotlib plots, i.e., we will use the <code>matplotlib()</code> command. All the arguments that you passed to <code>list_plot()</code> can be passed to the <code>matplotlib()</code> command, except for the <code>thickness</code> and the <code>plotjoined</code> parameters, which are exclusive of the former.</p>
<p>Consider the following instructions:</p>
<pre><code>%matplotlib inline
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
import matplotlib.pyplot as plt
data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20' ), (20, 40, 80, 160, 320, 640) ]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, plotjoined=true, thickness=2)
G = p.matplotlib(figure=plt.gcf(),scale='semilogy',tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None], axes_labels=[ 'Days', '$\\log \\;{N}$' ], ticks=[1, None], figsize=4)
G.autofmt_xdate(rotation=45)
G.show()
</code></pre>
<p>I have made only three changes to your code:</p>
<ol>
<li>I have used the magic <code>%matplotlib inline</code> as the first line, so that Matplotlib figures are shown in SageCell or Jupyter(lab).</li>
<li>I have imported <code>pyplot</code> under the name <code>plt</code> on line 6.</li>
<li>I have moved all the arguments you used in <code>list_plot</code> to the <code>matplotlib()</code> command, including the additional argument <code>figure=plt.gcf()</code>.</li>
<li>Concerning the rotation of the xticks, I used the <code>G.autofmt_xdate(rotation=45)</code> command. However, you can also use the <code>plt.xticks(rotation=45)</code> instruction; since Matplotlib already knows you are working with the current figure (that's the <code>plt.gcf()</code>), it will apply the changes directly to it, in that case.</li>
</ol>
<p>That's it!</p>
<p>By the way, you can remove the <code>G.show()</code> line, which I included for completeness.</p>
<p>The problem with your previous approach, and <a href="/users/23197/sebastien/">@Sébastien</a>'s is that the <code>matplotlib()</code> command does not preserve the semilog y-axis, nor the ticks formatter, etc. So, instead of using those options in <code>list_plot</code> where they will be overwritten later by <code>matplotlib()</code>, I passed them to the latter, so it can't ignore them.</p>
<p>The final result should look like this:</p>
<p><img alt="image description" src="/upfiles/1588112108282001.png"></p>
<p>I hope this helps!</p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51133#post-id-51133A discussion on sage-devel is needed when two persons do not agree on a decision and they ask on sage-devel for comments to get some consensus. In your case, you propose a clear improvement so such a discussion will not be needed. I suggest you to create a ticket, post a branch and reviewers will check it.Wed, 29 Apr 2020 09:24:12 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51133#post-id-51133Comment by tmonteil for <p>Hello, <a href="/users/27827/mrdvbnhbq/">@MrDvbnhbq</a>! I can see that you already got an answer for your question. However, I have an alternative approach.</p>
<p>As far as I know, there is no direct way to do this in SageMath, so we are going to use a mix of Sage plots and Matplotlib plots, i.e., we will use the <code>matplotlib()</code> command. All the arguments that you passed to <code>list_plot()</code> can be passed to the <code>matplotlib()</code> command, except for the <code>thickness</code> and the <code>plotjoined</code> parameters, which are exclusive of the former.</p>
<p>Consider the following instructions:</p>
<pre><code>%matplotlib inline
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
import matplotlib.pyplot as plt
data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20' ), (20, 40, 80, 160, 320, 640) ]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, plotjoined=true, thickness=2)
G = p.matplotlib(figure=plt.gcf(),scale='semilogy',tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None], axes_labels=[ 'Days', '$\\log \\;{N}$' ], ticks=[1, None], figsize=4)
G.autofmt_xdate(rotation=45)
G.show()
</code></pre>
<p>I have made only three changes to your code:</p>
<ol>
<li>I have used the magic <code>%matplotlib inline</code> as the first line, so that Matplotlib figures are shown in SageCell or Jupyter(lab).</li>
<li>I have imported <code>pyplot</code> under the name <code>plt</code> on line 6.</li>
<li>I have moved all the arguments you used in <code>list_plot</code> to the <code>matplotlib()</code> command, including the additional argument <code>figure=plt.gcf()</code>.</li>
<li>Concerning the rotation of the xticks, I used the <code>G.autofmt_xdate(rotation=45)</code> command. However, you can also use the <code>plt.xticks(rotation=45)</code> instruction; since Matplotlib already knows you are working with the current figure (that's the <code>plt.gcf()</code>), it will apply the changes directly to it, in that case.</li>
</ol>
<p>That's it!</p>
<p>By the way, you can remove the <code>G.show()</code> line, which I included for completeness.</p>
<p>The problem with your previous approach, and <a href="/users/23197/sebastien/">@Sébastien</a>'s is that the <code>matplotlib()</code> command does not preserve the semilog y-axis, nor the ticks formatter, etc. So, instead of using those options in <code>list_plot</code> where they will be overwritten later by <code>matplotlib()</code>, I passed them to the latter, so it can't ignore them.</p>
<p>The final result should look like this:</p>
<p><img alt="image description" src="/upfiles/1588112108282001.png"></p>
<p>I hope this helps!</p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51128#post-id-51128Looks good ! The interface between Sage and the underlying matplotlib, and the possilibty go work on that backend should be clarified and polished. Do you have an idea why some options passed to Sage (e.g. semilog y-axis) are not transfered to matplotlib ?Wed, 29 Apr 2020 01:50:34 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51128#post-id-51128Comment by tmonteil for <p>Hello, <a href="/users/27827/mrdvbnhbq/">@MrDvbnhbq</a>! I can see that you already got an answer for your question. However, I have an alternative approach.</p>
<p>As far as I know, there is no direct way to do this in SageMath, so we are going to use a mix of Sage plots and Matplotlib plots, i.e., we will use the <code>matplotlib()</code> command. All the arguments that you passed to <code>list_plot()</code> can be passed to the <code>matplotlib()</code> command, except for the <code>thickness</code> and the <code>plotjoined</code> parameters, which are exclusive of the former.</p>
<p>Consider the following instructions:</p>
<pre><code>%matplotlib inline
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
import matplotlib.pyplot as plt
data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20' ), (20, 40, 80, 160, 320, 640) ]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, plotjoined=true, thickness=2)
G = p.matplotlib(figure=plt.gcf(),scale='semilogy',tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None], axes_labels=[ 'Days', '$\\log \\;{N}$' ], ticks=[1, None], figsize=4)
G.autofmt_xdate(rotation=45)
G.show()
</code></pre>
<p>I have made only three changes to your code:</p>
<ol>
<li>I have used the magic <code>%matplotlib inline</code> as the first line, so that Matplotlib figures are shown in SageCell or Jupyter(lab).</li>
<li>I have imported <code>pyplot</code> under the name <code>plt</code> on line 6.</li>
<li>I have moved all the arguments you used in <code>list_plot</code> to the <code>matplotlib()</code> command, including the additional argument <code>figure=plt.gcf()</code>.</li>
<li>Concerning the rotation of the xticks, I used the <code>G.autofmt_xdate(rotation=45)</code> command. However, you can also use the <code>plt.xticks(rotation=45)</code> instruction; since Matplotlib already knows you are working with the current figure (that's the <code>plt.gcf()</code>), it will apply the changes directly to it, in that case.</li>
</ol>
<p>That's it!</p>
<p>By the way, you can remove the <code>G.show()</code> line, which I included for completeness.</p>
<p>The problem with your previous approach, and <a href="/users/23197/sebastien/">@Sébastien</a>'s is that the <code>matplotlib()</code> command does not preserve the semilog y-axis, nor the ticks formatter, etc. So, instead of using those options in <code>list_plot</code> where they will be overwritten later by <code>matplotlib()</code>, I passed them to the latter, so it can't ignore them.</p>
<p>The final result should look like this:</p>
<p><img alt="image description" src="/upfiles/1588112108282001.png"></p>
<p>I hope this helps!</p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51129#post-id-51129Also, since you seem at ease with the graphic internals, did you consider helping in improving that part of Sage ?Wed, 29 Apr 2020 02:02:37 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51129#post-id-51129Comment by dsejas for <p>Hello, <a href="/users/27827/mrdvbnhbq/">@MrDvbnhbq</a>! I can see that you already got an answer for your question. However, I have an alternative approach.</p>
<p>As far as I know, there is no direct way to do this in SageMath, so we are going to use a mix of Sage plots and Matplotlib plots, i.e., we will use the <code>matplotlib()</code> command. All the arguments that you passed to <code>list_plot()</code> can be passed to the <code>matplotlib()</code> command, except for the <code>thickness</code> and the <code>plotjoined</code> parameters, which are exclusive of the former.</p>
<p>Consider the following instructions:</p>
<pre><code>%matplotlib inline
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
import matplotlib.pyplot as plt
data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20' ), (20, 40, 80, 160, 320, 640) ]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, plotjoined=true, thickness=2)
G = p.matplotlib(figure=plt.gcf(),scale='semilogy',tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None], axes_labels=[ 'Days', '$\\log \\;{N}$' ], ticks=[1, None], figsize=4)
G.autofmt_xdate(rotation=45)
G.show()
</code></pre>
<p>I have made only three changes to your code:</p>
<ol>
<li>I have used the magic <code>%matplotlib inline</code> as the first line, so that Matplotlib figures are shown in SageCell or Jupyter(lab).</li>
<li>I have imported <code>pyplot</code> under the name <code>plt</code> on line 6.</li>
<li>I have moved all the arguments you used in <code>list_plot</code> to the <code>matplotlib()</code> command, including the additional argument <code>figure=plt.gcf()</code>.</li>
<li>Concerning the rotation of the xticks, I used the <code>G.autofmt_xdate(rotation=45)</code> command. However, you can also use the <code>plt.xticks(rotation=45)</code> instruction; since Matplotlib already knows you are working with the current figure (that's the <code>plt.gcf()</code>), it will apply the changes directly to it, in that case.</li>
</ol>
<p>That's it!</p>
<p>By the way, you can remove the <code>G.show()</code> line, which I included for completeness.</p>
<p>The problem with your previous approach, and <a href="/users/23197/sebastien/">@Sébastien</a>'s is that the <code>matplotlib()</code> command does not preserve the semilog y-axis, nor the ticks formatter, etc. So, instead of using those options in <code>list_plot</code> where they will be overwritten later by <code>matplotlib()</code>, I passed them to the latter, so it can't ignore them.</p>
<p>The final result should look like this:</p>
<p><img alt="image description" src="/upfiles/1588112108282001.png"></p>
<p>I hope this helps!</p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51131#post-id-51131Hello, @tmonteil! As for your first question, yes, I know why some options passed to Sage are not transferred to Matplotlib. The reason is that the `matplotlib()` command has some of the same arguments as the `list_plot` command, and those have default values, which are not overwritten by the Sage plot properties.
Let me give you an example. Suppose you use the following command:
q = p.matplotlib()
where `p` is a plot. If you check the signature of this method, you will notice it has the argument `axes` with default value `None`. As expected, when you don't use the `axes` argument here, it assumes the value `None`. However, the following that should happen in this case is that the code should use the `axes` value set for `p`, but That doesn't happen!
It should be easy to fix.Wed, 29 Apr 2020 07:51:51 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51131#post-id-51131Comment by dsejas for <p>Hello, <a href="/users/27827/mrdvbnhbq/">@MrDvbnhbq</a>! I can see that you already got an answer for your question. However, I have an alternative approach.</p>
<p>As far as I know, there is no direct way to do this in SageMath, so we are going to use a mix of Sage plots and Matplotlib plots, i.e., we will use the <code>matplotlib()</code> command. All the arguments that you passed to <code>list_plot()</code> can be passed to the <code>matplotlib()</code> command, except for the <code>thickness</code> and the <code>plotjoined</code> parameters, which are exclusive of the former.</p>
<p>Consider the following instructions:</p>
<pre><code>%matplotlib inline
import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
import matplotlib.pyplot as plt
data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20' ), (20, 40, 80, 160, 320, 640) ]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, plotjoined=true, thickness=2)
G = p.matplotlib(figure=plt.gcf(),scale='semilogy',tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None], axes_labels=[ 'Days', '$\\log \\;{N}$' ], ticks=[1, None], figsize=4)
G.autofmt_xdate(rotation=45)
G.show()
</code></pre>
<p>I have made only three changes to your code:</p>
<ol>
<li>I have used the magic <code>%matplotlib inline</code> as the first line, so that Matplotlib figures are shown in SageCell or Jupyter(lab).</li>
<li>I have imported <code>pyplot</code> under the name <code>plt</code> on line 6.</li>
<li>I have moved all the arguments you used in <code>list_plot</code> to the <code>matplotlib()</code> command, including the additional argument <code>figure=plt.gcf()</code>.</li>
<li>Concerning the rotation of the xticks, I used the <code>G.autofmt_xdate(rotation=45)</code> command. However, you can also use the <code>plt.xticks(rotation=45)</code> instruction; since Matplotlib already knows you are working with the current figure (that's the <code>plt.gcf()</code>), it will apply the changes directly to it, in that case.</li>
</ol>
<p>That's it!</p>
<p>By the way, you can remove the <code>G.show()</code> line, which I included for completeness.</p>
<p>The problem with your previous approach, and <a href="/users/23197/sebastien/">@Sébastien</a>'s is that the <code>matplotlib()</code> command does not preserve the semilog y-axis, nor the ticks formatter, etc. So, instead of using those options in <code>list_plot</code> where they will be overwritten later by <code>matplotlib()</code>, I passed them to the latter, so it can't ignore them.</p>
<p>The final result should look like this:</p>
<p><img alt="image description" src="/upfiles/1588112108282001.png"></p>
<p>I hope this helps!</p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51132#post-id-51132Continuing with your second question, @tmonteil, yes, I would be happy to help improving the Sage-Matplotlib interface. Actually, I've been thinking this for a long time. There is also much to improve in the 3d plots capabilities (check, for example, [this question](https://ask.sagemath.org/question/51073/3d-graphics-contour-plot-and-labels/)).
The following two weeks I am going to be very busy, but then I can start adding some improvements. Is there a recommended way to proceed? Should I work freely or should I pass every decision through the sage-devel forum?Wed, 29 Apr 2020 08:10:33 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51132#post-id-51132Answer by Sébastien for <p>Is there a way to access ticks labels to rotate them? I've tried <code>Graphics.matplotlib()</code> and <code>set_rotation()</code>, but this doesn't seem to produce changes. Am I doing wrong things?</p>
<p>In the example below, the formatter and locator are working correctly, but the problem is all labels are oriented horizontally, messing all together since each tick label is a date and therefore quite long. Need to rotate them.</p>
<pre><code>import csv
from datetime import datetime
from matplotlib import ticker
from matplotlib import dates
data = [('04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'),
(20, 40, 80, 160, 320, 640)]
labels = data[0]
labels = map(lambda x: dates.date2num(datetime.strptime(x, '%m/%d/%y')), labels)
labels = list(labels)
values = data[1]
values = map(lambda x: int(x), values)
# Z is a list of [(x1, y1), (x2, y2)...]
# x1, x2, ... are dates
# y1, y2, ... are values
Z = zip(labels, values)
Z = list(Z)
p = list_plot(Z, ticks=[1, None],
tick_formatter=[dates.DateFormatter('%d.%m.%Y'), None],
axes_labels=['Days', '$\\log \\;{N}$'], plotjoined=True,
thickness=2, figsize=4, scale='semilogy')
G = p.matplotlib()
labels = G.axes[0].xaxis.get_ticklabels()
labels = list(labels)
for label in labels:
label.set_rotation(45)
p
</code></pre>
<p>This outputs the plot with an ugly x-axis on which all the dates are messed up. How to fix that?</p>
<p><img src="https://i.imgur.com/14VIFv4.png" alt="Plot with dates as tick labels on the x-axis"></p>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?answer=51122#post-id-51122Ok, so it seems what was missing is the "get current graphics", see this other [question](https://ask.sagemath.org/question/46187/how-can-i-plot-error-bars-for-functions/?answer=46200#post-id-46200). Now this works:
sage: import matplotlib.pyplot as mpl
sage: figure = p.matplotlib(figure=mpl.gcf())
sage: figure.autofmt_xdate(rotation=45)
sage: figure.show()
sage: figure.savefig('a.png')
![image description](/upfiles/15881043047856882.png)
But then, the dates and log y scales need more work. There must be a way to fix this... Alternatively, you may use [pandas](https://pandas.pydata.org/) (which is an optional python library that one can install by doing `sage -pip install pandas`):
sage: from datetime import datetime
sage: data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'), (20, 40, 80,160, 320, 640) ]
sage: labels = [datetime.strptime(x, '%m/%d/%y') for x in data[0]]
sage: values = [int(a) for a in data[1]]
sage: import pandas as pd
sage: df = pd.DataFrame({'dates':labels,'values':values})
sage: df
dates values
0 2020-04-22 20
1 2020-04-23 40
2 2020-04-24 80
3 2020-04-25 160
4 2020-04-26 320
5 2020-04-27 640
sage: df.plot(x='dates', y='values', title='title', rot=30, logy=True, figsize=[4,4], yticks=[10,100,1000])
![image description](/upfiles/15881014733372772.png)
For the image to appear in a Jupyter notebook, add the following in the first cell:
%matplotlib inlineTue, 28 Apr 2020 21:21:37 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?answer=51122#post-id-51122Comment by MrDvbnhbq for <p>Ok, so it seems what was missing is the "get current graphics", see this other <a href="https://ask.sagemath.org/question/46187/how-can-i-plot-error-bars-for-functions/?answer=46200#post-id-46200">question</a>. Now this works:</p>
<pre><code>sage: import matplotlib.pyplot as mpl
sage: figure = p.matplotlib(figure=mpl.gcf())
sage: figure.autofmt_xdate(rotation=45)
sage: figure.show()
sage: figure.savefig('a.png')
</code></pre>
<p><img alt="image description" src="/upfiles/15881043047856882.png"></p>
<p>But then, the dates and log y scales need more work. There must be a way to fix this... Alternatively, you may use <a href="https://pandas.pydata.org/">pandas</a> (which is an optional python library that one can install by doing <code>sage -pip install pandas</code>):</p>
<pre><code>sage: from datetime import datetime
sage: data = [ ( '04/22/20', '04/23/20', '04/24/20','04/25/20','04/26/20', '04/27/20'), (20, 40, 80,160, 320, 640) ]
sage: labels = [datetime.strptime(x, '%m/%d/%y') for x in data[0]]
sage: values = [int(a) for a in data[1]]
sage: import pandas as pd
sage: df = pd.DataFrame({'dates':labels,'values':values})
sage: df
dates values
0 2020-04-22 20
1 2020-04-23 40
2 2020-04-24 80
3 2020-04-25 160
4 2020-04-26 320
5 2020-04-27 640
sage: df.plot(x='dates', y='values', title='title', rot=30, logy=True, figsize=[4,4], yticks=[10,100,1000])
</code></pre>
<p><img alt="image description" src="/upfiles/15881014733372772.png"></p>
<p>For the image to appear in a Jupyter notebook, add the following in the first cell:</p>
<pre><code>%matplotlib inline
</code></pre>
https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51126#post-id-51126Aha!.. That's a pretty nice solution. Will definitely dig deeper into pandas too. Thanks a lot for your help! Cheers 👍Tue, 28 Apr 2020 22:27:40 +0200https://ask.sagemath.org/question/51080/adapt-2d-plot-tick-label-attributes-to-dates/?comment=51126#post-id-51126