英文:
Change month labels in matplotlib without changing the locale
问题
使用以下代码:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'][0:200]
fig, ax = plt.subplots(figsize=(10, 7), constrained_layout=True)
ax.plot('date', 'adj_close', data=data)
# ax.xaxis.set_major_locator(mdates.MonthLocator())
# ax.grid(True)
ax.set_ylabel(r'Price [$]')
ax.xaxis.set_major_formatter(
mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))
plt.savefig("chart.png")
我需要将月份从英文更改为西班牙文,即:Dec 更改为 Dic,Apr 更改为 Abr,Jan 更改为 Ene 等等。
我可以通过像这样更改地区设置来完成:
locale.setlocale(locale.LC_TIME, 'es_ES')
但我无法在脚本中使用它,因为它在无服务器虚拟机上运行,您无法更改任何操作系统配置。
所以我考虑了从英文月份手动更改标签的方法。我看到了使用 DateFormatter
的示例,但这不起作用,因为它再次依赖于系统区域设置来获取月份名称,使用 strftime
,而我看到的任何其他格式化程序都使用数字而不是日期。所以有没有办法本地化月份的名称?
更新
在下面添加了解决方案。
英文:
With the code:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'][0:200]
fig, ax = plt.subplots(figsize=(10, 7), constrained_layout=True)
ax.plot('date', 'adj_close', data=data)
# ax.xaxis.set_major_locator(mdates.MonthLocator())
# ax.grid(True)
ax.set_ylabel(r'Price [$]')
ax.xaxis.set_major_formatter(
mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))
plt.savefig("chart.png")
And I need to change the months from English to Spanish, ie: Dec to Dic, Apr to Abr, Jan to Ene and so on.
I can do it by changing the locale like so:
locale.setlocale(locale.LC_TIME, 'es_ES')
But I can't use it in the script because it runs on a serverless vm where you can't change any of the os configuration.
So I thought in changing the labels "manually" from an English month to a Spanish one. I've seen examples using DateFormatter
but that doesn't work because again it relies in the system locale for the months names using strftime
and any other fromatter I've seen has been using numbers not dates. So is there any solution to localize the names of the months?
Update
Added solution below
答案1
得分: 1
一种方法是使用 babel.dates
来进行日期格式化,它允许使用区域设置参数而不改变系统的区域设置。
基本语法如下:
babel.dates.format_datetime([日期时间对象], [格式], locale=[区域设置])
请注意,格式不使用 %
符号。查看包文档以获取更多信息和示例。
要使用此包来格式化 Matplotlib 刻度标签,您可以重新实现来自Matplotlib源代码的 DateFormatter
,并将 strftime()
调用替换为 babel.dates.format_datetime()
。
作为概念验证:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
import babel.dates
class CustomDateFormatter(mdates.ticker.Formatter):
def __init__(self, fmt, fmtloc, tz=None, *, usetex=None):
self.tz = mdates.UTC
self.fmt = fmt
self.fmtloc = fmtloc
self._usetex = (usetex if usetex is not None else
mpl.rcParams['text.usetex'])
def __call__(self, x, pos=0):
result = babel.dates.format_datetime(mdates.num2date(x, self.tz), self.fmt, locale=self.fmtloc)
return mdates._wrap_in_tex(result) if self._usetex else result
data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'][0:200]
fig, ax = plt.subplots(figsize=(10, 7), constrained_layout=True)
ax.plot('date', 'adj_close', data=data)
ax.set_ylabel(r'Price [$]')
ax.xaxis.set_major_formatter(
CustomDateFormatter("MMM yyyy", "es"))
plt.savefig("chart.png")
结果:
对于 ConciseDateFormatter
,这个想法是一样的,但这个类要复杂得多,因此您需要在许多地方进行修改,以便正确处理命名空间(类似于上面代码中的 mdates.num2date
,而不是原始类中的普通 num2dates
调用)。
英文:
One way is to use babel.dates
for the date formatting, which allows for a locale argument without changing the system locale.
The basic syntax is:
babel.dates.format_datetime([datetime object], [format], locale=[locale])
Note that the format does not use %
signs. See the package documentation for more information and many examples.
To use this package to format Matplotlib tick labels you can re-implement DateFormatter
from the matplotlib source and replace the strftime()
call with babel.dates.format_datetime()
.
As a proof of concept:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
import babel.dates
class CustomDateFormatter(mdates.ticker.Formatter):
def __init__(self, fmt, fmtloc, tz=None, *, usetex=None):
self.tz = mdates.UTC
self.fmt = fmt
self.fmtloc = fmtloc
self._usetex = (usetex if usetex is not None else
mpl.rcParams['text.usetex'])
def __call__(self, x, pos=0):
result = babel.dates.format_datetime(mdates.num2date(x, self.tz), self.fmt, locale=self.fmtloc)
return mdates._wrap_in_tex(result) if self._usetex else result
data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'][0:200]
fig, ax = plt.subplots(figsize=(10, 7), constrained_layout=True)
ax.plot('date', 'adj_close', data=data)
ax.set_ylabel(r'Price [$]')
ax.xaxis.set_major_formatter(
CustomDateFormatter("MMM yyyy", "es"))
plt.savefig("chart.png")
Result:
For ConciseDateFormatter
this idea is the same but this class is much more complex so you will need to modify it in many places in order to get the namespaces right (similar to for example mdates.num2date
in the code above instead of a plain num2dates
call in the original class).
答案2
得分: 1
使用这个 [答案][1] 和 [Marijn][2] 的答案,我得到了以下解决方案,它使用 `set_xticklabels` 来更改标签,并使用 [Babel][3] 来将月份更改为本地化版本,而不更改 `locale`:
```python
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
import babel.dates
import matplotlib.ticker as mticker
data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'][0:200]
fig, ax = plt.subplots(figsize=(10, 7), constrained_layout=True)
ax.plot('date', 'adj_close', data=data)
# ax.xaxis.set_major_locator(mdates.MonthLocator())
# ax.grid(True)
ax.set_ylabel(r'Price [$]')
ax.xaxis.set_major_formatter(
mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))
ticks_loc = ax.get_xticks().tolist()
ax.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
ax.set_xticklabels([babel.dates.format_datetime(mdates.num2date(x),'yyyy',locale='es')
if mdates.num2date(x).month == 1
else babel.dates.format_datetime(mdates.num2date(x),'MMM',locale='es')
for x in ticks_loc])
plt.savefig("chart.png")
它产生了:
<details>
<summary>英文:</summary>
Using this [answer][1] and the answer by [Marijn][2] I came up with this solution that uses `set_xticklabels` to change the labels and [Babel][3] to change the months to a localized version without changing the `locale`:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
import babel.dates
import matplotlib.ticker as mticker
data = cbook.get_sample_data('goog.npz', np_load=True)['price_data'][0:200]
fig, ax = plt.subplots(figsize=(10, 7), constrained_layout=True)
ax.plot('date', 'adj_close', data=data)
# ax.xaxis.set_major_locator(mdates.MonthLocator())
# ax.grid(True)
ax.set_ylabel(r'Price [$]')
ax.xaxis.set_major_formatter(
mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))
ticks_loc = ax.get_xticks().tolist()
ax.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))
ax.set_xticklabels([babel.dates.format_datetime(mdates.num2date(x),'yyyy',locale='es')
if mdates.num2date(x).month == 1
else babel.dates.format_datetime(mdates.num2date(x),'MMM',locale='es')
for x in ticks_loc])
plt.savefig("chart.png")
It produces:
[![enter image description here][4]][4]
[1]: https://stackoverflow.com/a/63755285/1128695
[2]: https://stackoverflow.com/a/75051766/1128695
[3]: https://babel.pocoo.org/en/latest/
[4]: https://i.stack.imgur.com/GZ4eV.png
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论