|
| 1 | +""" |
| 2 | +========================= |
| 3 | +Combining Transformations |
| 4 | +========================= |
| 5 | +
|
| 6 | +This example showcases how to combine transformations, including Affine |
| 7 | +and blended transformations. |
| 8 | +
|
| 9 | +The goal of this exercise is to plot some circles with correct aspect |
| 10 | +ratios on axes that are unequal. The interesting thing is that the |
| 11 | +circles have a fixed size along the y-axis in data space rather than |
| 12 | +axes, figure or display space. |
| 13 | +
|
| 14 | +To ensure a large disparity in the data axis scales, we plot |
| 15 | +categorical vs time data. The x-axis is set to a short time inverval of |
| 16 | +a few minutes. The y-axis is integers corresponding to the category |
| 17 | +index. |
| 18 | +
|
| 19 | +The transform graph is constructed as follows: |
| 20 | +
|
| 21 | +- A blended transform is made to scale the data: |
| 22 | + - The y direction is the scale-only portion of `ax.transData` |
| 23 | + obtained with `AffineDeltaTransform` |
| 24 | + - The x direction is a reflection of the y, made using an `Affine2D` |
| 25 | + reflection matrix |
| 26 | +- The blended transformation is added to a `ScaledTranslation` to place |
| 27 | + the circles correctly |
| 28 | +
|
| 29 | +As a secondary showcase, this example shows how to work with datetimes |
| 30 | +along one axis when constructing the rectangular patches behind the |
| 31 | +circles. It also demonstrates a way to increment the line color cycler. |
| 32 | +""" |
| 33 | + |
| 34 | +fromdatetimeimportdatetime |
| 35 | + |
| 36 | +importnumpyasnp |
| 37 | + |
| 38 | +frommatplotlibimportdatesasmdate |
| 39 | +frommatplotlibimportpatchesasmpatch |
| 40 | +frommatplotlibimportpyplotasplt |
| 41 | +frommatplotlibimporttransformsasmtrans |
| 42 | + |
| 43 | +data= { |
| 44 | +"A": [datetime(2024,4,10,3,10,22), |
| 45 | +datetime(2024,4,10,3,21,13), |
| 46 | +datetime(2024,4,10,3,25,41)], |
| 47 | +"B": [datetime(2024,4,10,3,15,55), |
| 48 | +datetime(2024,4,10,3,40,8)], |
| 49 | +"C": [datetime(2024,4,10,3,12,18), |
| 50 | +datetime(2024,4,10,3,23,32), |
| 51 | +datetime(2024,4,10,3,32,12)], |
| 52 | +} |
| 53 | + |
| 54 | +fig,ax=plt.subplots(constrained_layout=True) |
| 55 | +ax.invert_yaxis() |
| 56 | + |
| 57 | +# The scaling transforms only need to be set up once |
| 58 | +vertical_scale_transform=mtrans.AffineDeltaTransform(ax.transData) |
| 59 | +reflection=mtrans.Affine2D.from_values(0,1,1,0,0,0) |
| 60 | +uniform_scale_transform=mtrans.blended_transform_factory( |
| 61 | +reflection+vertical_scale_transform+reflection,vertical_scale_transform) |
| 62 | + |
| 63 | +# Draw some rectangle spanning each dataset |
| 64 | +fori,datesinenumerate(data.values()): |
| 65 | +start=mdate.date2num(dates[0]) |
| 66 | +end=mdate.date2num(dates[-1]) |
| 67 | +width=end-start |
| 68 | +color=ax._get_lines.get_next_color() |
| 69 | +ax.add_patch(mpatch.Rectangle((start,i-0.4),width,0.8,color=color)) |
| 70 | + |
| 71 | +# Draw a circle at each event |
| 72 | +foreventindates: |
| 73 | +x=mdate.date2num(event) |
| 74 | +t=uniform_scale_transform+mtrans.ScaledTranslation(x,i,ax.transData) |
| 75 | +ax.add_patch(mpatch.Circle((0,0),0.2,facecolor="w",edgecolor="k", |
| 76 | +linewidth=2,transform=t)) |
| 77 | + |
| 78 | +# Set the y-axis to show the data labels |
| 79 | +ax.set_yticks(np.arange(len(data))) |
| 80 | +ax.set_yticklabels(list(data)) |
| 81 | + |
| 82 | +# Set the x-axis to display datetimes |
| 83 | +ax.xaxis.set_major_locator(locator:=mdate.AutoDateLocator()) |
| 84 | +ax.xaxis.set_major_formatter(mdate.AutoDateFormatter(locator)) |
| 85 | + |
| 86 | +ax.autoscale_view() |
| 87 | + |
| 88 | +plt.show() |