Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf72a2f4

Browse files
Implement Axes.label_lines
Co-authored-by: Robert Augustynowicz <robert.augustynowicz@mail.utoronto.ca>Co-authored-by: Omar Chehab <omarchehab98@gmail.com>Co-authored-by: Jinyang Hu <jinyang.hu@mail.utoronto.ca>Co-authored-by: Dennis Tismenko <dtismenkodeveloper@gmail.com>Co-authored-by: Jinming Zhang <jinming.zhang@mail.utoronto.ca>Closes:#12939
1 parent7125492 commitf72a2f4

15 files changed

+325
-20
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
New `~.axes.Axes.label_lines` method
2+
------------------------------------
3+
4+
A new `~.axes.Axes.label_lines` method has been added to label the end of lines on an axes.
5+
Previously, the user had to go through the hassle of positioning each label individually
6+
like the Bachelors degrees by gender example.
7+
8+
https://matplotlib.org/gallery/showcase/bachelors_degrees_by_gender.html
9+
10+
Now, to achieve the same effect, a user can simply call
11+
12+
ax.label_lines()

‎examples/showcase/bachelors_degrees_by_gender.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,30 +75,12 @@
7575
'Math and Statistics','Architecture','Physical Sciences',
7676
'Computer Science','Engineering']
7777

78-
y_offsets= {'Foreign Languages':0.5,'English':-0.5,
79-
'Communications\nand Journalism':0.75,
80-
'Art and Performance':-0.25,'Agriculture':1.25,
81-
'Social Sciences and History':0.25,'Business':-0.75,
82-
'Math and Statistics':0.75,'Architecture':-0.75,
83-
'Computer Science':0.75,'Engineering':-0.25}
84-
8578
forcolumninmajors:
8679
# Plot each line separately with its own color.
8780
column_rec_name=column.replace('\n','_').replace(' ','_')
88-
81+
column_label=column.replace('\n',' ').replace(' ',' ')
8982
line,=ax.plot('Year',column_rec_name,data=gender_degree_data,
90-
lw=2.5)
91-
92-
# Add a text label to the right end of every line. Most of the code below
93-
# is adding specific offsets y position because some labels overlapped.
94-
y_pos=gender_degree_data[column_rec_name][-1]-0.5
95-
96-
ifcolumniny_offsets:
97-
y_pos+=y_offsets[column]
98-
99-
# Again, make sure that all labels are large enough to be easily read
100-
# by the viewer.
101-
ax.text(2011.5,y_pos,column,fontsize=14,color=line.get_color())
83+
lw=2.5,label=column_label)
10284

10385
# Make the title big enough so it spans the entire plot, but don't make it
10486
# so big that it requires two lines to show.
@@ -108,6 +90,9 @@
10890
fig.suptitle("Percentage of Bachelor's degrees conferred to women in "
10991
"the U.S.A. by major (1970-2011)",fontsize=18,ha="center")
11092

93+
# Call the the label lines feature to add appropriate labels for each line
94+
plt.label_lines()
95+
11196
# Finally, save the figure as a PNG.
11297
# You can also save it as a PDF, JPEG, etc.
11398
# Just change the file extension in this call.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
====================================
3+
Line labels using pre-defined labels
4+
====================================
5+
6+
Defining line labels with plots.
7+
"""
8+
9+
10+
importnumpyasnp
11+
importmatplotlib.pyplotasplt
12+
13+
# Make some fake data.
14+
a=b=np.arange(0,3,.02)
15+
c=np.exp(a)
16+
d=c[::-1]
17+
18+
# Create plots with pre-defined labels.
19+
fig,ax=plt.subplots()
20+
ax.spines['top'].set_visible(False)
21+
ax.spines['right'].set_visible(False)
22+
ax.plot(a,c,'k--',label='Model length')
23+
ax.plot(a,d,'k:',label='Data length')
24+
ax.plot(a,c+d,'k',label='Total message length')
25+
26+
ax.label_lines()
27+
28+
plt.show()
29+
30+
#############################################################################
31+
#
32+
# ------------
33+
#
34+
# References
35+
# """"""""""
36+
#
37+
# The use of the following functions, methods, classes and modules is shown
38+
# in this example:
39+
40+
#import matplotlib
41+
#matplotlib.axes.Axes.plot
42+
#matplotlib.pyplot.plot
43+
#matplotlib.axes.Axes.label_lines
44+
#matplotlib.pyplot.label_lines

‎lib/matplotlib/axes/_axes.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,179 @@ def text(self, x, y, s, fontdict=None, **kwargs):
767767
self._add_text(t)
768768
returnt
769769

770+
deflabel_lines(self,*args,**kwargs):
771+
"""
772+
Place labels at the end of lines on the chart.
773+
774+
Call signatures::
775+
776+
label_lines()
777+
label_lines(labels)
778+
label_lines(handles, labels)
779+
780+
The call signatures correspond to three different ways of how to use
781+
this method.
782+
783+
The simplest way to use line_labels is without parameters. After the
784+
lines are created with their labels, the user can call this function
785+
which automatically applies the stored labels by the corresponding
786+
lines. This would be most effectively used after the complete creation
787+
of the line chart.
788+
789+
The second way to call this method is by specifying the first parameter
790+
which is labels. In doing so you would be able to name the lines if
791+
they lack names, or rename them as needed. This process occurs in the
792+
order in which the lines were added to the chart.
793+
794+
The Final way to use this method is specifying both the handles and the
795+
labels. In doing so you can specify names for select lines leaving the
796+
rest blank. This would be useful when users would want to specify
797+
select pieces of data to monitor when data clumps occur.
798+
799+
Parameters
800+
----------
801+
handles : sequence of `.Artist`, optional
802+
A list of Artists (lines) to be added to the line labels.
803+
Use this together with *labels*, if you need full control on what
804+
is shown in the line labels and the automatic mechanism described
805+
above is not sufficient.
806+
807+
The length of handles and labels should be the same in this
808+
case. If they are not, they are truncated to the smaller length.
809+
810+
labels : list of str, optional
811+
A list of labels to show next to the artists.
812+
Use this together with *handles*, if you need full control on what
813+
is shown in the line labels and the automatic mechanism described
814+
above is not sufficient.
815+
816+
Returns
817+
-------
818+
None
819+
820+
Notes
821+
-----
822+
Only line handles are supported by this method.
823+
824+
Examples
825+
--------
826+
.. plot:: gallery/text_labels_and_annotations/label_lines.py
827+
"""
828+
handles,labels,extra_args,kwargs=mlegend._parse_legend_args(
829+
[self],
830+
*args,
831+
**kwargs)
832+
iflen(extra_args):
833+
raiseTypeError(
834+
'label_lines only accepts two nonkeyword arguments')
835+
836+
self._line_labels= []
837+
838+
defget_last_data_point(handle):
839+
last_x=handle.get_xdata()[-1]
840+
last_y=handle.get_ydata()[-1]
841+
returnlast_x,last_y
842+
843+
xys=np.array([get_last_data_point(x)forxinhandles])
844+
845+
data_maxx,data_maxy=np.max(xys,axis=0)
846+
data_miny=np.min(xys,axis=0)[1]
847+
848+
fig_dpi_transform=self.figure.dpi_scale_trans.inverted()
849+
fig_bbox=self.get_window_extent().transformed(fig_dpi_transform)
850+
fig_width_px=fig_bbox.width*self.figure.dpi
851+
fig_height_px=fig_bbox.height*self.figure.dpi
852+
853+
fig_minx,fig_maxx=self.get_xbound()
854+
fig_miny,fig_maxy=self.get_ybound()
855+
fig_width_pt=abs(fig_maxx-fig_minx)
856+
fig_height_pt=abs(fig_maxy-fig_miny)
857+
858+
margin_left=8* (fig_width_pt/fig_width_px)
859+
margin_vertical=2* (fig_height_pt/fig_height_px)
860+
861+
text_fontsize=10
862+
text_height=text_fontsize* (fig_height_pt/fig_height_px)
863+
864+
bucket_height=text_height+2*margin_vertical
865+
buckets_total=1+int((fig_maxy-fig_miny-text_height)/
866+
bucket_height)
867+
buckets_map=0
868+
869+
defget_bucket_index(y):
870+
returnint((y-fig_miny)/bucket_height)
871+
872+
bucket_densities= [0]*buckets_total
873+
forxyinxys:
874+
data_x,data_y=xy
875+
ideal_bucket=get_bucket_index(data_y)
876+
ifideal_bucket>=0andideal_bucket<buckets_total:
877+
bucket_densities[ideal_bucket]+=1
878+
879+
defin_viewport(args):
880+
xy=args[2]
881+
x,y=xy
882+
returnfig_minx<x<fig_maxxandfig_miny<y<fig_maxy
883+
884+
defby_y(args):
885+
xy=args[2]
886+
x,y=xy
887+
returny
888+
889+
bucket_offset=None
890+
prev_ideal_bucket=-1
891+
forhandle,label,xyinsorted(filter(in_viewport,
892+
zip(handles,labels,xys)),
893+
key=by_y):
894+
data_x,data_y=xy
895+
896+
ideal_bucket=get_bucket_index(data_y)
897+
if (ideal_bucket!=prev_ideal_bucketand
898+
ideal_bucket<len(bucket_densities)):
899+
bucket_density=bucket_densities[ideal_bucket]
900+
ideal_offset=bucket_density//2
901+
empty_buckets_below=0
902+
foriinrange(ideal_bucket+1,
903+
ideal_bucket-ideal_offset+1,
904+
-1):
905+
ifi==0orbuckets_map& (1<<i)==1:
906+
break
907+
empty_buckets_below+=1
908+
bucket_offset=-min(ideal_offset,empty_buckets_below)
909+
prev_ideal_bucket=ideal_bucket
910+
911+
bucket=ideal_bucket+bucket_offset
912+
text_x,text_y=None,None
913+
914+
forindexinrange(buckets_total):
915+
bucket_index=max(0,min(bucket+index,buckets_total))
916+
bucket_mask=1<<bucket_index
917+
ifbuckets_map&bucket_mask==0:
918+
buckets_map|=bucket_mask
919+
text_x=data_maxx+margin_left
920+
text_y=bucket_index*bucket_height+fig_miny
921+
break
922+
923+
iftext_xisnotNoneandtext_yisnotNone:
924+
text_color=handle.get_color()
925+
y_axes=self.transLimits.transform((text_x,text_y))[1]
926+
line_label=self.annotate(label, (data_maxx,data_y),
927+
textcoords='axes fraction',
928+
xytext=(1,y_axes),
929+
fontsize=text_fontsize,
930+
color=text_color,
931+
annotation_clip=True)
932+
self._line_labels.append(line_label)
933+
934+
defhas_label_lines(self):
935+
returnself._line_labelsisnotNone
936+
937+
defrefresh_label_lines(self,*args,**kwargs):
938+
forlabelinself._line_labels:
939+
label.remove()
940+
self._line_labels=None
941+
self.label_lines(*args,**kwargs)
942+
770943
@cbook._rename_parameter("3.3","s","text")
771944
@docstring.dedent_interpd
772945
defannotate(self,text,xy,*args,**kwargs):

‎lib/matplotlib/axes/_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ def __init__(self, fig, rect,
567567

568568
self._layoutbox=None
569569
self._poslayoutbox=None
570+
self._line_labels=None
570571

571572
def__getstate__(self):
572573
# The renderer should be re-created by the figure, and then cached at

‎lib/matplotlib/backend_bases.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,6 +3056,11 @@ def draw(self):
30563056
# inline call to self.canvas.draw_idle().
30573057
def_draw(self):
30583058
forainself.canvas.figure.get_axes():
3059+
# TODO(henryhu123): Is this the right place do recompute the line
3060+
# labels?
3061+
ifa.has_label_lines():
3062+
a.refresh_label_lines()
3063+
30593064
xaxis=getattr(a,'xaxis',None)
30603065
yaxis=getattr(a,'yaxis',None)
30613066
locators= []
@@ -3086,6 +3091,8 @@ def _update_view(self):
30863091
# Restore both the original and modified positions
30873092
ax._set_position(pos_orig,'original')
30883093
ax._set_position(pos_active,'active')
3094+
ifax.has_label_lines():
3095+
ax.refresh_label_lines()
30893096
self.canvas.draw_idle()
30903097

30913098
defsave_figure(self,*args):

‎lib/matplotlib/pyplot.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2559,6 +2559,12 @@ def imshow(
25592559
return__ret
25602560

25612561

2562+
# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
2563+
@_copy_docstring_and_deprecators(Axes.label_lines)
2564+
deflabel_lines(*args,**kwargs):
2565+
returngca().label_lines(*args,**kwargs)
2566+
2567+
25622568
# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
25632569
@_copy_docstring_and_deprecators(Axes.legend)
25642570
deflegend(*args,**kwargs):
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp