matplotlib:如何防止x轴标签相互重叠

zeh*_*ron 39 python matplotlib bar-chart

我正在用matplotlib生成一个条形图.这一切都很好但我无法弄清楚如何防止x轴的标签相互重叠.这是一个例子:
在此输入图像描述

以下是postgres 9.1数据库的一些示例SQL:

drop table if exists mytable;
create table mytable(id bigint, version smallint, date_from timestamp without time zone);
insert into mytable(id, version, date_from) values

('4084036', '1', '2006-12-22 22:46:35'),
('4084938', '1', '2006-12-23 16:19:13'),
('4084938', '2', '2006-12-23 16:20:23'),
('4084939', '1', '2006-12-23 16:29:14'),
('4084954', '1', '2006-12-23 16:28:28'),
('4250653', '1', '2007-02-12 21:58:53'),
('4250657', '1', '2007-03-12 21:58:53')
;  
Run Code Online (Sandbox Code Playgroud)

这是我的python脚本:

# -*- coding: utf-8 -*-
#!/usr/bin/python2.7
import psycopg2
import matplotlib.pyplot as plt
fig = plt.figure()

# for savefig()
import pylab

###
### Connect to database with psycopg2
###

try:
  conn_string="dbname='x' user='y' host='z' password='pw'"
  print "Connecting to database\n->%s" % (conn_string)

  conn = psycopg2.connect(conn_string)
  print "Connection to database was established succesfully"
except:
  print "Connection to database failed"

###
### Execute SQL query
###  

# New cursor method for sql
cur = conn.cursor()

# Execute SQL query. For more than one row use three '"'
try:
  cur.execute(""" 

-- In which year/month have these points been created?
-- Need 'yyyymm' because I only need Months with years (values are summeed up). Without, query returns every day the db has an entry.

SELECT to_char(s.day,'yyyymm') AS month
      ,count(t.id)::int AS count
FROM  (
   SELECT generate_series(min(date_from)::date
                         ,max(date_from)::date
                         ,interval '1 day'
          )::date AS day
   FROM   mytable t
   ) s
LEFT   JOIN mytable t ON t.date_from::date = s.day
GROUP  BY month
ORDER  BY month;

  """)

# Return the results of the query. Fetchall() =  all rows, fetchone() = first row
  records = cur.fetchall()
  cur.close()

except:
  print "Query could not be executed"

# Unzip the data from the db-query. Order is the same as db-query output
year, count = zip(*records)

###
### Plot (Barchart)
###

# Count the length of the range of the count-values, y-axis-values, position of axis-labels, legend-label
plt.bar(range(len(count)), count, align='center', label='Amount of created/edited points')

# Add database-values to the plot with an offset of 10px/10px
ax = fig.add_subplot(111)
for i,j in zip(year,count):
    ax.annotate(str(j), xy=(i,j), xytext=(10,10), textcoords='offset points')

# Rotate x-labels on the x-axis
fig.autofmt_xdate()

# Label-values for x and y axis
plt.xticks(range(len(count)), (year))

# Label x and y axis
plt.xlabel('Year')
plt.ylabel('Amount of created/edited points')

# Locate legend on the plot (http://matplotlib.org/users/legend_guide.html#legend-location)
plt.legend(loc=1)

# Plot-title
plt.title("Amount of created/edited points over time")

# show plot
pylab.show()
Run Code Online (Sandbox Code Playgroud)

有没有办法阻止标签相互重叠?理想情况下是以自动方式,因为我无法预测酒吧的数量.

Joe*_*ton 31

我认为你对matplotlib如何处理日期的几点感到困惑.

目前你还没有真正绘制日期.您在x轴上绘制事物[0,1,2,...],然后使用日期的字符串表示手动标记每个点.

Matplotlib将自动定位滴答声.然而,你正在超越matplotlib的滴答定位功能(使用xticks基本上是在说:"我想在这些位置准确滴答".)

目前,[10, 20, 30, ...]如果matplotlib自动定位它们,你会得到一个滴答声.但是,这些将对应于您用于绘制它们的值,而不是日期(绘图时未使用的日期).

您可能希望使用日期来实际绘制内容.

目前,你正在做这样的事情:

import datetime as dt
import matplotlib.dates as mdates
import numpy as np
import matplotlib.pyplot as plt

# Generate a series of dates (these are in matplotlib's internal date format)
dates = mdates.drange(dt.datetime(2010, 01, 01), dt.datetime(2012,11,01), 
                      dt.timedelta(weeks=3))

# Create some data for the y-axis
counts = np.sin(np.linspace(0, np.pi, dates.size))

# Set up the axes and figure
fig, ax = plt.subplots()

# Make a bar plot, ignoring the date values
ax.bar(np.arange(counts.size), counts, align='center', width=1.0)

# Force matplotlib to place a tick at every bar and label them with the date
datelabels = mdates.num2date(dates) # Go back to a sequence of datetimes...
ax.set(xticks=np.arange(dates.size), xticklabels=datelabels) #Same as plt.xticks

# Make space for and rotate the x-axis tick labels
fig.autofmt_xdate()

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

相反,尝试这样的事情:

import datetime as dt
import matplotlib.dates as mdates
import numpy as np
import matplotlib.pyplot as plt

# Generate a series of dates (these are in matplotlib's internal date format)
dates = mdates.drange(dt.datetime(2010, 01, 01), dt.datetime(2012,11,01), 
                      dt.timedelta(weeks=3))

# Create some data for the y-axis
counts = np.sin(np.linspace(0, np.pi, dates.size))

# Set up the axes and figure
fig, ax = plt.subplots()

# By default, the bars will have a width of 0.8 (days, in this case) We want
# them quite a bit wider, so we'll make them them the minimum spacing between
# the dates. (To use the exact code below, you'll need to convert your sequence
# of datetimes into matplotlib's float-based date format.  
# Use "dates = mdates.date2num(dates)" to convert them.)
width = np.diff(dates).min()

# Make a bar plot. Note that I'm using "dates" directly instead of plotting
# "counts" against x-values of [0,1,2...]
ax.bar(dates, counts, align='center', width=width)

# Tell matplotlib to interpret the x-axis values as dates
ax.xaxis_date()

# Make space for and rotate the x-axis tick labels
fig.autofmt_xdate()

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 感谢您的回答。如果我有约会,这可以工作。但是我的刻度值未格式化为日期值。它们来自我的数据库,格式为字符串。因此,还有另一种使用我的代码的方式,但仅在x轴上每隔4个刻度显示一次吗? (2认同)

Pau*_*l H 10

编辑2014-09-30

熊猫现在有一个read_sql功能.你肯定想要使用它.

原始答案

以下是将日期字符串转换为实际日期时间对象的方法:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
data_tuples = [
    ('4084036', '1', '2006-12-22 22:46:35'),
    ('4084938', '1', '2006-12-23 16:19:13'),
    ('4084938', '2', '2006-12-23 16:20:23'),
    ('4084939', '1', '2006-12-23 16:29:14'),
    ('4084954', '1', '2006-12-23 16:28:28'),
    ('4250653', '1', '2007-02-12 21:58:53'),
    ('4250657', '1', '2007-03-12 21:58:53')]
datatypes = [('col1', 'i4'), ('col2', 'i4'), ('date', 'S20')]
data = np.array(data_tuples, dtype=datatypes)
col1 = data['col1']
dates = mdates.num2date(mdates.datestr2num(data['date']))
fig, ax1 = plt.subplots()
ax1.bar(dates, col1)
fig.autofmt_xdate()
Run Code Online (Sandbox Code Playgroud)

从数据库游标中获取一个简单的元组列表应该像...一样简单

data_tuples = []
for row in cursor:
    data_tuples.append(row)
Run Code Online (Sandbox Code Playgroud)

但是,我发布了一个函数版本,我用它来直接将db游标带到记录数组或pandas数据帧:如何将SQL查询结果转换为PANDAS数据结构?

希望这也有帮助.

  • 我也没有误读过.问题不是你实际上没有*解决OP的问题; 你所做的就是这样一种方式,对于一个新的读者来说,到达问题时,根本不会*与OP的问题有关.问题是,如果值是字符串,`matplotlib`不知道如何合理地绘制轴的标签,并且需要将x轴值转换为可以使用的类型......但答案是不会*说出那个,或根本不解释自己,这使得除了原始提问者以外的任何人都没有帮助(并且甚至限制了它对它们的用处). (5认同)
  • 是的,*问题*是"如何防止x轴标签重叠?" 这个问题和解析日期之间没有明显的关系. (4认同)
  • -1; 我敢肯定,这以某种间接的方式解决了这个问题,这对我来说并不是立即显而易见的,但应该将其阐明。实际上,这似乎与所提问题完全无关。您不会显示任何图像,甚至不会提及图形或标签。 (2认同)

And*_*eil 8

至于你如何在xaxis上只显示每个第4个刻度(例如)的问题,你可以这样做:

import matplotlib.ticker as mticker

myLocator = mticker.MultipleLocator(4)
ax.xaxis.set_major_locator(myLocator)
Run Code Online (Sandbox Code Playgroud)

  • 显示的代码不"仅显示每4个刻度".它设置4的整数倍的刻度. (3认同)

Mat*_*ijn 7

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# create a random dataframe with datetimeindex
date_range = pd.date_range('1/1/2011', '4/10/2011', freq='D')
df = pd.DataFrame(np.random.randint(0,10,size=(100, 1)), columns=['value'], index=date_range)
Run Code Online (Sandbox Code Playgroud)

日期刻度标签经常重叠:

plt.plot(df.index,df['value'])
plt.show()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

所以旋转它们并右对齐它们很有用。

fig, ax = plt.subplots()
ax.plot(df.index,df['value'])
ax.xaxis_date()     # interpret the x-axis values as dates
fig.autofmt_xdate() # make space for and rotate the x-axis tick labels
plt.show()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明