简体   繁体   中英

How to change distances between labels on Y axis in pyplot?

I am doing a plot of "travelling time" with time on x axis and checkpoints on y axis (Checkpoints are only names, and one checkpoint corresponds with one timestamp). The code for plotting itself is:

if checkpoints:
  plt.plot_date(timestamps,checkpoints,color = "b",linestyle='solid')
else:
  plt.plot_date(timestamps,distances,color = "b",linestyle='solid')

Timestamps : list of hours:mins, checkpoints : list of strings, distances : list of ints

I would need to remake the plot with different spacings on y axis, as the distance between checkpoints varies. This, I guess, can be achieved with using distance array as 2nd argument, but I have no clue how should I rename the distance labels so they correspond with the checkpoints.

Edit:

Data used:

Timestamps: They are converted by command datetime.strptime(timestamp, "%H:%M") and collected in an array. Input data as following:

4:45 4:48 4:51 4:52 4:53 4:54 4:55 4:56 5:15 5:19 5:24 5:25 5:26 5:28 5:29 5:30 5:33 5:35 5:39 5:40 5:42 5:43 5:44 5:45 5:39 5:40 5:46 5:47 5:48 5:50 5:51 5:52

Checkpoints: An array of strings, with the same length as timestamps. They are periodically repeated to match the timestamps:

[ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' ]

Distances: Similar to checkpoints, but they are integers, not strings.

[ 0, 9, 12, 16, 18, 21, 23, 24, 0, 9, 12, 16, 18, 21, 23, 24, 0, 9, 12, 16, 18, 21, 23, 24, 0, 9, 12, 16, 18, 21, 23, 24 ]

Produced output with checkpoints and timestamps:

示例 1

What I don't like is the even distance between checkpoints.

I would like to achieve this - the output with distances and timestamps (Numbers on Y axis are distances from checkpoint A), but with labels from the first example:

示例 2

The key to success is to use in y axis distances of checkpoints, instead of their text labels.

Your data needed here are:

ts = [
    '4:45', '4:48', '4:51', '4:52', '4:53', '4:54', '4:55', '4:56',
    '5:15', '5:19', '5:24', '5:25', '5:26', '5:28', '5:29', '5:30',
    '5:33', '5:35', '5:39', '5:40', '5:42', '5:43', '5:44', '5:45',
    '5:39', '5:40', '5:46', '5:47', '5:48', '5:50', '5:51', '5:52']
chkDist = [0, 9, 12, 16, 18, 21, 23, 24]
chkLbl  = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']

Checkpoint distances and labels are without repetitions.

Let's start from conversion of your data. Generate a DataFrame with:

  • 4 columns for each line (cut ts into rows and then transform),
  • the index as dist ,
  • and convert strings to datetime .

The code to do it is:

timestamps = pd.DataFrame(np.array(ts).reshape((4, -1)).T,
    index=chkDist).apply(pd.to_datetime)

The date part is taken from the current day, but actually it doesn't matter.

Convert also checkpoint distances and labels to a Series :

chkLabels = pd.Series(chkLbl, index=chkDist)

Then generate your picture as follows:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

fig, ax = plt.subplots(figsize=(6, 4), constrained_layout=True)
plt.plot_date(timestamps, timestamps.index, color='b', linestyle='solid')
ax.xaxis.set_major_locator(mdates.MinuteLocator(byminute=range(0,60,15)))
ax.xaxis.set_major_formatter(mdates.DateFormatter(fmt='%H:%M'))
plt.xticks(rotation=30)
for dist, lbl in chkLabels.iteritems():
    plt.text(xLbl, dist, lbl, ha='center', va='center')
ax.set_yticks(chkDist, minor=True)
plt.grid(which='minor', linestyle='--')
plt.grid(which='major')
plt.show()

Note that timestamps is a DataFrame , so from each of its columns there is generated separate line. I used MinuteLocator to have x labels every 15 minutes and DateFormatter to format them.

To print checkpoint labels I used additional loop, iterating over chkLabels and printing each label.

Note however that text coordinates for plt.text are expressed in data coordinates , so:

  • y coordinates are taken from the index of chkLabels ,
  • but x coordinates are expressed as minimal time - 10 min .

And the last step is to print the grid. For minor lines I used set_yticks with y taken from chkDist . Then I called plt.grid , separately for minor lines, with explicit specification of the linestyle, and for major lines, with default attributes.

For your data I got the following result:

在此处输入图片说明

Note that checkpoint labels are sometimes very close to each other (in this case G and H ). So if they were overlapping each other, you should modify the code to print only selected labels, eg by dropping some elements from chkLabels .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM