我正在用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') ;
这是我的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()
有没有一种方法可以防止标签相互重叠?理想情况下以自动方式进行,因为我无法预测条形图的数量。
我认为您对matplotlib如何处理日期感到有些困惑。
目前,您实际上并没有在绘制日期。您正在x轴上绘制事物[0,1,2,...],然后使用日期的字符串表示形式手动标记每个点。
[0,1,2,...]
Matplotlib将自动定位刻度线。但是,您要超越matplotlib的报价定位功能(xticks基本上是在说:“我想在这些位置准确报价”。)
xticks
此刻,[10, 20, 30, ...]如果matplotlib自动将它们定位,您将获得滴答声。但是,这些将对应于您用于绘制它们的值,而不是日期(绘制时未使用的日期)。
[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()
而是尝试这样的事情:
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()