_base.py 155 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284
  1. from collections import OrderedDict
  2. from contextlib import ExitStack
  3. import inspect
  4. import itertools
  5. import logging
  6. import math
  7. from numbers import Real
  8. from operator import attrgetter
  9. import types
  10. import numpy as np
  11. import matplotlib as mpl
  12. from matplotlib import cbook
  13. from matplotlib.cbook import _OrderedSet, _check_1d, index_of
  14. from matplotlib import docstring
  15. import matplotlib.colors as mcolors
  16. import matplotlib.lines as mlines
  17. import matplotlib.patches as mpatches
  18. import matplotlib.artist as martist
  19. import matplotlib.transforms as mtransforms
  20. import matplotlib.ticker as mticker
  21. import matplotlib.axis as maxis
  22. import matplotlib.spines as mspines
  23. import matplotlib.font_manager as font_manager
  24. import matplotlib.text as mtext
  25. import matplotlib.image as mimage
  26. from matplotlib.rcsetup import cycler, validate_axisbelow
  27. _log = logging.getLogger(__name__)
  28. class _axis_method_wrapper:
  29. """
  30. Helper to generate Axes methods wrapping Axis methods.
  31. After ::
  32. get_foo = _axis_method_wrapper("xaxis", "get_bar")
  33. (in the body of a class) ``get_foo`` is a method that forwards it arguments
  34. to the ``get_bar`` method of the ``xaxis`` attribute, and gets its
  35. signature and docstring from ``Axis.get_bar``.
  36. The docstring of ``get_foo`` is built by replacing "this Axis" by "the
  37. {attr_name}" (i.e., "the xaxis", "the yaxis") in the wrapped method's
  38. docstring; additional replacements can by given in *doc_sub*. The
  39. docstring is also dedented to simplify further manipulations.
  40. """
  41. def __init__(self, attr_name, method_name, *, doc_sub=None):
  42. self.attr_name = attr_name
  43. self.method_name = method_name
  44. self.doc_sub = doc_sub
  45. def __set_name__(self, owner, name):
  46. # This is called at the end of the class body as
  47. # ``self.__set_name__(cls, name_under_which_self_is_assigned)``; we
  48. # rely on that to give the wrapper the correct __name__/__qualname__.
  49. get_method = attrgetter(f"{self.attr_name}.{self.method_name}")
  50. def wrapper(self, *args, **kwargs):
  51. return get_method(self)(*args, **kwargs)
  52. wrapper.__module__ = owner.__module__
  53. wrapper.__name__ = name
  54. wrapper.__qualname__ = f"{owner.__qualname__}.{name}"
  55. # Manually copy the signature instead of using functools.wraps because
  56. # displaying the Axis method source when asking for the Axes method
  57. # source would be confusing.
  58. wrapped_method = getattr(maxis.Axis, self.method_name)
  59. wrapper.__signature__ = inspect.signature(wrapped_method)
  60. doc = wrapped_method.__doc__
  61. if doc:
  62. doc_sub = {"this Axis": f"the {self.attr_name}",
  63. **(self.doc_sub or {})}
  64. for k, v in doc_sub.items():
  65. assert k in doc, \
  66. (f"The definition of {wrapper.__qualname__} expected that "
  67. f"the docstring of Axis.{self.method_name} contains "
  68. f"{k!r} as a substring.")
  69. doc = doc.replace(k, v)
  70. wrapper.__doc__ = inspect.cleandoc(doc)
  71. setattr(owner, name, wrapper)
  72. def _process_plot_format(fmt):
  73. """
  74. Convert a MATLAB style color/line style format string to a (*linestyle*,
  75. *marker*, *color*) tuple.
  76. Example format strings include:
  77. * 'ko': black circles
  78. * '.b': blue dots
  79. * 'r--': red dashed lines
  80. * 'C2--': the third color in the color cycle, dashed lines
  81. See Also
  82. --------
  83. matplotlib.Line2D.lineStyles, matplotlib.colors.cnames
  84. All possible styles and color format strings.
  85. """
  86. linestyle = None
  87. marker = None
  88. color = None
  89. # Is fmt just a colorspec?
  90. try:
  91. color = mcolors.to_rgba(fmt)
  92. # We need to differentiate grayscale '1.0' from tri_down marker '1'
  93. try:
  94. fmtint = str(int(fmt))
  95. except ValueError:
  96. return linestyle, marker, color # Yes
  97. else:
  98. if fmt != fmtint:
  99. # user definitely doesn't want tri_down marker
  100. return linestyle, marker, color # Yes
  101. else:
  102. # ignore converted color
  103. color = None
  104. except ValueError:
  105. pass # No, not just a color.
  106. i = 0
  107. while i < len(fmt):
  108. c = fmt[i]
  109. if fmt[i:i+2] in mlines.lineStyles: # First, the two-char styles.
  110. if linestyle is not None:
  111. raise ValueError(
  112. 'Illegal format string "%s"; two linestyle symbols' % fmt)
  113. linestyle = fmt[i:i+2]
  114. i += 2
  115. elif c in mlines.lineStyles:
  116. if linestyle is not None:
  117. raise ValueError(
  118. 'Illegal format string "%s"; two linestyle symbols' % fmt)
  119. linestyle = c
  120. i += 1
  121. elif c in mlines.lineMarkers:
  122. if marker is not None:
  123. raise ValueError(
  124. 'Illegal format string "%s"; two marker symbols' % fmt)
  125. marker = c
  126. i += 1
  127. elif c in mcolors.get_named_colors_mapping():
  128. if color is not None:
  129. raise ValueError(
  130. 'Illegal format string "%s"; two color symbols' % fmt)
  131. color = c
  132. i += 1
  133. elif c == 'C' and i < len(fmt) - 1:
  134. color_cycle_number = int(fmt[i + 1])
  135. color = mcolors.to_rgba("C{}".format(color_cycle_number))
  136. i += 2
  137. else:
  138. raise ValueError(
  139. 'Unrecognized character %c in format string' % c)
  140. if linestyle is None and marker is None:
  141. linestyle = mpl.rcParams['lines.linestyle']
  142. if linestyle is None:
  143. linestyle = 'None'
  144. if marker is None:
  145. marker = 'None'
  146. return linestyle, marker, color
  147. class _process_plot_var_args:
  148. """
  149. Process variable length arguments to `~.Axes.plot`, to support ::
  150. plot(t, s)
  151. plot(t1, s1, t2, s2)
  152. plot(t1, s1, 'ko', t2, s2)
  153. plot(t1, s1, 'ko', t2, s2, 'r--', t3, e3)
  154. an arbitrary number of *x*, *y*, *fmt* are allowed
  155. """
  156. def __init__(self, axes, command='plot'):
  157. self.axes = axes
  158. self.command = command
  159. self.set_prop_cycle()
  160. def __getstate__(self):
  161. # note: it is not possible to pickle a generator (and thus a cycler).
  162. return {'axes': self.axes, 'command': self.command}
  163. def __setstate__(self, state):
  164. self.__dict__ = state.copy()
  165. self.set_prop_cycle()
  166. def set_prop_cycle(self, *args, **kwargs):
  167. # Can't do `args == (None,)` as that crashes cycler.
  168. if not (args or kwargs) or (len(args) == 1 and args[0] is None):
  169. prop_cycler = mpl.rcParams['axes.prop_cycle']
  170. else:
  171. prop_cycler = cycler(*args, **kwargs)
  172. self.prop_cycler = itertools.cycle(prop_cycler)
  173. # This should make a copy
  174. self._prop_keys = prop_cycler.keys
  175. def __call__(self, *args, data=None, **kwargs):
  176. self.axes._process_unit_info(kwargs=kwargs)
  177. for pos_only in "xy":
  178. if pos_only in kwargs:
  179. raise TypeError("{} got an unexpected keyword argument {!r}"
  180. .format(self.command, pos_only))
  181. if not args:
  182. return
  183. if data is None: # Process dict views
  184. args = [cbook.sanitize_sequence(a) for a in args]
  185. else: # Process the 'data' kwarg.
  186. replaced = [mpl._replacer(data, arg) for arg in args]
  187. if len(args) == 1:
  188. label_namer_idx = 0
  189. elif len(args) == 2: # Can be x, y or y, c.
  190. # Figure out what the second argument is.
  191. # 1) If the second argument cannot be a format shorthand, the
  192. # second argument is the label_namer.
  193. # 2) Otherwise (it could have been a format shorthand),
  194. # a) if we did perform a substitution, emit a warning, and
  195. # use it as label_namer.
  196. # b) otherwise, it is indeed a format shorthand; use the
  197. # first argument as label_namer.
  198. try:
  199. _process_plot_format(args[1])
  200. except ValueError: # case 1)
  201. label_namer_idx = 1
  202. else:
  203. if replaced[1] is not args[1]: # case 2a)
  204. cbook._warn_external(
  205. f"Second argument {args[1]!r} is ambiguous: could "
  206. f"be a format string but is in 'data'; using as "
  207. f"data. If it was intended as data, set the "
  208. f"format string to an empty string to suppress "
  209. f"this warning. If it was intended as a format "
  210. f"string, explicitly pass the x-values as well. "
  211. f"Alternatively, rename the entry in 'data'.",
  212. RuntimeWarning)
  213. label_namer_idx = 1
  214. else: # case 2b)
  215. label_namer_idx = 0
  216. elif len(args) == 3:
  217. label_namer_idx = 1
  218. else:
  219. raise ValueError(
  220. "Using arbitrary long args with data is not supported due "
  221. "to ambiguity of arguments; use multiple plotting calls "
  222. "instead")
  223. if kwargs.get("label") is None:
  224. kwargs["label"] = mpl._label_from_arg(
  225. replaced[label_namer_idx], args[label_namer_idx])
  226. args = replaced
  227. # Repeatedly grab (x, y) or (x, y, format) from the front of args and
  228. # massage them into arguments to plot() or fill().
  229. while args:
  230. this, args = args[:2], args[2:]
  231. if args and isinstance(args[0], str):
  232. this += args[0],
  233. args = args[1:]
  234. yield from self._plot_args(this, kwargs)
  235. def get_next_color(self):
  236. """Return the next color in the cycle."""
  237. if 'color' not in self._prop_keys:
  238. return 'k'
  239. return next(self.prop_cycler)['color']
  240. def _getdefaults(self, ignore, kw):
  241. """
  242. If some keys in the property cycle (excluding those in the set
  243. *ignore*) are absent or set to None in the dict *kw*, return a copy
  244. of the next entry in the property cycle, excluding keys in *ignore*.
  245. Otherwise, don't advance the property cycle, and return an empty dict.
  246. """
  247. prop_keys = self._prop_keys - ignore
  248. if any(kw.get(k, None) is None for k in prop_keys):
  249. # Need to copy this dictionary or else the next time around
  250. # in the cycle, the dictionary could be missing entries.
  251. default_dict = next(self.prop_cycler).copy()
  252. for p in ignore:
  253. default_dict.pop(p, None)
  254. else:
  255. default_dict = {}
  256. return default_dict
  257. def _setdefaults(self, defaults, kw):
  258. """
  259. Add to the dict *kw* the entries in the dict *default* that are absent
  260. or set to None in *kw*.
  261. """
  262. for k in defaults:
  263. if kw.get(k, None) is None:
  264. kw[k] = defaults[k]
  265. def _makeline(self, x, y, kw, kwargs):
  266. kw = {**kw, **kwargs} # Don't modify the original kw.
  267. default_dict = self._getdefaults(set(), kw)
  268. self._setdefaults(default_dict, kw)
  269. seg = mlines.Line2D(x, y, **kw)
  270. return seg
  271. def _makefill(self, x, y, kw, kwargs):
  272. # Polygon doesn't directly support unitized inputs.
  273. x = self.axes.convert_xunits(x)
  274. y = self.axes.convert_yunits(y)
  275. kw = kw.copy() # Don't modify the original kw.
  276. kwargs = kwargs.copy()
  277. # Ignore 'marker'-related properties as they aren't Polygon
  278. # properties, but they are Line2D properties, and so they are
  279. # likely to appear in the default cycler construction.
  280. # This is done here to the defaults dictionary as opposed to the
  281. # other two dictionaries because we do want to capture when a
  282. # *user* explicitly specifies a marker which should be an error.
  283. # We also want to prevent advancing the cycler if there are no
  284. # defaults needed after ignoring the given properties.
  285. ignores = {'marker', 'markersize', 'markeredgecolor',
  286. 'markerfacecolor', 'markeredgewidth'}
  287. # Also ignore anything provided by *kwargs*.
  288. for k, v in kwargs.items():
  289. if v is not None:
  290. ignores.add(k)
  291. # Only using the first dictionary to use as basis
  292. # for getting defaults for back-compat reasons.
  293. # Doing it with both seems to mess things up in
  294. # various places (probably due to logic bugs elsewhere).
  295. default_dict = self._getdefaults(ignores, kw)
  296. self._setdefaults(default_dict, kw)
  297. # Looks like we don't want "color" to be interpreted to
  298. # mean both facecolor and edgecolor for some reason.
  299. # So the "kw" dictionary is thrown out, and only its
  300. # 'color' value is kept and translated as a 'facecolor'.
  301. # This design should probably be revisited as it increases
  302. # complexity.
  303. facecolor = kw.get('color', None)
  304. # Throw out 'color' as it is now handled as a facecolor
  305. default_dict.pop('color', None)
  306. # To get other properties set from the cycler
  307. # modify the kwargs dictionary.
  308. self._setdefaults(default_dict, kwargs)
  309. seg = mpatches.Polygon(np.column_stack((x, y)),
  310. facecolor=facecolor,
  311. fill=kwargs.get('fill', True),
  312. closed=kw['closed'])
  313. seg.set(**kwargs)
  314. return seg
  315. def _plot_args(self, tup, kwargs):
  316. if len(tup) > 1 and isinstance(tup[-1], str):
  317. linestyle, marker, color = _process_plot_format(tup[-1])
  318. tup = tup[:-1]
  319. elif len(tup) == 3:
  320. raise ValueError('third arg must be a format string')
  321. else:
  322. linestyle, marker, color = None, None, None
  323. # Don't allow any None value; these would be up-converted to one
  324. # element array of None which causes problems downstream.
  325. if any(v is None for v in tup):
  326. raise ValueError("x, y, and format string must not be None")
  327. kw = {}
  328. for k, v in zip(('linestyle', 'marker', 'color'),
  329. (linestyle, marker, color)):
  330. if v is not None:
  331. kw[k] = v
  332. if len(tup) == 2:
  333. x = _check_1d(tup[0])
  334. y = _check_1d(tup[-1])
  335. else:
  336. x, y = index_of(tup[-1])
  337. if self.axes.xaxis is not None:
  338. self.axes.xaxis.update_units(x)
  339. if self.axes.yaxis is not None:
  340. self.axes.yaxis.update_units(y)
  341. if x.shape[0] != y.shape[0]:
  342. raise ValueError(f"x and y must have same first dimension, but "
  343. f"have shapes {x.shape} and {y.shape}")
  344. if x.ndim > 2 or y.ndim > 2:
  345. raise ValueError(f"x and y can be no greater than 2-D, but have "
  346. f"shapes {x.shape} and {y.shape}")
  347. if x.ndim == 1:
  348. x = x[:, np.newaxis]
  349. if y.ndim == 1:
  350. y = y[:, np.newaxis]
  351. if self.command == 'plot':
  352. func = self._makeline
  353. else:
  354. kw['closed'] = kwargs.get('closed', True)
  355. func = self._makefill
  356. ncx, ncy = x.shape[1], y.shape[1]
  357. if ncx > 1 and ncy > 1 and ncx != ncy:
  358. raise ValueError(f"x has {ncx} columns but y has {ncy} columns")
  359. return [func(x[:, j % ncx], y[:, j % ncy], kw, kwargs)
  360. for j in range(max(ncx, ncy))]
  361. @cbook._define_aliases({"facecolor": ["fc"]})
  362. class _AxesBase(martist.Artist):
  363. name = "rectilinear"
  364. _shared_x_axes = cbook.Grouper()
  365. _shared_y_axes = cbook.Grouper()
  366. _twinned_axes = cbook.Grouper()
  367. def __str__(self):
  368. return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})".format(
  369. type(self).__name__, self._position.bounds)
  370. def __init__(self, fig, rect,
  371. facecolor=None, # defaults to rc axes.facecolor
  372. frameon=True,
  373. sharex=None, # use Axes instance's xaxis info
  374. sharey=None, # use Axes instance's yaxis info
  375. label='',
  376. xscale=None,
  377. yscale=None,
  378. box_aspect=None,
  379. **kwargs
  380. ):
  381. """
  382. Build an axes in a figure.
  383. Parameters
  384. ----------
  385. fig : `~matplotlib.figure.Figure`
  386. The axes is build in the `.Figure` *fig*.
  387. rect : [left, bottom, width, height]
  388. The axes is build in the rectangle *rect*. *rect* is in
  389. `.Figure` coordinates.
  390. sharex, sharey : `~.axes.Axes`, optional
  391. The x or y `~.matplotlib.axis` is shared with the x or
  392. y axis in the input `~.axes.Axes`.
  393. frameon : bool, default: True
  394. Whether the axes frame is visible.
  395. box_aspect : None, or a number, optional
  396. Sets the aspect of the axes box. See `~.axes.Axes.set_box_aspect`
  397. for details.
  398. **kwargs
  399. Other optional keyword arguments:
  400. %(Axes)s
  401. Returns
  402. -------
  403. `~.axes.Axes`
  404. The new `~.axes.Axes` object.
  405. """
  406. martist.Artist.__init__(self)
  407. if isinstance(rect, mtransforms.Bbox):
  408. self._position = rect
  409. else:
  410. self._position = mtransforms.Bbox.from_bounds(*rect)
  411. if self._position.width < 0 or self._position.height < 0:
  412. raise ValueError('Width and height specified must be non-negative')
  413. self._originalPosition = self._position.frozen()
  414. self.axes = self
  415. self._aspect = 'auto'
  416. self._adjustable = 'box'
  417. self._anchor = 'C'
  418. self._stale_viewlim_x = False
  419. self._stale_viewlim_y = False
  420. self._sharex = sharex
  421. self._sharey = sharey
  422. self.set_label(label)
  423. self.set_figure(fig)
  424. self.set_box_aspect(box_aspect)
  425. self._axes_locator = None # Optionally set via update(kwargs).
  426. self.spines = self._gen_axes_spines()
  427. # this call may differ for non-sep axes, e.g., polar
  428. self._init_axis()
  429. if facecolor is None:
  430. facecolor = mpl.rcParams['axes.facecolor']
  431. self._facecolor = facecolor
  432. self._frameon = frameon
  433. self.set_axisbelow(mpl.rcParams['axes.axisbelow'])
  434. self._rasterization_zorder = None
  435. self.cla()
  436. # funcs used to format x and y - fall back on major formatters
  437. self.fmt_xdata = None
  438. self.fmt_ydata = None
  439. self.set_navigate(True)
  440. self.set_navigate_mode(None)
  441. if xscale:
  442. self.set_xscale(xscale)
  443. if yscale:
  444. self.set_yscale(yscale)
  445. self.update(kwargs)
  446. if self.xaxis is not None:
  447. self._xcid = self.xaxis.callbacks.connect(
  448. 'units finalize', lambda: self._on_units_changed(scalex=True))
  449. if self.yaxis is not None:
  450. self._ycid = self.yaxis.callbacks.connect(
  451. 'units finalize', lambda: self._on_units_changed(scaley=True))
  452. rcParams = mpl.rcParams
  453. self.tick_params(
  454. top=rcParams['xtick.top'] and rcParams['xtick.minor.top'],
  455. bottom=rcParams['xtick.bottom'] and rcParams['xtick.minor.bottom'],
  456. labeltop=(rcParams['xtick.labeltop'] and
  457. rcParams['xtick.minor.top']),
  458. labelbottom=(rcParams['xtick.labelbottom'] and
  459. rcParams['xtick.minor.bottom']),
  460. left=rcParams['ytick.left'] and rcParams['ytick.minor.left'],
  461. right=rcParams['ytick.right'] and rcParams['ytick.minor.right'],
  462. labelleft=(rcParams['ytick.labelleft'] and
  463. rcParams['ytick.minor.left']),
  464. labelright=(rcParams['ytick.labelright'] and
  465. rcParams['ytick.minor.right']),
  466. which='minor')
  467. self.tick_params(
  468. top=rcParams['xtick.top'] and rcParams['xtick.major.top'],
  469. bottom=rcParams['xtick.bottom'] and rcParams['xtick.major.bottom'],
  470. labeltop=(rcParams['xtick.labeltop'] and
  471. rcParams['xtick.major.top']),
  472. labelbottom=(rcParams['xtick.labelbottom'] and
  473. rcParams['xtick.major.bottom']),
  474. left=rcParams['ytick.left'] and rcParams['ytick.major.left'],
  475. right=rcParams['ytick.right'] and rcParams['ytick.major.right'],
  476. labelleft=(rcParams['ytick.labelleft'] and
  477. rcParams['ytick.major.left']),
  478. labelright=(rcParams['ytick.labelright'] and
  479. rcParams['ytick.major.right']),
  480. which='major')
  481. self._layoutbox = None
  482. self._poslayoutbox = None
  483. def __getstate__(self):
  484. # The renderer should be re-created by the figure, and then cached at
  485. # that point.
  486. state = super().__getstate__()
  487. for key in ['_layoutbox', '_poslayoutbox']:
  488. state[key] = None
  489. # Prune the sharing & twinning info to only contain the current group.
  490. for grouper_name in [
  491. '_shared_x_axes', '_shared_y_axes', '_twinned_axes']:
  492. grouper = getattr(self, grouper_name)
  493. state[grouper_name] = (grouper.get_siblings(self)
  494. if self in grouper else None)
  495. return state
  496. def __setstate__(self, state):
  497. # Merge the grouping info back into the global groupers.
  498. for grouper_name in [
  499. '_shared_x_axes', '_shared_y_axes', '_twinned_axes']:
  500. siblings = state.pop(grouper_name)
  501. if siblings:
  502. getattr(self, grouper_name).join(*siblings)
  503. self.__dict__ = state
  504. self._stale = True
  505. def get_window_extent(self, *args, **kwargs):
  506. """
  507. Return the axes bounding box in display space; *args* and *kwargs*
  508. are empty.
  509. This bounding box does not include the spines, ticks, ticklables,
  510. or other labels. For a bounding box including these elements use
  511. `~matplotlib.axes.Axes.get_tightbbox`.
  512. See Also
  513. --------
  514. matplotlib.axes.Axes.get_tightbbox
  515. matplotlib.axis.Axis.get_tightbbox
  516. matplotlib.spines.get_window_extent
  517. """
  518. return self.bbox
  519. def _init_axis(self):
  520. # This is moved out of __init__ because non-separable axes don't use it
  521. self.xaxis = maxis.XAxis(self)
  522. self.spines['bottom'].register_axis(self.xaxis)
  523. self.spines['top'].register_axis(self.xaxis)
  524. self.yaxis = maxis.YAxis(self)
  525. self.spines['left'].register_axis(self.yaxis)
  526. self.spines['right'].register_axis(self.yaxis)
  527. self._update_transScale()
  528. def set_figure(self, fig):
  529. # docstring inherited
  530. martist.Artist.set_figure(self, fig)
  531. self.bbox = mtransforms.TransformedBbox(self._position,
  532. fig.transFigure)
  533. # these will be updated later as data is added
  534. self.dataLim = mtransforms.Bbox.null()
  535. self._viewLim = mtransforms.Bbox.unit()
  536. self.transScale = mtransforms.TransformWrapper(
  537. mtransforms.IdentityTransform())
  538. self._set_lim_and_transforms()
  539. def _unstale_viewLim(self):
  540. # We should arrange to store this information once per share-group
  541. # instead of on every axis.
  542. scalex = any(ax._stale_viewlim_x
  543. for ax in self._shared_x_axes.get_siblings(self))
  544. scaley = any(ax._stale_viewlim_y
  545. for ax in self._shared_y_axes.get_siblings(self))
  546. if scalex or scaley:
  547. for ax in self._shared_x_axes.get_siblings(self):
  548. ax._stale_viewlim_x = False
  549. for ax in self._shared_y_axes.get_siblings(self):
  550. ax._stale_viewlim_y = False
  551. self.autoscale_view(scalex=scalex, scaley=scaley)
  552. @property
  553. def viewLim(self):
  554. self._unstale_viewLim()
  555. return self._viewLim
  556. # API could be better, right now this is just to match the old calls to
  557. # autoscale_view() after each plotting method.
  558. def _request_autoscale_view(self, tight=None, scalex=True, scaley=True):
  559. if tight is not None:
  560. self._tight = tight
  561. if scalex:
  562. self._stale_viewlim_x = True # Else keep old state.
  563. if scaley:
  564. self._stale_viewlim_y = True
  565. def _set_lim_and_transforms(self):
  566. """
  567. Set the *_xaxis_transform*, *_yaxis_transform*, *transScale*,
  568. *transData*, *transLimits* and *transAxes* transformations.
  569. .. note::
  570. This method is primarily used by rectilinear projections of the
  571. `~matplotlib.axes.Axes` class, and is meant to be overridden by
  572. new kinds of projection axes that need different transformations
  573. and limits. (See `~matplotlib.projections.polar.PolarAxes` for an
  574. example.)
  575. """
  576. self.transAxes = mtransforms.BboxTransformTo(self.bbox)
  577. # Transforms the x and y axis separately by a scale factor.
  578. # It is assumed that this part will have non-linear components
  579. # (e.g., for a log scale).
  580. self.transScale = mtransforms.TransformWrapper(
  581. mtransforms.IdentityTransform())
  582. # An affine transformation on the data, generally to limit the
  583. # range of the axes
  584. self.transLimits = mtransforms.BboxTransformFrom(
  585. mtransforms.TransformedBbox(self._viewLim, self.transScale))
  586. # The parentheses are important for efficiency here -- they
  587. # group the last two (which are usually affines) separately
  588. # from the first (which, with log-scaling can be non-affine).
  589. self.transData = self.transScale + (self.transLimits + self.transAxes)
  590. self._xaxis_transform = mtransforms.blended_transform_factory(
  591. self.transData, self.transAxes)
  592. self._yaxis_transform = mtransforms.blended_transform_factory(
  593. self.transAxes, self.transData)
  594. def get_xaxis_transform(self, which='grid'):
  595. """
  596. Get the transformation used for drawing x-axis labels, ticks
  597. and gridlines. The x-direction is in data coordinates and the
  598. y-direction is in axis coordinates.
  599. .. note::
  600. This transformation is primarily used by the
  601. `~matplotlib.axis.Axis` class, and is meant to be
  602. overridden by new kinds of projections that may need to
  603. place axis elements in different locations.
  604. """
  605. if which == 'grid':
  606. return self._xaxis_transform
  607. elif which == 'tick1':
  608. # for cartesian projection, this is bottom spine
  609. return self.spines['bottom'].get_spine_transform()
  610. elif which == 'tick2':
  611. # for cartesian projection, this is top spine
  612. return self.spines['top'].get_spine_transform()
  613. else:
  614. raise ValueError('unknown value for which')
  615. def get_xaxis_text1_transform(self, pad_points):
  616. """
  617. Returns
  618. -------
  619. transform : Transform
  620. The transform used for drawing x-axis labels, which will add
  621. *pad_points* of padding (in points) between the axes and the label.
  622. The x-direction is in data coordinates and the y-direction is in
  623. axis corrdinates
  624. valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
  625. The text vertical alignment.
  626. halign : {'center', 'left', 'right'}
  627. The text horizontal alignment.
  628. Notes
  629. -----
  630. This transformation is primarily used by the `~matplotlib.axis.Axis`
  631. class, and is meant to be overridden by new kinds of projections that
  632. may need to place axis elements in different locations.
  633. """
  634. labels_align = mpl.rcParams["xtick.alignment"]
  635. return (self.get_xaxis_transform(which='tick1') +
  636. mtransforms.ScaledTranslation(0, -1 * pad_points / 72,
  637. self.figure.dpi_scale_trans),
  638. "top", labels_align)
  639. def get_xaxis_text2_transform(self, pad_points):
  640. """
  641. Returns
  642. -------
  643. transform : Transform
  644. The transform used for drawing secondary x-axis labels, which will
  645. add *pad_points* of padding (in points) between the axes and the
  646. label. The x-direction is in data coordinates and the y-direction
  647. is in axis corrdinates
  648. valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
  649. The text vertical alignment.
  650. halign : {'center', 'left', 'right'}
  651. The text horizontal alignment.
  652. Notes
  653. -----
  654. This transformation is primarily used by the `~matplotlib.axis.Axis`
  655. class, and is meant to be overridden by new kinds of projections that
  656. may need to place axis elements in different locations.
  657. """
  658. labels_align = mpl.rcParams["xtick.alignment"]
  659. return (self.get_xaxis_transform(which='tick2') +
  660. mtransforms.ScaledTranslation(0, pad_points / 72,
  661. self.figure.dpi_scale_trans),
  662. "bottom", labels_align)
  663. def get_yaxis_transform(self, which='grid'):
  664. """
  665. Get the transformation used for drawing y-axis labels, ticks
  666. and gridlines. The x-direction is in axis coordinates and the
  667. y-direction is in data coordinates.
  668. .. note::
  669. This transformation is primarily used by the
  670. `~matplotlib.axis.Axis` class, and is meant to be
  671. overridden by new kinds of projections that may need to
  672. place axis elements in different locations.
  673. """
  674. if which == 'grid':
  675. return self._yaxis_transform
  676. elif which == 'tick1':
  677. # for cartesian projection, this is bottom spine
  678. return self.spines['left'].get_spine_transform()
  679. elif which == 'tick2':
  680. # for cartesian projection, this is top spine
  681. return self.spines['right'].get_spine_transform()
  682. else:
  683. raise ValueError('unknown value for which')
  684. def get_yaxis_text1_transform(self, pad_points):
  685. """
  686. Returns
  687. -------
  688. transform : Transform
  689. The transform used for drawing y-axis labels, which will add
  690. *pad_points* of padding (in points) between the axes and the label.
  691. The x-direction is in axis coordinates and the y-direction is in
  692. data corrdinates
  693. valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
  694. The text vertical alignment.
  695. halign : {'center', 'left', 'right'}
  696. The text horizontal alignment.
  697. Notes
  698. -----
  699. This transformation is primarily used by the `~matplotlib.axis.Axis`
  700. class, and is meant to be overridden by new kinds of projections that
  701. may need to place axis elements in different locations.
  702. """
  703. labels_align = mpl.rcParams["ytick.alignment"]
  704. return (self.get_yaxis_transform(which='tick1') +
  705. mtransforms.ScaledTranslation(-1 * pad_points / 72, 0,
  706. self.figure.dpi_scale_trans),
  707. labels_align, "right")
  708. def get_yaxis_text2_transform(self, pad_points):
  709. """
  710. Returns
  711. -------
  712. transform : Transform
  713. The transform used for drawing secondart y-axis labels, which will
  714. add *pad_points* of padding (in points) between the axes and the
  715. label. The x-direction is in axis coordinates and the y-direction
  716. is in data corrdinates
  717. valign : {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
  718. The text vertical alignment.
  719. halign : {'center', 'left', 'right'}
  720. The text horizontal alignment.
  721. Notes
  722. -----
  723. This transformation is primarily used by the `~matplotlib.axis.Axis`
  724. class, and is meant to be overridden by new kinds of projections that
  725. may need to place axis elements in different locations.
  726. """
  727. labels_align = mpl.rcParams["ytick.alignment"]
  728. return (self.get_yaxis_transform(which='tick2') +
  729. mtransforms.ScaledTranslation(pad_points / 72, 0,
  730. self.figure.dpi_scale_trans),
  731. labels_align, "left")
  732. def _update_transScale(self):
  733. self.transScale.set(
  734. mtransforms.blended_transform_factory(
  735. self.xaxis.get_transform(), self.yaxis.get_transform()))
  736. for line in getattr(self, "lines", []): # Not set during init.
  737. try:
  738. line._transformed_path.invalidate()
  739. except AttributeError:
  740. pass
  741. def get_position(self, original=False):
  742. """
  743. Get a copy of the axes rectangle as a `.Bbox`.
  744. Parameters
  745. ----------
  746. original : bool
  747. If ``True``, return the original position. Otherwise return the
  748. active position. For an explanation of the positions see
  749. `.set_position`.
  750. Returns
  751. -------
  752. `.Bbox`
  753. """
  754. if original:
  755. return self._originalPosition.frozen()
  756. else:
  757. locator = self.get_axes_locator()
  758. if not locator:
  759. self.apply_aspect()
  760. return self._position.frozen()
  761. def set_position(self, pos, which='both'):
  762. """
  763. Set the axes position.
  764. Axes have two position attributes. The 'original' position is the
  765. position allocated for the Axes. The 'active' position is the
  766. position the Axes is actually drawn at. These positions are usually
  767. the same unless a fixed aspect is set to the Axes. See `.set_aspect`
  768. for details.
  769. Parameters
  770. ----------
  771. pos : [left, bottom, width, height] or `~matplotlib.transforms.Bbox`
  772. The new position of the in `.Figure` coordinates.
  773. which : {'both', 'active', 'original'}, default: 'both'
  774. Determines which position variables to change.
  775. """
  776. self._set_position(pos, which=which)
  777. # because this is being called externally to the library we
  778. # zero the constrained layout parts.
  779. self._layoutbox = None
  780. self._poslayoutbox = None
  781. def _set_position(self, pos, which='both'):
  782. """
  783. Private version of set_position.
  784. Call this internally to get the same functionality of `get_position`,
  785. but not to take the axis out of the constrained_layout hierarchy.
  786. """
  787. if not isinstance(pos, mtransforms.BboxBase):
  788. pos = mtransforms.Bbox.from_bounds(*pos)
  789. for ax in self._twinned_axes.get_siblings(self):
  790. if which in ('both', 'active'):
  791. ax._position.set(pos)
  792. if which in ('both', 'original'):
  793. ax._originalPosition.set(pos)
  794. self.stale = True
  795. def reset_position(self):
  796. """
  797. Reset the active position to the original position.
  798. This resets the a possible position change due to aspect constraints.
  799. For an explanation of the positions see `.set_position`.
  800. """
  801. for ax in self._twinned_axes.get_siblings(self):
  802. pos = ax.get_position(original=True)
  803. ax.set_position(pos, which='active')
  804. def set_axes_locator(self, locator):
  805. """
  806. Set the axes locator.
  807. Parameters
  808. ----------
  809. locator : Callable[[Axes, Renderer], Bbox]
  810. """
  811. self._axes_locator = locator
  812. self.stale = True
  813. def get_axes_locator(self):
  814. """
  815. Return the axes_locator.
  816. """
  817. return self._axes_locator
  818. def _set_artist_props(self, a):
  819. """Set the boilerplate props for artists added to axes."""
  820. a.set_figure(self.figure)
  821. if not a.is_transform_set():
  822. a.set_transform(self.transData)
  823. a.axes = self
  824. if a.mouseover:
  825. self._mouseover_set.add(a)
  826. def _gen_axes_patch(self):
  827. """
  828. Returns
  829. -------
  830. Patch
  831. The patch used to draw the background of the axes. It is also used
  832. as the clipping path for any data elements on the axes.
  833. In the standard axes, this is a rectangle, but in other projections
  834. it may not be.
  835. Notes
  836. -----
  837. Intended to be overridden by new projection types.
  838. """
  839. return mpatches.Rectangle((0.0, 0.0), 1.0, 1.0)
  840. def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
  841. """
  842. Returns
  843. -------
  844. dict
  845. Mapping of spine names to `.Line2D` or `.Patch` instances that are
  846. used to draw axes spines.
  847. In the standard axes, spines are single line segments, but in other
  848. projections they may not be.
  849. Notes
  850. -----
  851. Intended to be overridden by new projection types.
  852. """
  853. return OrderedDict((side, mspines.Spine.linear_spine(self, side))
  854. for side in ['left', 'right', 'bottom', 'top'])
  855. def sharex(self, other):
  856. """
  857. Share the x-axis with *other*.
  858. This is equivalent to passing ``sharex=other`` when constructing the
  859. axes, and cannot be used if the x-axis is already being shared with
  860. another axes.
  861. """
  862. cbook._check_isinstance(_AxesBase, other=other)
  863. if self._sharex is not None and other is not self._sharex:
  864. raise ValueError("x-axis is already shared")
  865. self._shared_x_axes.join(self, other)
  866. self._sharex = other
  867. self.xaxis.major = other.xaxis.major # Ticker instances holding
  868. self.xaxis.minor = other.xaxis.minor # locator and formatter.
  869. x0, x1 = other.get_xlim()
  870. self.set_xlim(x0, x1, emit=False, auto=other.get_autoscalex_on())
  871. self.xaxis._scale = other.xaxis._scale
  872. def sharey(self, other):
  873. """
  874. Share the y-axis with *other*.
  875. This is equivalent to passing ``sharey=other`` when constructing the
  876. axes, and cannot be used if the y-axis is already being shared with
  877. another axes.
  878. """
  879. cbook._check_isinstance(_AxesBase, other=other)
  880. if self._sharey is not None and other is not self._sharey:
  881. raise ValueError("y-axis is already shared")
  882. self._shared_y_axes.join(self, other)
  883. self._sharey = other
  884. self.yaxis.major = other.yaxis.major # Ticker instances holding
  885. self.yaxis.minor = other.yaxis.minor # locator and formatter.
  886. y0, y1 = other.get_ylim()
  887. self.set_ylim(y0, y1, emit=False, auto=other.get_autoscaley_on())
  888. self.yaxis._scale = other.yaxis._scale
  889. def cla(self):
  890. """Clear the current axes."""
  891. # Note: this is called by Axes.__init__()
  892. # stash the current visibility state
  893. if hasattr(self, 'patch'):
  894. patch_visible = self.patch.get_visible()
  895. else:
  896. patch_visible = True
  897. xaxis_visible = self.xaxis.get_visible()
  898. yaxis_visible = self.yaxis.get_visible()
  899. self.xaxis.cla()
  900. self.yaxis.cla()
  901. for name, spine in self.spines.items():
  902. spine.cla()
  903. self.ignore_existing_data_limits = True
  904. self.callbacks = cbook.CallbackRegistry()
  905. if self._sharex is not None:
  906. self.sharex(self._sharex)
  907. else:
  908. self.xaxis._set_scale('linear')
  909. try:
  910. self.set_xlim(0, 1)
  911. except TypeError:
  912. pass
  913. if self._sharey is not None:
  914. self.sharey(self._sharey)
  915. else:
  916. self.yaxis._set_scale('linear')
  917. try:
  918. self.set_ylim(0, 1)
  919. except TypeError:
  920. pass
  921. # update the minor locator for x and y axis based on rcParams
  922. if mpl.rcParams['xtick.minor.visible']:
  923. self.xaxis.set_minor_locator(mticker.AutoMinorLocator())
  924. if mpl.rcParams['ytick.minor.visible']:
  925. self.yaxis.set_minor_locator(mticker.AutoMinorLocator())
  926. if self._sharex is None:
  927. self._autoscaleXon = True
  928. if self._sharey is None:
  929. self._autoscaleYon = True
  930. self._xmargin = mpl.rcParams['axes.xmargin']
  931. self._ymargin = mpl.rcParams['axes.ymargin']
  932. self._tight = None
  933. self._use_sticky_edges = True
  934. self._update_transScale() # needed?
  935. self._get_lines = _process_plot_var_args(self)
  936. self._get_patches_for_fill = _process_plot_var_args(self, 'fill')
  937. self._gridOn = mpl.rcParams['axes.grid']
  938. self.lines = []
  939. self.patches = []
  940. self.texts = []
  941. self.tables = []
  942. self.artists = []
  943. self.images = []
  944. self._mouseover_set = _OrderedSet()
  945. self.child_axes = []
  946. self._current_image = None # strictly for pyplot via _sci, _gci
  947. self.legend_ = None
  948. self.collections = [] # collection.Collection instances
  949. self.containers = []
  950. self.grid(False) # Disable grid on init to use rcParameter
  951. self.grid(self._gridOn, which=mpl.rcParams['axes.grid.which'],
  952. axis=mpl.rcParams['axes.grid.axis'])
  953. props = font_manager.FontProperties(
  954. size=mpl.rcParams['axes.titlesize'],
  955. weight=mpl.rcParams['axes.titleweight'])
  956. y = mpl.rcParams['axes.titley']
  957. if y is None:
  958. y = 1.0
  959. self._autotitlepos = True
  960. else:
  961. self._autotitlepos = False
  962. self.title = mtext.Text(
  963. x=0.5, y=y, text='',
  964. fontproperties=props,
  965. verticalalignment='baseline',
  966. horizontalalignment='center',
  967. )
  968. self._left_title = mtext.Text(
  969. x=0.0, y=y, text='',
  970. fontproperties=props.copy(),
  971. verticalalignment='baseline',
  972. horizontalalignment='left', )
  973. self._right_title = mtext.Text(
  974. x=1.0, y=y, text='',
  975. fontproperties=props.copy(),
  976. verticalalignment='baseline',
  977. horizontalalignment='right',
  978. )
  979. title_offset_points = mpl.rcParams['axes.titlepad']
  980. # refactor this out so it can be called in ax.set_title if
  981. # pad argument used...
  982. self._set_title_offset_trans(title_offset_points)
  983. for _title in (self.title, self._left_title, self._right_title):
  984. self._set_artist_props(_title)
  985. # The patch draws the background of the axes. We want this to be below
  986. # the other artists. We use the frame to draw the edges so we are
  987. # setting the edgecolor to None.
  988. self.patch = self._gen_axes_patch()
  989. self.patch.set_figure(self.figure)
  990. self.patch.set_facecolor(self._facecolor)
  991. self.patch.set_edgecolor('None')
  992. self.patch.set_linewidth(0)
  993. self.patch.set_transform(self.transAxes)
  994. self.set_axis_on()
  995. self.xaxis.set_clip_path(self.patch)
  996. self.yaxis.set_clip_path(self.patch)
  997. self._shared_x_axes.clean()
  998. self._shared_y_axes.clean()
  999. if self._sharex is not None:
  1000. self.xaxis.set_visible(xaxis_visible)
  1001. self.patch.set_visible(patch_visible)
  1002. if self._sharey is not None:
  1003. self.yaxis.set_visible(yaxis_visible)
  1004. self.patch.set_visible(patch_visible)
  1005. self.stale = True
  1006. def clear(self):
  1007. """Clear the axes."""
  1008. self.cla()
  1009. def get_facecolor(self):
  1010. """Get the facecolor of the Axes."""
  1011. return self.patch.get_facecolor()
  1012. def set_facecolor(self, color):
  1013. """
  1014. Set the facecolor of the Axes.
  1015. Parameters
  1016. ----------
  1017. color : color
  1018. """
  1019. self._facecolor = color
  1020. self.stale = True
  1021. return self.patch.set_facecolor(color)
  1022. def _set_title_offset_trans(self, title_offset_points):
  1023. """
  1024. Set the offset for the title either from :rc:`axes.titlepad`
  1025. or from set_title kwarg ``pad``.
  1026. """
  1027. self.titleOffsetTrans = mtransforms.ScaledTranslation(
  1028. 0.0, title_offset_points / 72,
  1029. self.figure.dpi_scale_trans)
  1030. for _title in (self.title, self._left_title, self._right_title):
  1031. _title.set_transform(self.transAxes + self.titleOffsetTrans)
  1032. _title.set_clip_box(None)
  1033. def set_prop_cycle(self, *args, **kwargs):
  1034. """
  1035. Set the property cycle of the Axes.
  1036. The property cycle controls the style properties such as color,
  1037. marker and linestyle of future plot commands. The style properties
  1038. of data already added to the Axes are not modified.
  1039. Call signatures::
  1040. set_prop_cycle(cycler)
  1041. set_prop_cycle(label=values[, label2=values2[, ...]])
  1042. set_prop_cycle(label, values)
  1043. Form 1 sets given `~cycler.Cycler` object.
  1044. Form 2 creates a `~cycler.Cycler` which cycles over one or more
  1045. properties simultaneously and set it as the property cycle of the
  1046. axes. If multiple properties are given, their value lists must have
  1047. the same length. This is just a shortcut for explicitly creating a
  1048. cycler and passing it to the function, i.e. it's short for
  1049. ``set_prop_cycle(cycler(label=values label2=values2, ...))``.
  1050. Form 3 creates a `~cycler.Cycler` for a single property and set it
  1051. as the property cycle of the axes. This form exists for compatibility
  1052. with the original `cycler.cycler` interface. Its use is discouraged
  1053. in favor of the kwarg form, i.e. ``set_prop_cycle(label=values)``.
  1054. Parameters
  1055. ----------
  1056. cycler : Cycler
  1057. Set the given Cycler. *None* resets to the cycle defined by the
  1058. current style.
  1059. label : str
  1060. The property key. Must be a valid `.Artist` property.
  1061. For example, 'color' or 'linestyle'. Aliases are allowed,
  1062. such as 'c' for 'color' and 'lw' for 'linewidth'.
  1063. values : iterable
  1064. Finite-length iterable of the property values. These values
  1065. are validated and will raise a ValueError if invalid.
  1066. See Also
  1067. --------
  1068. matplotlib.rcsetup.cycler
  1069. Convenience function for creating validated cyclers for properties.
  1070. cycler.cycler
  1071. The original function for creating unvalidated cyclers.
  1072. Examples
  1073. --------
  1074. Setting the property cycle for a single property:
  1075. >>> ax.set_prop_cycle(color=['red', 'green', 'blue'])
  1076. Setting the property cycle for simultaneously cycling over multiple
  1077. properties (e.g. red circle, green plus, blue cross):
  1078. >>> ax.set_prop_cycle(color=['red', 'green', 'blue'],
  1079. ... marker=['o', '+', 'x'])
  1080. """
  1081. if args and kwargs:
  1082. raise TypeError("Cannot supply both positional and keyword "
  1083. "arguments to this method.")
  1084. # Can't do `args == (None,)` as that crashes cycler.
  1085. if len(args) == 1 and args[0] is None:
  1086. prop_cycle = None
  1087. else:
  1088. prop_cycle = cycler(*args, **kwargs)
  1089. self._get_lines.set_prop_cycle(prop_cycle)
  1090. self._get_patches_for_fill.set_prop_cycle(prop_cycle)
  1091. def get_aspect(self):
  1092. return self._aspect
  1093. def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
  1094. """
  1095. Set the aspect of the axis scaling, i.e. the ratio of y-unit to x-unit.
  1096. Parameters
  1097. ----------
  1098. aspect : {'auto'} or num
  1099. Possible values:
  1100. ======== =================================================
  1101. value description
  1102. ======== =================================================
  1103. 'auto' automatic; fill the position rectangle with data.
  1104. num a circle will be stretched such that the height
  1105. is *num* times the width. 'equal' is a synonym
  1106. for ``aspect=1``, i.e. same scaling for x and y.
  1107. ======== =================================================
  1108. adjustable : None or {'box', 'datalim'}, optional
  1109. If not ``None``, this defines which parameter will be adjusted to
  1110. meet the required aspect. See `.set_adjustable` for further
  1111. details.
  1112. anchor : None or str or 2-tuple of float, optional
  1113. If not ``None``, this defines where the Axes will be drawn if there
  1114. is extra space due to aspect constraints. The most common way to
  1115. to specify the anchor are abbreviations of cardinal directions:
  1116. ===== =====================
  1117. value description
  1118. ===== =====================
  1119. 'C' centered
  1120. 'SW' lower left corner
  1121. 'S' middle of bottom edge
  1122. 'SE' lower right corner
  1123. etc.
  1124. ===== =====================
  1125. See `.set_anchor` for further details.
  1126. share : bool, default: False
  1127. If ``True``, apply the settings to all shared Axes.
  1128. See Also
  1129. --------
  1130. matplotlib.axes.Axes.set_adjustable
  1131. Set how the Axes adjusts to achieve the required aspect ratio.
  1132. matplotlib.axes.Axes.set_anchor
  1133. Set the position in case of extra space.
  1134. """
  1135. if cbook._str_equal(aspect, 'equal'):
  1136. aspect = 1
  1137. if not cbook._str_equal(aspect, 'auto'):
  1138. aspect = float(aspect) # raise ValueError if necessary
  1139. if share:
  1140. axes = {*self._shared_x_axes.get_siblings(self),
  1141. *self._shared_y_axes.get_siblings(self)}
  1142. else:
  1143. axes = [self]
  1144. for ax in axes:
  1145. ax._aspect = aspect
  1146. if adjustable is None:
  1147. adjustable = self._adjustable
  1148. self.set_adjustable(adjustable, share=share) # Handle sharing.
  1149. if anchor is not None:
  1150. self.set_anchor(anchor, share=share)
  1151. self.stale = True
  1152. def get_adjustable(self):
  1153. """
  1154. Return whether the Axes will adjust its physical dimension ('box') or
  1155. its data limits ('datalim') to achieve the desired aspect ratio.
  1156. See Also
  1157. --------
  1158. matplotlib.axes.Axes.set_adjustable
  1159. Set how the Axes adjusts to achieve the required aspect ratio.
  1160. matplotlib.axes.Axes.set_aspect
  1161. For a description of aspect handling.
  1162. """
  1163. return self._adjustable
  1164. def set_adjustable(self, adjustable, share=False):
  1165. """
  1166. Set how the Axes adjusts to achieve the required aspect ratio.
  1167. Parameters
  1168. ----------
  1169. adjustable : {'box', 'datalim'}
  1170. If 'box', change the physical dimensions of the Axes.
  1171. If 'datalim', change the ``x`` or ``y`` data limits.
  1172. share : bool, default: False
  1173. If ``True``, apply the settings to all shared Axes.
  1174. See Also
  1175. --------
  1176. matplotlib.axes.Axes.set_aspect
  1177. For a description of aspect handling.
  1178. Notes
  1179. -----
  1180. Shared Axes (of which twinned Axes are a special case)
  1181. impose restrictions on how aspect ratios can be imposed.
  1182. For twinned Axes, use 'datalim'. For Axes that share both
  1183. x and y, use 'box'. Otherwise, either 'datalim' or 'box'
  1184. may be used. These limitations are partly a requirement
  1185. to avoid over-specification, and partly a result of the
  1186. particular implementation we are currently using, in
  1187. which the adjustments for aspect ratios are done sequentially
  1188. and independently on each Axes as it is drawn.
  1189. """
  1190. cbook._check_in_list(["box", "datalim"], adjustable=adjustable)
  1191. if share:
  1192. axs = {*self._shared_x_axes.get_siblings(self),
  1193. *self._shared_y_axes.get_siblings(self)}
  1194. else:
  1195. axs = [self]
  1196. if (adjustable == "datalim"
  1197. and any(getattr(ax.get_data_ratio, "__func__", None)
  1198. != _AxesBase.get_data_ratio
  1199. for ax in axs)):
  1200. # Limits adjustment by apply_aspect assumes that the axes' aspect
  1201. # ratio can be computed from the data limits and scales.
  1202. raise ValueError("Cannot set axes adjustable to 'datalim' for "
  1203. "Axes which override 'get_data_ratio'")
  1204. for ax in axs:
  1205. ax._adjustable = adjustable
  1206. self.stale = True
  1207. def get_box_aspect(self):
  1208. """
  1209. Get the axes box aspect.
  1210. Will be ``None`` if not explicitly specified.
  1211. See Also
  1212. --------
  1213. matplotlib.axes.Axes.set_box_aspect
  1214. for a description of box aspect.
  1215. matplotlib.axes.Axes.set_aspect
  1216. for a description of aspect handling.
  1217. """
  1218. return self._box_aspect
  1219. def set_box_aspect(self, aspect=None):
  1220. """
  1221. Set the axes box aspect. The box aspect is the ratio of the
  1222. axes height to the axes width in physical units. This is not to be
  1223. confused with the data aspect, set via `~.Axes.set_aspect`.
  1224. Parameters
  1225. ----------
  1226. aspect : None, or a number
  1227. Changes the physical dimensions of the Axes, such that the ratio
  1228. of the axes height to the axes width in physical units is equal to
  1229. *aspect*. If *None*, the axes geometry will not be adjusted.
  1230. Note that calling this function with a number changes the *adjustable*
  1231. to *datalim*.
  1232. See Also
  1233. --------
  1234. matplotlib.axes.Axes.set_aspect
  1235. for a description of aspect handling.
  1236. """
  1237. axs = {*self._twinned_axes.get_siblings(self),
  1238. *self._twinned_axes.get_siblings(self)}
  1239. if aspect is not None:
  1240. aspect = float(aspect)
  1241. # when box_aspect is set to other than ´None`,
  1242. # adjustable must be "datalim"
  1243. for ax in axs:
  1244. ax.set_adjustable("datalim")
  1245. for ax in axs:
  1246. ax._box_aspect = aspect
  1247. ax.stale = True
  1248. def get_anchor(self):
  1249. """
  1250. Get the anchor location.
  1251. See Also
  1252. --------
  1253. matplotlib.axes.Axes.set_anchor
  1254. for a description of the anchor.
  1255. matplotlib.axes.Axes.set_aspect
  1256. for a description of aspect handling.
  1257. """
  1258. return self._anchor
  1259. def set_anchor(self, anchor, share=False):
  1260. """
  1261. Define the anchor location.
  1262. The actual drawing area (active position) of the Axes may be smaller
  1263. than the Bbox (original position) when a fixed aspect is required. The
  1264. anchor defines where the drawing area will be located within the
  1265. available space.
  1266. Parameters
  1267. ----------
  1268. anchor : 2-tuple of floats or {'C', 'SW', 'S', 'SE', ...}
  1269. The anchor position may be either:
  1270. - a sequence (*cx*, *cy*). *cx* and *cy* may range from 0
  1271. to 1, where 0 is left or bottom and 1 is right or top.
  1272. - a string using cardinal directions as abbreviation:
  1273. - 'C' for centered
  1274. - 'S' (south) for bottom-center
  1275. - 'SW' (south west) for bottom-left
  1276. - etc.
  1277. Here is an overview of the possible positions:
  1278. +------+------+------+
  1279. | 'NW' | 'N' | 'NE' |
  1280. +------+------+------+
  1281. | 'W' | 'C' | 'E' |
  1282. +------+------+------+
  1283. | 'SW' | 'S' | 'SE' |
  1284. +------+------+------+
  1285. share : bool, default: False
  1286. If ``True``, apply the settings to all shared Axes.
  1287. See Also
  1288. --------
  1289. matplotlib.axes.Axes.set_aspect
  1290. for a description of aspect handling.
  1291. """
  1292. if not (anchor in mtransforms.Bbox.coefs or len(anchor) == 2):
  1293. raise ValueError('argument must be among %s' %
  1294. ', '.join(mtransforms.Bbox.coefs))
  1295. if share:
  1296. axes = {*self._shared_x_axes.get_siblings(self),
  1297. *self._shared_y_axes.get_siblings(self)}
  1298. else:
  1299. axes = [self]
  1300. for ax in axes:
  1301. ax._anchor = anchor
  1302. self.stale = True
  1303. def get_data_ratio(self):
  1304. """
  1305. Return the aspect ratio of the scaled data.
  1306. Notes
  1307. -----
  1308. This method is intended to be overridden by new projection types.
  1309. """
  1310. txmin, txmax = self.xaxis.get_transform().transform(self.get_xbound())
  1311. tymin, tymax = self.yaxis.get_transform().transform(self.get_ybound())
  1312. xsize = max(abs(txmax - txmin), 1e-30)
  1313. ysize = max(abs(tymax - tymin), 1e-30)
  1314. return ysize / xsize
  1315. @cbook.deprecated("3.2")
  1316. def get_data_ratio_log(self):
  1317. """
  1318. Return the aspect ratio of the raw data in log scale.
  1319. Notes
  1320. -----
  1321. Will be used when both axis are in log scale.
  1322. """
  1323. xmin, xmax = self.get_xbound()
  1324. ymin, ymax = self.get_ybound()
  1325. xsize = max(abs(math.log10(xmax) - math.log10(xmin)), 1e-30)
  1326. ysize = max(abs(math.log10(ymax) - math.log10(ymin)), 1e-30)
  1327. return ysize / xsize
  1328. def apply_aspect(self, position=None):
  1329. """
  1330. Adjust the Axes for a specified data aspect ratio.
  1331. Depending on `.get_adjustable` this will modify either the
  1332. Axes box (position) or the view limits. In the former case,
  1333. `~matplotlib.axes.Axes.get_anchor` will affect the position.
  1334. Notes
  1335. -----
  1336. This is called automatically when each Axes is drawn. You may need
  1337. to call it yourself if you need to update the Axes position and/or
  1338. view limits before the Figure is drawn.
  1339. See Also
  1340. --------
  1341. matplotlib.axes.Axes.set_aspect
  1342. For a description of aspect ratio handling.
  1343. matplotlib.axes.Axes.set_adjustable
  1344. Set how the Axes adjusts to achieve the required aspect ratio.
  1345. matplotlib.axes.Axes.set_anchor
  1346. Set the position in case of extra space.
  1347. """
  1348. if position is None:
  1349. position = self.get_position(original=True)
  1350. aspect = self.get_aspect()
  1351. if aspect == 'auto' and self._box_aspect is None:
  1352. self._set_position(position, which='active')
  1353. return
  1354. fig_width, fig_height = self.get_figure().get_size_inches()
  1355. fig_aspect = fig_height / fig_width
  1356. if self._adjustable == 'box':
  1357. if self in self._twinned_axes:
  1358. raise RuntimeError("Adjustable 'box' is not allowed in a "
  1359. "twinned Axes; use 'datalim' instead")
  1360. box_aspect = aspect * self.get_data_ratio()
  1361. pb = position.frozen()
  1362. pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
  1363. self._set_position(pb1.anchored(self.get_anchor(), pb), 'active')
  1364. return
  1365. # The following is only seen if self._adjustable == 'datalim'
  1366. if self._box_aspect is not None:
  1367. pb = position.frozen()
  1368. pb1 = pb.shrunk_to_aspect(self._box_aspect, pb, fig_aspect)
  1369. self._set_position(pb1.anchored(self.get_anchor(), pb), 'active')
  1370. if aspect == "auto":
  1371. return
  1372. # reset active to original in case it had been changed by prior use
  1373. # of 'box'
  1374. if self._box_aspect is None:
  1375. self._set_position(position, which='active')
  1376. else:
  1377. position = pb1.anchored(self.get_anchor(), pb)
  1378. x_trf = self.xaxis.get_transform()
  1379. y_trf = self.yaxis.get_transform()
  1380. xmin, xmax = x_trf.transform(self.get_xbound())
  1381. ymin, ymax = y_trf.transform(self.get_ybound())
  1382. xsize = max(abs(xmax - xmin), 1e-30)
  1383. ysize = max(abs(ymax - ymin), 1e-30)
  1384. box_aspect = fig_aspect * (position.height / position.width)
  1385. data_ratio = box_aspect / aspect
  1386. y_expander = data_ratio * xsize / ysize - 1
  1387. # If y_expander > 0, the dy/dx viewLim ratio needs to increase
  1388. if abs(y_expander) < 0.005:
  1389. return
  1390. dL = self.dataLim
  1391. x0, x1 = x_trf.transform(dL.intervalx)
  1392. y0, y1 = y_trf.transform(dL.intervaly)
  1393. xr = 1.05 * (x1 - x0)
  1394. yr = 1.05 * (y1 - y0)
  1395. xmarg = xsize - xr
  1396. ymarg = ysize - yr
  1397. Ysize = data_ratio * xsize
  1398. Xsize = ysize / data_ratio
  1399. Xmarg = Xsize - xr
  1400. Ymarg = Ysize - yr
  1401. # Setting these targets to, e.g., 0.05*xr does not seem to help.
  1402. xm = 0
  1403. ym = 0
  1404. shared_x = self in self._shared_x_axes
  1405. shared_y = self in self._shared_y_axes
  1406. # Not sure whether we need this check:
  1407. if shared_x and shared_y:
  1408. raise RuntimeError("adjustable='datalim' is not allowed when both "
  1409. "axes are shared")
  1410. # If y is shared, then we are only allowed to change x, etc.
  1411. if shared_y:
  1412. adjust_y = False
  1413. else:
  1414. if xmarg > xm and ymarg > ym:
  1415. adjy = ((Ymarg > 0 and y_expander < 0) or
  1416. (Xmarg < 0 and y_expander > 0))
  1417. else:
  1418. adjy = y_expander > 0
  1419. adjust_y = shared_x or adjy # (Ymarg > xmarg)
  1420. if adjust_y:
  1421. yc = 0.5 * (ymin + ymax)
  1422. y0 = yc - Ysize / 2.0
  1423. y1 = yc + Ysize / 2.0
  1424. self.set_ybound(y_trf.inverted().transform([y0, y1]))
  1425. else:
  1426. xc = 0.5 * (xmin + xmax)
  1427. x0 = xc - Xsize / 2.0
  1428. x1 = xc + Xsize / 2.0
  1429. self.set_xbound(x_trf.inverted().transform([x0, x1]))
  1430. def axis(self, *args, emit=True, **kwargs):
  1431. """
  1432. Convenience method to get or set some axis properties.
  1433. Call signatures::
  1434. xmin, xmax, ymin, ymax = axis()
  1435. xmin, xmax, ymin, ymax = axis([xmin, xmax, ymin, ymax])
  1436. xmin, xmax, ymin, ymax = axis(option)
  1437. xmin, xmax, ymin, ymax = axis(**kwargs)
  1438. Parameters
  1439. ----------
  1440. xmin, xmax, ymin, ymax : float, optional
  1441. The axis limits to be set. This can also be achieved using ::
  1442. ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax))
  1443. option : bool or str
  1444. If a bool, turns axis lines and labels on or off. If a string,
  1445. possible values are:
  1446. ======== ==========================================================
  1447. Value Description
  1448. ======== ==========================================================
  1449. 'on' Turn on axis lines and labels. Same as ``True``.
  1450. 'off' Turn off axis lines and labels. Same as ``False``.
  1451. 'equal' Set equal scaling (i.e., make circles circular) by
  1452. changing axis limits. This is the same as
  1453. ``ax.set_aspect('equal', adjustable='datalim')``.
  1454. Explicit data limits may not be respected in this case.
  1455. 'scaled' Set equal scaling (i.e., make circles circular) by
  1456. changing dimensions of the plot box. This is the same as
  1457. ``ax.set_aspect('equal', adjustable='box', anchor='C')``.
  1458. Additionally, further autoscaling will be disabled.
  1459. 'tight' Set limits just large enough to show all data, then
  1460. disable further autoscaling.
  1461. 'auto' Automatic scaling (fill plot box with data).
  1462. 'image' 'scaled' with axis limits equal to data limits.
  1463. 'square' Square plot; similar to 'scaled', but initially forcing
  1464. ``xmax-xmin == ymax-ymin``.
  1465. ======== ==========================================================
  1466. emit : bool, default: True
  1467. Whether observers are notified of the axis limit change.
  1468. This option is passed on to `~.Axes.set_xlim` and
  1469. `~.Axes.set_ylim`.
  1470. Returns
  1471. -------
  1472. xmin, xmax, ymin, ymax : float
  1473. The axis limits.
  1474. See Also
  1475. --------
  1476. matplotlib.axes.Axes.set_xlim
  1477. matplotlib.axes.Axes.set_ylim
  1478. """
  1479. if len(args) == 1 and isinstance(args[0], (str, bool)):
  1480. s = args[0]
  1481. if s is True:
  1482. s = 'on'
  1483. if s is False:
  1484. s = 'off'
  1485. s = s.lower()
  1486. if s == 'on':
  1487. self.set_axis_on()
  1488. elif s == 'off':
  1489. self.set_axis_off()
  1490. elif s in ('equal', 'tight', 'scaled', 'auto', 'image', 'square'):
  1491. self.set_autoscale_on(True)
  1492. self.set_aspect('auto')
  1493. self.autoscale_view(tight=False)
  1494. # self.apply_aspect()
  1495. if s == 'equal':
  1496. self.set_aspect('equal', adjustable='datalim')
  1497. elif s == 'scaled':
  1498. self.set_aspect('equal', adjustable='box', anchor='C')
  1499. self.set_autoscale_on(False) # Req. by Mark Bakker
  1500. elif s == 'tight':
  1501. self.autoscale_view(tight=True)
  1502. self.set_autoscale_on(False)
  1503. elif s == 'image':
  1504. self.autoscale_view(tight=True)
  1505. self.set_autoscale_on(False)
  1506. self.set_aspect('equal', adjustable='box', anchor='C')
  1507. elif s == 'square':
  1508. self.set_aspect('equal', adjustable='box', anchor='C')
  1509. self.set_autoscale_on(False)
  1510. xlim = self.get_xlim()
  1511. ylim = self.get_ylim()
  1512. edge_size = max(np.diff(xlim), np.diff(ylim))[0]
  1513. self.set_xlim([xlim[0], xlim[0] + edge_size],
  1514. emit=emit, auto=False)
  1515. self.set_ylim([ylim[0], ylim[0] + edge_size],
  1516. emit=emit, auto=False)
  1517. else:
  1518. raise ValueError('Unrecognized string %s to axis; '
  1519. 'try on or off' % s)
  1520. else:
  1521. if len(args) >= 1:
  1522. if len(args) != 1:
  1523. cbook.warn_deprecated(
  1524. "3.2", message="Passing more than one positional "
  1525. "argument to axis() is deprecated and will raise a "
  1526. "TypeError %(removal)s.")
  1527. limits = args[0]
  1528. try:
  1529. xmin, xmax, ymin, ymax = limits
  1530. except (TypeError, ValueError) as err:
  1531. raise TypeError('the first argument to axis() must be an '
  1532. 'interable of the form '
  1533. '[xmin, xmax, ymin, ymax]') from err
  1534. else:
  1535. xmin = kwargs.pop('xmin', None)
  1536. xmax = kwargs.pop('xmax', None)
  1537. ymin = kwargs.pop('ymin', None)
  1538. ymax = kwargs.pop('ymax', None)
  1539. xauto = (None # Keep autoscale state as is.
  1540. if xmin is None and xmax is None
  1541. else False) # Turn off autoscale.
  1542. yauto = (None
  1543. if ymin is None and ymax is None
  1544. else False)
  1545. self.set_xlim(xmin, xmax, emit=emit, auto=xauto)
  1546. self.set_ylim(ymin, ymax, emit=emit, auto=yauto)
  1547. if kwargs:
  1548. raise TypeError(f"axis() got an unexpected keyword argument "
  1549. f"'{next(iter(kwargs))}'")
  1550. return (*self.get_xlim(), *self.get_ylim())
  1551. def get_legend(self):
  1552. """Return the `.Legend` instance, or None if no legend is defined."""
  1553. return self.legend_
  1554. def get_images(self):
  1555. r"""Return a list of `.AxesImage`\s contained by the Axes."""
  1556. return cbook.silent_list('AxesImage', self.images)
  1557. def get_lines(self):
  1558. """Return a list of lines contained by the Axes."""
  1559. return cbook.silent_list('Line2D', self.lines)
  1560. def get_xaxis(self):
  1561. """Return the XAxis instance."""
  1562. return self.xaxis
  1563. def get_yaxis(self):
  1564. """Return the YAxis instance."""
  1565. return self.yaxis
  1566. get_xgridlines = _axis_method_wrapper("xaxis", "get_gridlines")
  1567. get_xticklines = _axis_method_wrapper("xaxis", "get_ticklines")
  1568. get_ygridlines = _axis_method_wrapper("yaxis", "get_gridlines")
  1569. get_yticklines = _axis_method_wrapper("yaxis", "get_ticklines")
  1570. # Adding and tracking artists
  1571. def _sci(self, im):
  1572. """
  1573. Set the current image.
  1574. This image will be the target of colormap functions like
  1575. `~.pyplot.viridis`, and other functions such as `~.pyplot.clim`. The
  1576. current image is an attribute of the current axes.
  1577. """
  1578. if isinstance(im, mpl.contour.ContourSet):
  1579. if im.collections[0] not in self.collections:
  1580. raise ValueError("ContourSet must be in current Axes")
  1581. elif im not in self.images and im not in self.collections:
  1582. raise ValueError("Argument must be an image, collection, or "
  1583. "ContourSet in this Axes")
  1584. self._current_image = im
  1585. def _gci(self):
  1586. """Helper for `~matplotlib.pyplot.gci`; do not use elsewhere."""
  1587. return self._current_image
  1588. def has_data(self):
  1589. """
  1590. Return *True* if any artists have been added to axes.
  1591. This should not be used to determine whether the *dataLim*
  1592. need to be updated, and may not actually be useful for
  1593. anything.
  1594. """
  1595. return (
  1596. len(self.collections) +
  1597. len(self.images) +
  1598. len(self.lines) +
  1599. len(self.patches)) > 0
  1600. def add_artist(self, a):
  1601. """
  1602. Add an `~.Artist` to the axes, and return the artist.
  1603. Use `add_artist` only for artists for which there is no dedicated
  1604. "add" method; and if necessary, use a method such as `update_datalim`
  1605. to manually update the dataLim if the artist is to be included in
  1606. autoscaling.
  1607. If no ``transform`` has been specified when creating the artist (e.g.
  1608. ``artist.get_transform() == None``) then the transform is set to
  1609. ``ax.transData``.
  1610. """
  1611. a.axes = self
  1612. self.artists.append(a)
  1613. a._remove_method = self.artists.remove
  1614. self._set_artist_props(a)
  1615. a.set_clip_path(self.patch)
  1616. self.stale = True
  1617. return a
  1618. def add_child_axes(self, ax):
  1619. """
  1620. Add an `~.AxesBase` to the axes' children; return the child axes.
  1621. This is the lowlevel version. See `.axes.Axes.inset_axes`.
  1622. """
  1623. # normally axes have themselves as the axes, but these need to have
  1624. # their parent...
  1625. # Need to bypass the getter...
  1626. ax._axes = self
  1627. ax.stale_callback = martist._stale_axes_callback
  1628. self.child_axes.append(ax)
  1629. ax._remove_method = self.child_axes.remove
  1630. self.stale = True
  1631. return ax
  1632. def add_collection(self, collection, autolim=True):
  1633. """
  1634. Add a `~.Collection` to the axes' collections; return the collection.
  1635. """
  1636. label = collection.get_label()
  1637. if not label:
  1638. collection.set_label('_collection%d' % len(self.collections))
  1639. self.collections.append(collection)
  1640. collection._remove_method = self.collections.remove
  1641. self._set_artist_props(collection)
  1642. if collection.get_clip_path() is None:
  1643. collection.set_clip_path(self.patch)
  1644. if autolim:
  1645. # Make sure viewLim is not stale (mostly to match
  1646. # pre-lazy-autoscale behavior, which is not really better).
  1647. self._unstale_viewLim()
  1648. self.update_datalim(collection.get_datalim(self.transData))
  1649. self.stale = True
  1650. return collection
  1651. def add_image(self, image):
  1652. """
  1653. Add an `~.AxesImage` to the axes' images; return the image.
  1654. """
  1655. self._set_artist_props(image)
  1656. if not image.get_label():
  1657. image.set_label('_image%d' % len(self.images))
  1658. self.images.append(image)
  1659. image._remove_method = self.images.remove
  1660. self.stale = True
  1661. return image
  1662. def _update_image_limits(self, image):
  1663. xmin, xmax, ymin, ymax = image.get_extent()
  1664. self.axes.update_datalim(((xmin, ymin), (xmax, ymax)))
  1665. def add_line(self, line):
  1666. """
  1667. Add a `.Line2D` to the axes' lines; return the line.
  1668. """
  1669. self._set_artist_props(line)
  1670. if line.get_clip_path() is None:
  1671. line.set_clip_path(self.patch)
  1672. self._update_line_limits(line)
  1673. if not line.get_label():
  1674. line.set_label('_line%d' % len(self.lines))
  1675. self.lines.append(line)
  1676. line._remove_method = self.lines.remove
  1677. self.stale = True
  1678. return line
  1679. def _add_text(self, txt):
  1680. """
  1681. Add a `~.Text` to the axes' texts; return the text.
  1682. """
  1683. self._set_artist_props(txt)
  1684. self.texts.append(txt)
  1685. txt._remove_method = self.texts.remove
  1686. self.stale = True
  1687. return txt
  1688. def _update_line_limits(self, line):
  1689. """
  1690. Figures out the data limit of the given line, updating self.dataLim.
  1691. """
  1692. path = line.get_path()
  1693. if path.vertices.size == 0:
  1694. return
  1695. line_trans = line.get_transform()
  1696. if line_trans == self.transData:
  1697. data_path = path
  1698. elif any(line_trans.contains_branch_seperately(self.transData)):
  1699. # identify the transform to go from line's coordinates
  1700. # to data coordinates
  1701. trans_to_data = line_trans - self.transData
  1702. # if transData is affine we can use the cached non-affine component
  1703. # of line's path. (since the non-affine part of line_trans is
  1704. # entirely encapsulated in trans_to_data).
  1705. if self.transData.is_affine:
  1706. line_trans_path = line._get_transformed_path()
  1707. na_path, _ = line_trans_path.get_transformed_path_and_affine()
  1708. data_path = trans_to_data.transform_path_affine(na_path)
  1709. else:
  1710. data_path = trans_to_data.transform_path(path)
  1711. else:
  1712. # for backwards compatibility we update the dataLim with the
  1713. # coordinate range of the given path, even though the coordinate
  1714. # systems are completely different. This may occur in situations
  1715. # such as when ax.transAxes is passed through for absolute
  1716. # positioning.
  1717. data_path = path
  1718. if data_path.vertices.size > 0:
  1719. updatex, updatey = line_trans.contains_branch_seperately(
  1720. self.transData)
  1721. self.dataLim.update_from_path(data_path,
  1722. self.ignore_existing_data_limits,
  1723. updatex=updatex,
  1724. updatey=updatey)
  1725. self.ignore_existing_data_limits = False
  1726. def add_patch(self, p):
  1727. """
  1728. Add a `~.Patch` to the axes' patches; return the patch.
  1729. """
  1730. self._set_artist_props(p)
  1731. if p.get_clip_path() is None:
  1732. p.set_clip_path(self.patch)
  1733. self._update_patch_limits(p)
  1734. self.patches.append(p)
  1735. p._remove_method = self.patches.remove
  1736. return p
  1737. def _update_patch_limits(self, patch):
  1738. """Update the data limits for the given patch."""
  1739. # hist can add zero height Rectangles, which is useful to keep
  1740. # the bins, counts and patches lined up, but it throws off log
  1741. # scaling. We'll ignore rects with zero height or width in
  1742. # the auto-scaling
  1743. # cannot check for '==0' since unitized data may not compare to zero
  1744. # issue #2150 - we update the limits if patch has non zero width
  1745. # or height.
  1746. if (isinstance(patch, mpatches.Rectangle) and
  1747. ((not patch.get_width()) and (not patch.get_height()))):
  1748. return
  1749. vertices = patch.get_path().vertices
  1750. if vertices.size > 0:
  1751. xys = patch.get_patch_transform().transform(vertices)
  1752. if patch.get_data_transform() != self.transData:
  1753. patch_to_data = (patch.get_data_transform() -
  1754. self.transData)
  1755. xys = patch_to_data.transform(xys)
  1756. updatex, updatey = patch.get_transform().\
  1757. contains_branch_seperately(self.transData)
  1758. self.update_datalim(xys, updatex=updatex,
  1759. updatey=updatey)
  1760. def add_table(self, tab):
  1761. """
  1762. Add a `~.Table` to the axes' tables; return the table.
  1763. """
  1764. self._set_artist_props(tab)
  1765. self.tables.append(tab)
  1766. tab.set_clip_path(self.patch)
  1767. tab._remove_method = self.tables.remove
  1768. return tab
  1769. def add_container(self, container):
  1770. """
  1771. Add a `~.Container` to the axes' containers; return the container.
  1772. """
  1773. label = container.get_label()
  1774. if not label:
  1775. container.set_label('_container%d' % len(self.containers))
  1776. self.containers.append(container)
  1777. container._remove_method = self.containers.remove
  1778. return container
  1779. def _on_units_changed(self, scalex=False, scaley=False):
  1780. """
  1781. Callback for processing changes to axis units.
  1782. Currently requests updates of data limits and view limits.
  1783. """
  1784. self.relim()
  1785. self._request_autoscale_view(scalex=scalex, scaley=scaley)
  1786. def relim(self, visible_only=False):
  1787. """
  1788. Recompute the data limits based on current artists.
  1789. At present, `~.Collection` instances are not supported.
  1790. Parameters
  1791. ----------
  1792. visible_only : bool, default: False
  1793. Whether to exclude invisible artists.
  1794. """
  1795. # Collections are deliberately not supported (yet); see
  1796. # the TODO note in artists.py.
  1797. self.dataLim.ignore(True)
  1798. self.dataLim.set_points(mtransforms.Bbox.null().get_points())
  1799. self.ignore_existing_data_limits = True
  1800. for line in self.lines:
  1801. if not visible_only or line.get_visible():
  1802. self._update_line_limits(line)
  1803. for p in self.patches:
  1804. if not visible_only or p.get_visible():
  1805. self._update_patch_limits(p)
  1806. for image in self.images:
  1807. if not visible_only or image.get_visible():
  1808. self._update_image_limits(image)
  1809. def update_datalim(self, xys, updatex=True, updatey=True):
  1810. """
  1811. Extend the `~.Axes.dataLim` Bbox to include the given points.
  1812. If no data is set currently, the Bbox will ignore its limits and set
  1813. the bound to be the bounds of the xydata (*xys*). Otherwise, it will
  1814. compute the bounds of the union of its current data and the data in
  1815. *xys*.
  1816. Parameters
  1817. ----------
  1818. xys : 2D array-like
  1819. The points to include in the data limits Bbox. This can be either
  1820. a list of (x, y) tuples or a Nx2 array.
  1821. updatex, updatey : bool, default: True
  1822. Whether to update the x/y limits.
  1823. """
  1824. xys = np.asarray(xys)
  1825. if not np.any(np.isfinite(xys)):
  1826. return
  1827. self.dataLim.update_from_data_xy(xys, self.ignore_existing_data_limits,
  1828. updatex=updatex, updatey=updatey)
  1829. self.ignore_existing_data_limits = False
  1830. @cbook.deprecated(
  1831. "3.3", alternative="ax.dataLim.set(Bbox.union([ax.dataLim, bounds]))")
  1832. def update_datalim_bounds(self, bounds):
  1833. """
  1834. Extend the `~.Axes.datalim` Bbox to include the given
  1835. `~matplotlib.transforms.Bbox`.
  1836. Parameters
  1837. ----------
  1838. bounds : `~matplotlib.transforms.Bbox`
  1839. """
  1840. self.dataLim.set(mtransforms.Bbox.union([self.dataLim, bounds]))
  1841. def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
  1842. """Look for unit *kwargs* and update the axis instances as necessary"""
  1843. def _process_single_axis(data, axis, unit_name, kwargs):
  1844. # Return if there's no axis set
  1845. if axis is None:
  1846. return kwargs
  1847. if data is not None:
  1848. # We only need to update if there is nothing set yet.
  1849. if not axis.have_units():
  1850. axis.update_units(data)
  1851. # Check for units in the kwargs, and if present update axis
  1852. if kwargs is not None:
  1853. units = kwargs.pop(unit_name, axis.units)
  1854. if self.name == 'polar':
  1855. polar_units = {'xunits': 'thetaunits', 'yunits': 'runits'}
  1856. units = kwargs.pop(polar_units[unit_name], units)
  1857. if units != axis.units:
  1858. axis.set_units(units)
  1859. # If the units being set imply a different converter,
  1860. # we need to update.
  1861. if data is not None:
  1862. axis.update_units(data)
  1863. return kwargs
  1864. kwargs = _process_single_axis(xdata, self.xaxis, 'xunits', kwargs)
  1865. kwargs = _process_single_axis(ydata, self.yaxis, 'yunits', kwargs)
  1866. return kwargs
  1867. def in_axes(self, mouseevent):
  1868. """
  1869. Return *True* if the given *mouseevent* (in display coords)
  1870. is in the Axes
  1871. """
  1872. return self.patch.contains(mouseevent)[0]
  1873. def get_autoscale_on(self):
  1874. """
  1875. Get whether autoscaling is applied for both axes on plot commands
  1876. """
  1877. return self._autoscaleXon and self._autoscaleYon
  1878. def get_autoscalex_on(self):
  1879. """
  1880. Get whether autoscaling for the x-axis is applied on plot commands
  1881. """
  1882. return self._autoscaleXon
  1883. def get_autoscaley_on(self):
  1884. """
  1885. Get whether autoscaling for the y-axis is applied on plot commands
  1886. """
  1887. return self._autoscaleYon
  1888. def set_autoscale_on(self, b):
  1889. """
  1890. Set whether autoscaling is applied on plot commands
  1891. Parameters
  1892. ----------
  1893. b : bool
  1894. """
  1895. self._autoscaleXon = b
  1896. self._autoscaleYon = b
  1897. def set_autoscalex_on(self, b):
  1898. """
  1899. Set whether autoscaling for the x-axis is applied on plot commands
  1900. Parameters
  1901. ----------
  1902. b : bool
  1903. """
  1904. self._autoscaleXon = b
  1905. def set_autoscaley_on(self, b):
  1906. """
  1907. Set whether autoscaling for the y-axis is applied on plot commands
  1908. Parameters
  1909. ----------
  1910. b : bool
  1911. """
  1912. self._autoscaleYon = b
  1913. @property
  1914. def use_sticky_edges(self):
  1915. """
  1916. When autoscaling, whether to obey all `Artist.sticky_edges`.
  1917. Default is ``True``.
  1918. Setting this to ``False`` ensures that the specified margins
  1919. will be applied, even if the plot includes an image, for
  1920. example, which would otherwise force a view limit to coincide
  1921. with its data limit.
  1922. The changing this property does not change the plot until
  1923. `autoscale` or `autoscale_view` is called.
  1924. """
  1925. return self._use_sticky_edges
  1926. @use_sticky_edges.setter
  1927. def use_sticky_edges(self, b):
  1928. self._use_sticky_edges = bool(b)
  1929. # No effect until next autoscaling, which will mark the axes as stale.
  1930. def set_xmargin(self, m):
  1931. """
  1932. Set padding of X data limits prior to autoscaling.
  1933. *m* times the data interval will be added to each
  1934. end of that interval before it is used in autoscaling.
  1935. For example, if your data is in the range [0, 2], a factor of
  1936. ``m = 0.1`` will result in a range [-0.2, 2.2].
  1937. Negative values -0.5 < m < 0 will result in clipping of the data range.
  1938. I.e. for a data range [0, 2], a factor of ``m = -0.1`` will result in
  1939. a range [0.2, 1.8].
  1940. Parameters
  1941. ----------
  1942. m : float greater than -0.5
  1943. """
  1944. if m <= -0.5:
  1945. raise ValueError("margin must be greater than -0.5")
  1946. self._xmargin = m
  1947. self._request_autoscale_view(scalex=True, scaley=False)
  1948. self.stale = True
  1949. def set_ymargin(self, m):
  1950. """
  1951. Set padding of Y data limits prior to autoscaling.
  1952. *m* times the data interval will be added to each
  1953. end of that interval before it is used in autoscaling.
  1954. For example, if your data is in the range [0, 2], a factor of
  1955. ``m = 0.1`` will result in a range [-0.2, 2.2].
  1956. Negative values -0.5 < m < 0 will result in clipping of the data range.
  1957. I.e. for a data range [0, 2], a factor of ``m = -0.1`` will result in
  1958. a range [0.2, 1.8].
  1959. Parameters
  1960. ----------
  1961. m : float greater than -0.5
  1962. """
  1963. if m <= -0.5:
  1964. raise ValueError("margin must be greater than -0.5")
  1965. self._ymargin = m
  1966. self._request_autoscale_view(scalex=False, scaley=True)
  1967. self.stale = True
  1968. def margins(self, *margins, x=None, y=None, tight=True):
  1969. """
  1970. Set or retrieve autoscaling margins.
  1971. The padding added to each limit of the axes is the *margin*
  1972. times the data interval. All input parameters must be floats
  1973. within the range [0, 1]. Passing both positional and keyword
  1974. arguments is invalid and will raise a TypeError. If no
  1975. arguments (positional or otherwise) are provided, the current
  1976. margins will remain in place and simply be returned.
  1977. Specifying any margin changes only the autoscaling; for example,
  1978. if *xmargin* is not None, then *xmargin* times the X data
  1979. interval will be added to each end of that interval before
  1980. it is used in autoscaling.
  1981. Parameters
  1982. ----------
  1983. *margins : float, optional
  1984. If a single positional argument is provided, it specifies
  1985. both margins of the x-axis and y-axis limits. If two
  1986. positional arguments are provided, they will be interpreted
  1987. as *xmargin*, *ymargin*. If setting the margin on a single
  1988. axis is desired, use the keyword arguments described below.
  1989. x, y : float, optional
  1990. Specific margin values for the x-axis and y-axis,
  1991. respectively. These cannot be used with positional
  1992. arguments, but can be used individually to alter on e.g.,
  1993. only the y-axis.
  1994. tight : bool or None, default: True
  1995. The *tight* parameter is passed to :meth:`autoscale_view`,
  1996. which is executed after a margin is changed; the default
  1997. here is *True*, on the assumption that when margins are
  1998. specified, no additional padding to match tick marks is
  1999. usually desired. Set *tight* to *None* will preserve
  2000. the previous setting.
  2001. Returns
  2002. -------
  2003. xmargin, ymargin : float
  2004. Notes
  2005. -----
  2006. If a previously used Axes method such as :meth:`pcolor` has set
  2007. :attr:`use_sticky_edges` to `True`, only the limits not set by
  2008. the "sticky artists" will be modified. To force all of the
  2009. margins to be set, set :attr:`use_sticky_edges` to `False`
  2010. before calling :meth:`margins`.
  2011. """
  2012. if margins and x is not None and y is not None:
  2013. raise TypeError('Cannot pass both positional and keyword '
  2014. 'arguments for x and/or y.')
  2015. elif len(margins) == 1:
  2016. x = y = margins[0]
  2017. elif len(margins) == 2:
  2018. x, y = margins
  2019. elif margins:
  2020. raise TypeError('Must pass a single positional argument for all '
  2021. 'margins, or one for each margin (x, y).')
  2022. if x is None and y is None:
  2023. if tight is not True:
  2024. cbook._warn_external(f'ignoring tight={tight!r} in get mode')
  2025. return self._xmargin, self._ymargin
  2026. if tight is not None:
  2027. self._tight = tight
  2028. if x is not None:
  2029. self.set_xmargin(x)
  2030. if y is not None:
  2031. self.set_ymargin(y)
  2032. def set_rasterization_zorder(self, z):
  2033. """
  2034. Parameters
  2035. ----------
  2036. z : float or None
  2037. zorder below which artists are rasterized. ``None`` means that
  2038. artists do not get rasterized based on zorder.
  2039. """
  2040. self._rasterization_zorder = z
  2041. self.stale = True
  2042. def get_rasterization_zorder(self):
  2043. """Return the zorder value below which artists will be rasterized."""
  2044. return self._rasterization_zorder
  2045. def autoscale(self, enable=True, axis='both', tight=None):
  2046. """
  2047. Autoscale the axis view to the data (toggle).
  2048. Convenience method for simple axis view autoscaling.
  2049. It turns autoscaling on or off, and then,
  2050. if autoscaling for either axis is on, it performs
  2051. the autoscaling on the specified axis or axes.
  2052. Parameters
  2053. ----------
  2054. enable : bool or None, default: True
  2055. True turns autoscaling on, False turns it off.
  2056. None leaves the autoscaling state unchanged.
  2057. axis : {'both', 'x', 'y'}, default: 'both'
  2058. Which axis to operate on.
  2059. tight : bool or None, default: None
  2060. If True, first set the margins to zero. Then, this argument is
  2061. forwarded to `autoscale_view` (regardless of its value); see the
  2062. description of its behavior there.
  2063. """
  2064. if enable is None:
  2065. scalex = True
  2066. scaley = True
  2067. else:
  2068. scalex = False
  2069. scaley = False
  2070. if axis in ['x', 'both']:
  2071. self._autoscaleXon = bool(enable)
  2072. scalex = self._autoscaleXon
  2073. if axis in ['y', 'both']:
  2074. self._autoscaleYon = bool(enable)
  2075. scaley = self._autoscaleYon
  2076. if tight and scalex:
  2077. self._xmargin = 0
  2078. if tight and scaley:
  2079. self._ymargin = 0
  2080. self._request_autoscale_view(tight=tight, scalex=scalex, scaley=scaley)
  2081. def autoscale_view(self, tight=None, scalex=True, scaley=True):
  2082. """
  2083. Autoscale the view limits using the data limits.
  2084. Parameters
  2085. ----------
  2086. tight : bool or None
  2087. If *True*, only expand the axis limits using the margins. Note
  2088. that unlike for `autoscale`, ``tight=True`` does *not* set the
  2089. margins to zero.
  2090. If *False* and :rc:`axes.autolimit_mode` is 'round_numbers', then
  2091. after expansion by the margins, further expand the axis limits
  2092. using the axis major locator.
  2093. If None (the default), reuse the value set in the previous call to
  2094. `autoscale_view` (the initial value is False, but the default style
  2095. sets :rc:`axes.autolimit_mode` to 'data', in which case this
  2096. behaves like True).
  2097. scalex : bool, default: True
  2098. Whether to autoscale the x axis.
  2099. scaley : bool, default: True
  2100. Whether to autoscale the y axis.
  2101. Notes
  2102. -----
  2103. The autoscaling preserves any preexisting axis direction reversal.
  2104. The data limits are not updated automatically when artist data are
  2105. changed after the artist has been added to an Axes instance. In that
  2106. case, use :meth:`matplotlib.axes.Axes.relim` prior to calling
  2107. autoscale_view.
  2108. If the views of the axes are fixed, e.g. via `set_xlim`, they will
  2109. not be changed by autoscale_view().
  2110. See :meth:`matplotlib.axes.Axes.autoscale` for an alternative.
  2111. """
  2112. if tight is not None:
  2113. self._tight = bool(tight)
  2114. x_stickies = y_stickies = np.array([])
  2115. if self.use_sticky_edges:
  2116. # Only iterate over axes and artists if needed. The check for
  2117. # ``hasattr(ax, "lines")`` is necessary because this can be called
  2118. # very early in the axes init process (e.g., for twin axes) when
  2119. # these attributes don't even exist yet, in which case
  2120. # `get_children` would raise an AttributeError.
  2121. if self._xmargin and scalex and self._autoscaleXon:
  2122. x_stickies = np.sort(np.concatenate([
  2123. artist.sticky_edges.x
  2124. for ax in self._shared_x_axes.get_siblings(self)
  2125. if hasattr(ax, "lines")
  2126. for artist in ax.get_children()]))
  2127. if self._ymargin and scaley and self._autoscaleYon:
  2128. y_stickies = np.sort(np.concatenate([
  2129. artist.sticky_edges.y
  2130. for ax in self._shared_y_axes.get_siblings(self)
  2131. if hasattr(ax, "lines")
  2132. for artist in ax.get_children()]))
  2133. if self.get_xscale().lower() == 'log':
  2134. x_stickies = x_stickies[x_stickies > 0]
  2135. if self.get_yscale().lower() == 'log':
  2136. y_stickies = y_stickies[y_stickies > 0]
  2137. def handle_single_axis(scale, autoscaleon, shared_axes, interval,
  2138. minpos, axis, margin, stickies, set_bound):
  2139. if not (scale and autoscaleon):
  2140. return # nothing to do...
  2141. shared = shared_axes.get_siblings(self)
  2142. dl = [ax.dataLim for ax in shared]
  2143. # ignore non-finite data limits if good limits exist
  2144. finite_dl = [d for d in dl if np.isfinite(d).all()]
  2145. if len(finite_dl):
  2146. # if finite limits exist for at least one axis (and the
  2147. # other is infinite), restore the finite limits
  2148. x_finite = [d for d in dl
  2149. if (np.isfinite(d.intervalx).all() and
  2150. (d not in finite_dl))]
  2151. y_finite = [d for d in dl
  2152. if (np.isfinite(d.intervaly).all() and
  2153. (d not in finite_dl))]
  2154. dl = finite_dl
  2155. dl.extend(x_finite)
  2156. dl.extend(y_finite)
  2157. bb = mtransforms.BboxBase.union(dl)
  2158. x0, x1 = getattr(bb, interval)
  2159. # If x0 and x1 are non finite, use the locator to figure out
  2160. # default limits.
  2161. locator = axis.get_major_locator()
  2162. x0, x1 = locator.nonsingular(x0, x1)
  2163. # Prevent margin addition from crossing a sticky value. A small
  2164. # tolerance must be added due to floating point issues with
  2165. # streamplot; it is defined relative to x0, x1, x1-x0 but has
  2166. # no absolute term (e.g. "+1e-8") to avoid issues when working with
  2167. # datasets where all values are tiny (less than 1e-8).
  2168. tol = 1e-5 * max(abs(x0), abs(x1), abs(x1 - x0))
  2169. # Index of largest element < x0 + tol, if any.
  2170. i0 = stickies.searchsorted(x0 + tol) - 1
  2171. x0bound = stickies[i0] if i0 != -1 else None
  2172. # Index of smallest element > x1 - tol, if any.
  2173. i1 = stickies.searchsorted(x1 - tol)
  2174. x1bound = stickies[i1] if i1 != len(stickies) else None
  2175. # Add the margin in figure space and then transform back, to handle
  2176. # non-linear scales.
  2177. minpos = getattr(bb, minpos)
  2178. transform = axis.get_transform()
  2179. inverse_trans = transform.inverted()
  2180. x0, x1 = axis._scale.limit_range_for_scale(x0, x1, minpos)
  2181. x0t, x1t = transform.transform([x0, x1])
  2182. delta = (x1t - x0t) * margin
  2183. if not np.isfinite(delta):
  2184. delta = 0 # If a bound isn't finite, set margin to zero.
  2185. x0, x1 = inverse_trans.transform([x0t - delta, x1t + delta])
  2186. # Apply sticky bounds.
  2187. if x0bound is not None:
  2188. x0 = max(x0, x0bound)
  2189. if x1bound is not None:
  2190. x1 = min(x1, x1bound)
  2191. if not self._tight:
  2192. x0, x1 = locator.view_limits(x0, x1)
  2193. set_bound(x0, x1)
  2194. # End of definition of internal function 'handle_single_axis'.
  2195. handle_single_axis(
  2196. scalex, self._autoscaleXon, self._shared_x_axes, 'intervalx',
  2197. 'minposx', self.xaxis, self._xmargin, x_stickies, self.set_xbound)
  2198. handle_single_axis(
  2199. scaley, self._autoscaleYon, self._shared_y_axes, 'intervaly',
  2200. 'minposy', self.yaxis, self._ymargin, y_stickies, self.set_ybound)
  2201. def _get_axis_list(self):
  2202. return self.xaxis, self.yaxis
  2203. def _get_axis_map(self):
  2204. """
  2205. Return a mapping of `Axis` "names" to `Axis` instances.
  2206. The `Axis` name is derived from the attribute under which the instance
  2207. is stored, so e.g. for polar axes, the theta-axis is still named "x"
  2208. and the r-axis is still named "y" (for back-compatibility).
  2209. In practice, this means that the entries are typically "x" and "y", and
  2210. additionally "z" for 3D axes.
  2211. """
  2212. d = {}
  2213. axis_list = self._get_axis_list()
  2214. for k, v in vars(self).items():
  2215. if k.endswith("axis") and v in axis_list:
  2216. d[k[:-len("axis")]] = v
  2217. return d
  2218. def _update_title_position(self, renderer):
  2219. """
  2220. Update the title position based on the bounding box enclosing
  2221. all the ticklabels and x-axis spine and xlabel...
  2222. """
  2223. if self._autotitlepos is not None and not self._autotitlepos:
  2224. _log.debug('title position was updated manually, not adjusting')
  2225. return
  2226. titles = (self.title, self._left_title, self._right_title)
  2227. for title in titles:
  2228. x, _ = title.get_position()
  2229. # need to start again in case of window resizing
  2230. title.set_position((x, 1.0))
  2231. # need to check all our twins too...
  2232. axs = self._twinned_axes.get_siblings(self)
  2233. # and all the children
  2234. for ax in self.child_axes:
  2235. if ax is not None:
  2236. locator = ax.get_axes_locator()
  2237. if locator:
  2238. pos = locator(self, renderer)
  2239. ax.apply_aspect(pos)
  2240. else:
  2241. ax.apply_aspect()
  2242. axs = axs + [ax]
  2243. top = -np.Inf
  2244. for ax in axs:
  2245. if (ax.xaxis.get_ticks_position() in ['top', 'unknown']
  2246. or ax.xaxis.get_label_position() == 'top'):
  2247. bb = ax.xaxis.get_tightbbox(renderer)
  2248. else:
  2249. bb = ax.get_window_extent(renderer)
  2250. if bb is not None:
  2251. top = max(top, bb.ymax)
  2252. if top < 0:
  2253. # the top of axes is not even on the figure, so don't try and
  2254. # automatically place it.
  2255. _log.debug('top of axes not in the figure, so title not moved')
  2256. return
  2257. if title.get_window_extent(renderer).ymin < top:
  2258. _, y = self.transAxes.inverted().transform((0, top))
  2259. title.set_position((x, y))
  2260. # empirically, this doesn't always get the min to top,
  2261. # so we need to adjust again.
  2262. if title.get_window_extent(renderer).ymin < top:
  2263. _, y = self.transAxes.inverted().transform(
  2264. (0., 2 * top - title.get_window_extent(renderer).ymin))
  2265. title.set_position((x, y))
  2266. ymax = max(title.get_position()[1] for title in titles)
  2267. for title in titles:
  2268. # now line up all the titles at the highest baseline.
  2269. x, _ = title.get_position()
  2270. title.set_position((x, ymax))
  2271. # Drawing
  2272. @martist.allow_rasterization
  2273. @cbook._delete_parameter(
  2274. "3.3", "inframe", alternative="Axes.redraw_in_frame()")
  2275. def draw(self, renderer=None, inframe=False):
  2276. # docstring inherited
  2277. if renderer is None:
  2278. cbook.warn_deprecated(
  2279. "3.3", message="Support for not passing the 'renderer' "
  2280. "parameter to Axes.draw() is deprecated since %(since)s and "
  2281. "will be removed %(removal)s. Use axes.draw_artist(axes) "
  2282. "instead.")
  2283. renderer = self.figure._cachedRenderer
  2284. if renderer is None:
  2285. raise RuntimeError('No renderer defined')
  2286. if not self.get_visible():
  2287. return
  2288. self._unstale_viewLim()
  2289. renderer.open_group('axes', gid=self.get_gid())
  2290. # prevent triggering call backs during the draw process
  2291. self._stale = True
  2292. # loop over self and child axes...
  2293. locator = self.get_axes_locator()
  2294. if locator:
  2295. pos = locator(self, renderer)
  2296. self.apply_aspect(pos)
  2297. else:
  2298. self.apply_aspect()
  2299. artists = self.get_children()
  2300. artists.remove(self.patch)
  2301. # the frame draws the edges around the axes patch -- we
  2302. # decouple these so the patch can be in the background and the
  2303. # frame in the foreground. Do this before drawing the axis
  2304. # objects so that the spine has the opportunity to update them.
  2305. if not (self.axison and self._frameon):
  2306. for spine in self.spines.values():
  2307. artists.remove(spine)
  2308. self._update_title_position(renderer)
  2309. if not self.axison or inframe:
  2310. for _axis in self._get_axis_list():
  2311. artists.remove(_axis)
  2312. if inframe:
  2313. artists.remove(self.title)
  2314. artists.remove(self._left_title)
  2315. artists.remove(self._right_title)
  2316. if not self.figure.canvas.is_saving():
  2317. artists = [a for a in artists
  2318. if not a.get_animated() or a in self.images]
  2319. artists = sorted(artists, key=attrgetter('zorder'))
  2320. # rasterize artists with negative zorder
  2321. # if the minimum zorder is negative, start rasterization
  2322. rasterization_zorder = self._rasterization_zorder
  2323. if (rasterization_zorder is not None and
  2324. artists and artists[0].zorder < rasterization_zorder):
  2325. renderer.start_rasterizing()
  2326. artists_rasterized = [a for a in artists
  2327. if a.zorder < rasterization_zorder]
  2328. artists = [a for a in artists
  2329. if a.zorder >= rasterization_zorder]
  2330. else:
  2331. artists_rasterized = []
  2332. # the patch draws the background rectangle -- the frame below
  2333. # will draw the edges
  2334. if self.axison and self._frameon:
  2335. self.patch.draw(renderer)
  2336. if artists_rasterized:
  2337. for a in artists_rasterized:
  2338. a.draw(renderer)
  2339. renderer.stop_rasterizing()
  2340. mimage._draw_list_compositing_images(renderer, self, artists)
  2341. renderer.close_group('axes')
  2342. self.stale = False
  2343. def draw_artist(self, a):
  2344. """
  2345. Efficiently redraw a single artist.
  2346. This method can only be used after an initial draw which caches the
  2347. renderer.
  2348. """
  2349. if self.figure._cachedRenderer is None:
  2350. raise AttributeError("draw_artist can only be used after an "
  2351. "initial draw which caches the renderer")
  2352. a.draw(self.figure._cachedRenderer)
  2353. def redraw_in_frame(self):
  2354. """
  2355. Efficiently redraw Axes data, but not axis ticks, labels, etc.
  2356. This method can only be used after an initial draw which caches the
  2357. renderer.
  2358. """
  2359. if self.figure._cachedRenderer is None:
  2360. raise AttributeError("redraw_in_frame can only be used after an "
  2361. "initial draw which caches the renderer")
  2362. with ExitStack() as stack:
  2363. for artist in [*self._get_axis_list(),
  2364. self.title, self._left_title, self._right_title]:
  2365. stack.callback(artist.set_visible, artist.get_visible())
  2366. artist.set_visible(False)
  2367. self.draw(self.figure._cachedRenderer)
  2368. def get_renderer_cache(self):
  2369. return self.figure._cachedRenderer
  2370. # Axes rectangle characteristics
  2371. def get_frame_on(self):
  2372. """Get whether the axes rectangle patch is drawn."""
  2373. return self._frameon
  2374. def set_frame_on(self, b):
  2375. """
  2376. Set whether the axes rectangle patch is drawn.
  2377. Parameters
  2378. ----------
  2379. b : bool
  2380. """
  2381. self._frameon = b
  2382. self.stale = True
  2383. def get_axisbelow(self):
  2384. """
  2385. Get whether axis ticks and gridlines are above or below most artists.
  2386. Returns
  2387. -------
  2388. bool or 'line'
  2389. See Also
  2390. --------
  2391. set_axisbelow
  2392. """
  2393. return self._axisbelow
  2394. def set_axisbelow(self, b):
  2395. """
  2396. Set whether axis ticks and gridlines are above or below most artists.
  2397. This controls the zorder of the ticks and gridlines. For more
  2398. information on the zorder see :doc:`/gallery/misc/zorder_demo`.
  2399. Parameters
  2400. ----------
  2401. b : bool or 'line'
  2402. Possible values:
  2403. - *True* (zorder = 0.5): Ticks and gridlines are below all Artists.
  2404. - 'line' (zorder = 1.5): Ticks and gridlines are above patches
  2405. (e.g. rectangles, with default zorder = 1) but still below lines
  2406. and markers (with their default zorder = 2).
  2407. - *False* (zorder = 2.5): Ticks and gridlines are above patches
  2408. and lines / markers.
  2409. See Also
  2410. --------
  2411. get_axisbelow
  2412. """
  2413. self._axisbelow = axisbelow = validate_axisbelow(b)
  2414. if axisbelow is True:
  2415. zorder = 0.5
  2416. elif axisbelow is False:
  2417. zorder = 2.5
  2418. elif axisbelow == "line":
  2419. zorder = 1.5
  2420. else:
  2421. raise ValueError("Unexpected axisbelow value")
  2422. for axis in self._get_axis_list():
  2423. axis.set_zorder(zorder)
  2424. self.stale = True
  2425. @docstring.dedent_interpd
  2426. def grid(self, b=None, which='major', axis='both', **kwargs):
  2427. """
  2428. Configure the grid lines.
  2429. Parameters
  2430. ----------
  2431. b : bool or None, optional
  2432. Whether to show the grid lines. If any *kwargs* are supplied,
  2433. it is assumed you want the grid on and *b* will be set to True.
  2434. If *b* is *None* and there are no *kwargs*, this toggles the
  2435. visibility of the lines.
  2436. which : {'major', 'minor', 'both'}, optional
  2437. The grid lines to apply the changes on.
  2438. axis : {'both', 'x', 'y'}, optional
  2439. The axis to apply the changes on.
  2440. **kwargs : `.Line2D` properties
  2441. Define the line properties of the grid, e.g.::
  2442. grid(color='r', linestyle='-', linewidth=2)
  2443. Valid keyword arguments are:
  2444. %(_Line2D_docstr)s
  2445. Notes
  2446. -----
  2447. The axis is drawn as a unit, so the effective zorder for drawing the
  2448. grid is determined by the zorder of each axis, not by the zorder of the
  2449. `.Line2D` objects comprising the grid. Therefore, to set grid zorder,
  2450. use `.set_axisbelow` or, for more control, call the
  2451. `~.Artist.set_zorder` method of each axis.
  2452. """
  2453. cbook._check_in_list(['x', 'y', 'both'], axis=axis)
  2454. if axis in ['x', 'both']:
  2455. self.xaxis.grid(b, which=which, **kwargs)
  2456. if axis in ['y', 'both']:
  2457. self.yaxis.grid(b, which=which, **kwargs)
  2458. def ticklabel_format(self, *, axis='both', style='', scilimits=None,
  2459. useOffset=None, useLocale=None, useMathText=None):
  2460. r"""
  2461. Configure the `.ScalarFormatter` used by default for linear axes.
  2462. If a parameter is not set, the corresponding property of the formatter
  2463. is left unchanged.
  2464. Parameters
  2465. ----------
  2466. axis : {'x', 'y', 'both'}, default: 'both'
  2467. The axes to configure. Only major ticks are affected.
  2468. style : {'sci', 'scientific', 'plain'}
  2469. Whether to use scientific notation.
  2470. The formatter default is to use scientific notation.
  2471. scilimits : pair of ints (m, n)
  2472. Scientific notation is used only for numbers outside the range
  2473. 10\ :sup:`m` to 10\ :sup:`n` (and only if the formatter is
  2474. configured to use scientific notation at all). Use (0, 0) to
  2475. include all numbers. Use (m, m) where m != 0 to fix the order of
  2476. magnitude to 10\ :sup:`m`.
  2477. The formatter default is :rc:`axes.formatter.limits`.
  2478. useOffset : bool or float
  2479. If True, the offset is calculated as needed.
  2480. If False, no offset is used.
  2481. If a numeric value, it sets the offset.
  2482. The formatter default is :rc:`axes.formatter.useoffset`.
  2483. useLocale : bool
  2484. Whether to format the number using the current locale or using the
  2485. C (English) locale. This affects e.g. the decimal separator. The
  2486. formatter default is :rc:`axes.formatter.use_locale`.
  2487. useMathText : bool
  2488. Render the offset and scientific notation in mathtext.
  2489. The formatter default is :rc:`axes.formatter.use_mathtext`.
  2490. Raises
  2491. ------
  2492. AttributeError
  2493. If the current formatter is not a `.ScalarFormatter`.
  2494. """
  2495. style = style.lower()
  2496. axis = axis.lower()
  2497. if scilimits is not None:
  2498. try:
  2499. m, n = scilimits
  2500. m + n + 1 # check that both are numbers
  2501. except (ValueError, TypeError) as err:
  2502. raise ValueError("scilimits must be a sequence of 2 integers"
  2503. ) from err
  2504. STYLES = {'sci': True, 'scientific': True, 'plain': False, '': None}
  2505. is_sci_style = cbook._check_getitem(STYLES, style=style)
  2506. axis_map = {**{k: [v] for k, v in self._get_axis_map().items()},
  2507. 'both': self._get_axis_list()}
  2508. axises = cbook._check_getitem(axis_map, axis=axis)
  2509. try:
  2510. for axis in axises:
  2511. if is_sci_style is not None:
  2512. axis.major.formatter.set_scientific(is_sci_style)
  2513. if scilimits is not None:
  2514. axis.major.formatter.set_powerlimits(scilimits)
  2515. if useOffset is not None:
  2516. axis.major.formatter.set_useOffset(useOffset)
  2517. if useLocale is not None:
  2518. axis.major.formatter.set_useLocale(useLocale)
  2519. if useMathText is not None:
  2520. axis.major.formatter.set_useMathText(useMathText)
  2521. except AttributeError as err:
  2522. raise AttributeError(
  2523. "This method only works with the ScalarFormatter") from err
  2524. def locator_params(self, axis='both', tight=None, **kwargs):
  2525. """
  2526. Control behavior of major tick locators.
  2527. Because the locator is involved in autoscaling, `~.Axes.autoscale_view`
  2528. is called automatically after the parameters are changed.
  2529. Parameters
  2530. ----------
  2531. axis : {'both', 'x', 'y'}, default: 'both'
  2532. The axis on which to operate.
  2533. tight : bool or None, optional
  2534. Parameter passed to `~.Axes.autoscale_view`.
  2535. Default is None, for no change.
  2536. Other Parameters
  2537. ----------------
  2538. **kwargs
  2539. Remaining keyword arguments are passed to directly to the
  2540. ``set_params()`` method of the locator. Supported keywords depend
  2541. on the type of the locator. See for example
  2542. `~.ticker.MaxNLocator.set_params` for the `.ticker.MaxNLocator`
  2543. used by default for linear axes.
  2544. Examples
  2545. --------
  2546. When plotting small subplots, one might want to reduce the maximum
  2547. number of ticks and use tight bounds, for example::
  2548. ax.locator_params(tight=True, nbins=4)
  2549. """
  2550. cbook._check_in_list(['x', 'y', 'both'], axis=axis)
  2551. update_x = axis in ['x', 'both']
  2552. update_y = axis in ['y', 'both']
  2553. if update_x:
  2554. self.xaxis.get_major_locator().set_params(**kwargs)
  2555. if update_y:
  2556. self.yaxis.get_major_locator().set_params(**kwargs)
  2557. self._request_autoscale_view(tight=tight,
  2558. scalex=update_x, scaley=update_y)
  2559. self.stale = True
  2560. def tick_params(self, axis='both', **kwargs):
  2561. """
  2562. Change the appearance of ticks, tick labels, and gridlines.
  2563. Tick properties that are not explicitly set using the keyword
  2564. arguments remain unchanged unless *reset* is True.
  2565. Parameters
  2566. ----------
  2567. axis : {'x', 'y', 'both'}, default: 'both'
  2568. The axis to which the parameters are applied.
  2569. which : {'major', 'minor', 'both'}, default: 'major'
  2570. The group of ticks to which the parameters are applied.
  2571. reset : bool, default: False
  2572. Whether to reset the ticks to defaults before updating them.
  2573. Other Parameters
  2574. ----------------
  2575. direction : {'in', 'out', 'inout'}
  2576. Puts ticks inside the axes, outside the axes, or both.
  2577. length : float
  2578. Tick length in points.
  2579. width : float
  2580. Tick width in points.
  2581. color : color
  2582. Tick color.
  2583. pad : float
  2584. Distance in points between tick and label.
  2585. labelsize : float or str
  2586. Tick label font size in points or as a string (e.g., 'large').
  2587. labelcolor : color
  2588. Tick label color.
  2589. colors : color
  2590. Tick color and label color.
  2591. zorder : float
  2592. Tick and label zorder.
  2593. bottom, top, left, right : bool
  2594. Whether to draw the respective ticks.
  2595. labelbottom, labeltop, labelleft, labelright : bool
  2596. Whether to draw the respective tick labels.
  2597. labelrotation : float
  2598. Tick label rotation
  2599. grid_color : color
  2600. Gridline color.
  2601. grid_alpha : float
  2602. Transparency of gridlines: 0 (transparent) to 1 (opaque).
  2603. grid_linewidth : float
  2604. Width of gridlines in points.
  2605. grid_linestyle : str
  2606. Any valid `.Line2D` line style spec.
  2607. Examples
  2608. --------
  2609. ::
  2610. ax.tick_params(direction='out', length=6, width=2, colors='r',
  2611. grid_color='r', grid_alpha=0.5)
  2612. This will make all major ticks be red, pointing out of the box,
  2613. and with dimensions 6 points by 2 points. Tick labels will
  2614. also be red. Gridlines will be red and translucent.
  2615. """
  2616. cbook._check_in_list(['x', 'y', 'both'], axis=axis)
  2617. if axis in ['x', 'both']:
  2618. xkw = dict(kwargs)
  2619. xkw.pop('left', None)
  2620. xkw.pop('right', None)
  2621. xkw.pop('labelleft', None)
  2622. xkw.pop('labelright', None)
  2623. self.xaxis.set_tick_params(**xkw)
  2624. if axis in ['y', 'both']:
  2625. ykw = dict(kwargs)
  2626. ykw.pop('top', None)
  2627. ykw.pop('bottom', None)
  2628. ykw.pop('labeltop', None)
  2629. ykw.pop('labelbottom', None)
  2630. self.yaxis.set_tick_params(**ykw)
  2631. def set_axis_off(self):
  2632. """
  2633. Turn the x- and y-axis off.
  2634. This affects the axis lines, ticks, ticklabels, grid and axis labels.
  2635. """
  2636. self.axison = False
  2637. self.stale = True
  2638. def set_axis_on(self):
  2639. """
  2640. Turn the x- and y-axis on.
  2641. This affects the axis lines, ticks, ticklabels, grid and axis labels.
  2642. """
  2643. self.axison = True
  2644. self.stale = True
  2645. # data limits, ticks, tick labels, and formatting
  2646. def invert_xaxis(self):
  2647. """
  2648. Invert the x-axis.
  2649. See Also
  2650. --------
  2651. xaxis_inverted
  2652. get_xlim, set_xlim
  2653. get_xbound, set_xbound
  2654. """
  2655. self.xaxis.set_inverted(not self.xaxis.get_inverted())
  2656. xaxis_inverted = _axis_method_wrapper("xaxis", "get_inverted")
  2657. def get_xbound(self):
  2658. """
  2659. Return the lower and upper x-axis bounds, in increasing order.
  2660. See Also
  2661. --------
  2662. set_xbound
  2663. get_xlim, set_xlim
  2664. invert_xaxis, xaxis_inverted
  2665. """
  2666. left, right = self.get_xlim()
  2667. if left < right:
  2668. return left, right
  2669. else:
  2670. return right, left
  2671. def set_xbound(self, lower=None, upper=None):
  2672. """
  2673. Set the lower and upper numerical bounds of the x-axis.
  2674. This method will honor axes inversion regardless of parameter order.
  2675. It will not change the autoscaling setting (`.get_autoscalex_on()`).
  2676. Parameters
  2677. ----------
  2678. lower, upper : float or None
  2679. The lower and upper bounds. If *None*, the respective axis bound
  2680. is not modified.
  2681. See Also
  2682. --------
  2683. get_xbound
  2684. get_xlim, set_xlim
  2685. invert_xaxis, xaxis_inverted
  2686. """
  2687. if upper is None and np.iterable(lower):
  2688. lower, upper = lower
  2689. old_lower, old_upper = self.get_xbound()
  2690. if lower is None:
  2691. lower = old_lower
  2692. if upper is None:
  2693. upper = old_upper
  2694. self.set_xlim(sorted((lower, upper),
  2695. reverse=bool(self.xaxis_inverted())),
  2696. auto=None)
  2697. def get_xlim(self):
  2698. """
  2699. Return the x-axis view limits.
  2700. Returns
  2701. -------
  2702. left, right : (float, float)
  2703. The current x-axis limits in data coordinates.
  2704. See Also
  2705. --------
  2706. set_xlim
  2707. set_xbound, get_xbound
  2708. invert_xaxis, xaxis_inverted
  2709. Notes
  2710. -----
  2711. The x-axis may be inverted, in which case the *left* value will
  2712. be greater than the *right* value.
  2713. """
  2714. return tuple(self.viewLim.intervalx)
  2715. def _validate_converted_limits(self, limit, convert):
  2716. """
  2717. Raise ValueError if converted limits are non-finite.
  2718. Note that this function also accepts None as a limit argument.
  2719. Returns
  2720. -------
  2721. The limit value after call to convert(), or None if limit is None.
  2722. """
  2723. if limit is not None:
  2724. converted_limit = convert(limit)
  2725. if (isinstance(converted_limit, Real)
  2726. and not np.isfinite(converted_limit)):
  2727. raise ValueError("Axis limits cannot be NaN or Inf")
  2728. return converted_limit
  2729. def set_xlim(self, left=None, right=None, emit=True, auto=False,
  2730. *, xmin=None, xmax=None):
  2731. """
  2732. Set the x-axis view limits.
  2733. Parameters
  2734. ----------
  2735. left : float, optional
  2736. The left xlim in data coordinates. Passing *None* leaves the
  2737. limit unchanged.
  2738. The left and right xlims may also be passed as the tuple
  2739. (*left*, *right*) as the first positional argument (or as
  2740. the *left* keyword argument).
  2741. .. ACCEPTS: (bottom: float, top: float)
  2742. right : float, optional
  2743. The right xlim in data coordinates. Passing *None* leaves the
  2744. limit unchanged.
  2745. emit : bool, default: True
  2746. Whether to notify observers of limit change.
  2747. auto : bool or None, default: False
  2748. Whether to turn on autoscaling of the x-axis. True turns on,
  2749. False turns off, None leaves unchanged.
  2750. xmin, xmax : float, optional
  2751. They are equivalent to left and right respectively,
  2752. and it is an error to pass both *xmin* and *left* or
  2753. *xmax* and *right*.
  2754. Returns
  2755. -------
  2756. left, right : (float, float)
  2757. The new x-axis limits in data coordinates.
  2758. See Also
  2759. --------
  2760. get_xlim
  2761. set_xbound, get_xbound
  2762. invert_xaxis, xaxis_inverted
  2763. Notes
  2764. -----
  2765. The *left* value may be greater than the *right* value, in which
  2766. case the x-axis values will decrease from left to right.
  2767. Examples
  2768. --------
  2769. >>> set_xlim(left, right)
  2770. >>> set_xlim((left, right))
  2771. >>> left, right = set_xlim(left, right)
  2772. One limit may be left unchanged.
  2773. >>> set_xlim(right=right_lim)
  2774. Limits may be passed in reverse order to flip the direction of
  2775. the x-axis. For example, suppose *x* represents the number of
  2776. years before present. The x-axis limits might be set like the
  2777. following so 5000 years ago is on the left of the plot and the
  2778. present is on the right.
  2779. >>> set_xlim(5000, 0)
  2780. """
  2781. if right is None and np.iterable(left):
  2782. left, right = left
  2783. if xmin is not None:
  2784. if left is not None:
  2785. raise TypeError('Cannot pass both `xmin` and `left`')
  2786. left = xmin
  2787. if xmax is not None:
  2788. if right is not None:
  2789. raise TypeError('Cannot pass both `xmax` and `right`')
  2790. right = xmax
  2791. self._process_unit_info(xdata=(left, right))
  2792. left = self._validate_converted_limits(left, self.convert_xunits)
  2793. right = self._validate_converted_limits(right, self.convert_xunits)
  2794. if left is None or right is None:
  2795. # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
  2796. # so only grab the limits if we really need them.
  2797. old_left, old_right = self.get_xlim()
  2798. if left is None:
  2799. left = old_left
  2800. if right is None:
  2801. right = old_right
  2802. if self.get_xscale() == 'log' and (left <= 0 or right <= 0):
  2803. # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
  2804. # so only grab the limits if we really need them.
  2805. old_left, old_right = self.get_xlim()
  2806. if left <= 0:
  2807. cbook._warn_external(
  2808. 'Attempted to set non-positive left xlim on a '
  2809. 'log-scaled axis.\n'
  2810. 'Invalid limit will be ignored.')
  2811. left = old_left
  2812. if right <= 0:
  2813. cbook._warn_external(
  2814. 'Attempted to set non-positive right xlim on a '
  2815. 'log-scaled axis.\n'
  2816. 'Invalid limit will be ignored.')
  2817. right = old_right
  2818. if left == right:
  2819. cbook._warn_external(
  2820. f"Attempting to set identical left == right == {left} results "
  2821. f"in singular transformations; automatically expanding.")
  2822. reverse = left > right
  2823. left, right = self.xaxis.get_major_locator().nonsingular(left, right)
  2824. left, right = self.xaxis.limit_range_for_scale(left, right)
  2825. # cast to bool to avoid bad interaction between python 3.8 and np.bool_
  2826. left, right = sorted([left, right], reverse=bool(reverse))
  2827. self._viewLim.intervalx = (left, right)
  2828. # Mark viewlims as no longer stale without triggering an autoscale.
  2829. for ax in self._shared_x_axes.get_siblings(self):
  2830. ax._stale_viewlim_x = False
  2831. if auto is not None:
  2832. self._autoscaleXon = bool(auto)
  2833. if emit:
  2834. self.callbacks.process('xlim_changed', self)
  2835. # Call all of the other x-axes that are shared with this one
  2836. for other in self._shared_x_axes.get_siblings(self):
  2837. if other is not self:
  2838. other.set_xlim(self.viewLim.intervalx,
  2839. emit=False, auto=auto)
  2840. if other.figure != self.figure:
  2841. other.figure.canvas.draw_idle()
  2842. self.stale = True
  2843. return left, right
  2844. get_xscale = _axis_method_wrapper("xaxis", "get_scale")
  2845. def set_xscale(self, value, **kwargs):
  2846. """
  2847. Set the x-axis scale.
  2848. Parameters
  2849. ----------
  2850. value : {"linear", "log", "symlog", "logit", ...}
  2851. The axis scale type to apply.
  2852. **kwargs
  2853. Different keyword arguments are accepted, depending on the scale.
  2854. See the respective class keyword arguments:
  2855. - `matplotlib.scale.LinearScale`
  2856. - `matplotlib.scale.LogScale`
  2857. - `matplotlib.scale.SymmetricalLogScale`
  2858. - `matplotlib.scale.LogitScale`
  2859. Notes
  2860. -----
  2861. By default, Matplotlib supports the above mentioned scales.
  2862. Additionally, custom scales may be registered using
  2863. `matplotlib.scale.register_scale`. These scales can then also
  2864. be used here.
  2865. """
  2866. old_default_lims = (self.xaxis.get_major_locator()
  2867. .nonsingular(-np.inf, np.inf))
  2868. g = self.get_shared_x_axes()
  2869. for ax in g.get_siblings(self):
  2870. ax.xaxis._set_scale(value, **kwargs)
  2871. ax._update_transScale()
  2872. ax.stale = True
  2873. new_default_lims = (self.xaxis.get_major_locator()
  2874. .nonsingular(-np.inf, np.inf))
  2875. if old_default_lims != new_default_lims:
  2876. # Force autoscaling now, to take advantage of the scale locator's
  2877. # nonsingular() before it possibly gets swapped out by the user.
  2878. self.autoscale_view(scaley=False)
  2879. get_xticks = _axis_method_wrapper("xaxis", "get_ticklocs")
  2880. set_xticks = _axis_method_wrapper("xaxis", "set_ticks")
  2881. get_xmajorticklabels = _axis_method_wrapper("xaxis", "get_majorticklabels")
  2882. get_xminorticklabels = _axis_method_wrapper("xaxis", "get_minorticklabels")
  2883. get_xticklabels = _axis_method_wrapper("xaxis", "get_ticklabels")
  2884. set_xticklabels = _axis_method_wrapper(
  2885. "xaxis", "_set_ticklabels",
  2886. doc_sub={"Axis.set_ticks": "Axes.set_xticks"})
  2887. def invert_yaxis(self):
  2888. """
  2889. Invert the y-axis.
  2890. See Also
  2891. --------
  2892. yaxis_inverted
  2893. get_ylim, set_ylim
  2894. get_ybound, set_ybound
  2895. """
  2896. self.yaxis.set_inverted(not self.yaxis.get_inverted())
  2897. yaxis_inverted = _axis_method_wrapper("yaxis", "get_inverted")
  2898. def get_ybound(self):
  2899. """
  2900. Return the lower and upper y-axis bounds, in increasing order.
  2901. See Also
  2902. --------
  2903. set_ybound
  2904. get_ylim, set_ylim
  2905. invert_yaxis, yaxis_inverted
  2906. """
  2907. bottom, top = self.get_ylim()
  2908. if bottom < top:
  2909. return bottom, top
  2910. else:
  2911. return top, bottom
  2912. def set_ybound(self, lower=None, upper=None):
  2913. """
  2914. Set the lower and upper numerical bounds of the y-axis.
  2915. This method will honor axes inversion regardless of parameter order.
  2916. It will not change the autoscaling setting (`.get_autoscaley_on()`).
  2917. Parameters
  2918. ----------
  2919. lower, upper : float or None
  2920. The lower and upper bounds. If *None*, the respective axis bound
  2921. is not modified.
  2922. See Also
  2923. --------
  2924. get_ybound
  2925. get_ylim, set_ylim
  2926. invert_yaxis, yaxis_inverted
  2927. """
  2928. if upper is None and np.iterable(lower):
  2929. lower, upper = lower
  2930. old_lower, old_upper = self.get_ybound()
  2931. if lower is None:
  2932. lower = old_lower
  2933. if upper is None:
  2934. upper = old_upper
  2935. self.set_ylim(sorted((lower, upper),
  2936. reverse=bool(self.yaxis_inverted())),
  2937. auto=None)
  2938. def get_ylim(self):
  2939. """
  2940. Return the y-axis view limits.
  2941. Returns
  2942. -------
  2943. bottom, top : (float, float)
  2944. The current y-axis limits in data coordinates.
  2945. See Also
  2946. --------
  2947. set_ylim
  2948. set_ybound, get_ybound
  2949. invert_yaxis, yaxis_inverted
  2950. Notes
  2951. -----
  2952. The y-axis may be inverted, in which case the *bottom* value
  2953. will be greater than the *top* value.
  2954. """
  2955. return tuple(self.viewLim.intervaly)
  2956. def set_ylim(self, bottom=None, top=None, emit=True, auto=False,
  2957. *, ymin=None, ymax=None):
  2958. """
  2959. Set the y-axis view limits.
  2960. Parameters
  2961. ----------
  2962. bottom : float, optional
  2963. The bottom ylim in data coordinates. Passing *None* leaves the
  2964. limit unchanged.
  2965. The bottom and top ylims may also be passed as the tuple
  2966. (*bottom*, *top*) as the first positional argument (or as
  2967. the *bottom* keyword argument).
  2968. .. ACCEPTS: (bottom: float, top: float)
  2969. top : float, optional
  2970. The top ylim in data coordinates. Passing *None* leaves the
  2971. limit unchanged.
  2972. emit : bool, default: True
  2973. Whether to notify observers of limit change.
  2974. auto : bool or None, default: False
  2975. Whether to turn on autoscaling of the y-axis. *True* turns on,
  2976. *False* turns off, *None* leaves unchanged.
  2977. ymin, ymax : float, optional
  2978. They are equivalent to bottom and top respectively,
  2979. and it is an error to pass both *ymin* and *bottom* or
  2980. *ymax* and *top*.
  2981. Returns
  2982. -------
  2983. bottom, top : (float, float)
  2984. The new y-axis limits in data coordinates.
  2985. See Also
  2986. --------
  2987. get_ylim
  2988. set_ybound, get_ybound
  2989. invert_yaxis, yaxis_inverted
  2990. Notes
  2991. -----
  2992. The *bottom* value may be greater than the *top* value, in which
  2993. case the y-axis values will decrease from *bottom* to *top*.
  2994. Examples
  2995. --------
  2996. >>> set_ylim(bottom, top)
  2997. >>> set_ylim((bottom, top))
  2998. >>> bottom, top = set_ylim(bottom, top)
  2999. One limit may be left unchanged.
  3000. >>> set_ylim(top=top_lim)
  3001. Limits may be passed in reverse order to flip the direction of
  3002. the y-axis. For example, suppose ``y`` represents depth of the
  3003. ocean in m. The y-axis limits might be set like the following
  3004. so 5000 m depth is at the bottom of the plot and the surface,
  3005. 0 m, is at the top.
  3006. >>> set_ylim(5000, 0)
  3007. """
  3008. if top is None and np.iterable(bottom):
  3009. bottom, top = bottom
  3010. if ymin is not None:
  3011. if bottom is not None:
  3012. raise TypeError('Cannot pass both `ymin` and `bottom`')
  3013. bottom = ymin
  3014. if ymax is not None:
  3015. if top is not None:
  3016. raise TypeError('Cannot pass both `ymax` and `top`')
  3017. top = ymax
  3018. self._process_unit_info(ydata=(bottom, top))
  3019. bottom = self._validate_converted_limits(bottom, self.convert_yunits)
  3020. top = self._validate_converted_limits(top, self.convert_yunits)
  3021. if bottom is None or top is None:
  3022. # Axes init calls set_ylim(0, 1) before get_ylim() can be called,
  3023. # so only grab the limits if we really need them.
  3024. old_bottom, old_top = self.get_ylim()
  3025. if bottom is None:
  3026. bottom = old_bottom
  3027. if top is None:
  3028. top = old_top
  3029. if self.get_yscale() == 'log' and (bottom <= 0 or top <= 0):
  3030. # Axes init calls set_xlim(0, 1) before get_xlim() can be called,
  3031. # so only grab the limits if we really need them.
  3032. old_bottom, old_top = self.get_ylim()
  3033. if bottom <= 0:
  3034. cbook._warn_external(
  3035. 'Attempted to set non-positive bottom ylim on a '
  3036. 'log-scaled axis.\n'
  3037. 'Invalid limit will be ignored.')
  3038. bottom = old_bottom
  3039. if top <= 0:
  3040. cbook._warn_external(
  3041. 'Attempted to set non-positive top ylim on a '
  3042. 'log-scaled axis.\n'
  3043. 'Invalid limit will be ignored.')
  3044. top = old_top
  3045. if bottom == top:
  3046. cbook._warn_external(
  3047. f"Attempting to set identical bottom == top == {bottom} "
  3048. f"results in singular transformations; automatically "
  3049. f"expanding.")
  3050. reverse = bottom > top
  3051. bottom, top = self.yaxis.get_major_locator().nonsingular(bottom, top)
  3052. bottom, top = self.yaxis.limit_range_for_scale(bottom, top)
  3053. # cast to bool to avoid bad interaction between python 3.8 and np.bool_
  3054. bottom, top = sorted([bottom, top], reverse=bool(reverse))
  3055. self._viewLim.intervaly = (bottom, top)
  3056. # Mark viewlims as no longer stale without triggering an autoscale.
  3057. for ax in self._shared_y_axes.get_siblings(self):
  3058. ax._stale_viewlim_y = False
  3059. if auto is not None:
  3060. self._autoscaleYon = bool(auto)
  3061. if emit:
  3062. self.callbacks.process('ylim_changed', self)
  3063. # Call all of the other y-axes that are shared with this one
  3064. for other in self._shared_y_axes.get_siblings(self):
  3065. if other is not self:
  3066. other.set_ylim(self.viewLim.intervaly,
  3067. emit=False, auto=auto)
  3068. if other.figure != self.figure:
  3069. other.figure.canvas.draw_idle()
  3070. self.stale = True
  3071. return bottom, top
  3072. get_yscale = _axis_method_wrapper("yaxis", "get_scale")
  3073. def set_yscale(self, value, **kwargs):
  3074. """
  3075. Set the y-axis scale.
  3076. Parameters
  3077. ----------
  3078. value : {"linear", "log", "symlog", "logit", ...}
  3079. The axis scale type to apply.
  3080. **kwargs
  3081. Different keyword arguments are accepted, depending on the scale.
  3082. See the respective class keyword arguments:
  3083. - `matplotlib.scale.LinearScale`
  3084. - `matplotlib.scale.LogScale`
  3085. - `matplotlib.scale.SymmetricalLogScale`
  3086. - `matplotlib.scale.LogitScale`
  3087. Notes
  3088. -----
  3089. By default, Matplotlib supports the above mentioned scales.
  3090. Additionally, custom scales may be registered using
  3091. `matplotlib.scale.register_scale`. These scales can then also
  3092. be used here.
  3093. """
  3094. old_default_lims = (self.yaxis.get_major_locator()
  3095. .nonsingular(-np.inf, np.inf))
  3096. g = self.get_shared_y_axes()
  3097. for ax in g.get_siblings(self):
  3098. ax.yaxis._set_scale(value, **kwargs)
  3099. ax._update_transScale()
  3100. ax.stale = True
  3101. new_default_lims = (self.yaxis.get_major_locator()
  3102. .nonsingular(-np.inf, np.inf))
  3103. if old_default_lims != new_default_lims:
  3104. # Force autoscaling now, to take advantage of the scale locator's
  3105. # nonsingular() before it possibly gets swapped out by the user.
  3106. self.autoscale_view(scalex=False)
  3107. get_yticks = _axis_method_wrapper("yaxis", "get_ticklocs")
  3108. set_yticks = _axis_method_wrapper("yaxis", "set_ticks")
  3109. get_ymajorticklabels = _axis_method_wrapper("yaxis", "get_majorticklabels")
  3110. get_yminorticklabels = _axis_method_wrapper("yaxis", "get_minorticklabels")
  3111. get_yticklabels = _axis_method_wrapper("yaxis", "get_ticklabels")
  3112. set_yticklabels = _axis_method_wrapper(
  3113. "yaxis", "_set_ticklabels",
  3114. doc_sub={"Axis.set_ticks": "Axes.set_yticks"})
  3115. xaxis_date = _axis_method_wrapper("xaxis", "axis_date")
  3116. yaxis_date = _axis_method_wrapper("yaxis", "axis_date")
  3117. def format_xdata(self, x):
  3118. """
  3119. Return *x* formatted as an x-value.
  3120. This function will use the `.fmt_xdata` attribute if it is not None,
  3121. else will fall back on the xaxis major formatter.
  3122. """
  3123. return (self.fmt_xdata if self.fmt_xdata is not None
  3124. else self.xaxis.get_major_formatter().format_data_short)(x)
  3125. def format_ydata(self, y):
  3126. """
  3127. Return *y* formatted as an y-value.
  3128. This function will use the `.fmt_ydata` attribute if it is not None,
  3129. else will fall back on the yaxis major formatter.
  3130. """
  3131. return (self.fmt_ydata if self.fmt_ydata is not None
  3132. else self.yaxis.get_major_formatter().format_data_short)(y)
  3133. def format_coord(self, x, y):
  3134. """Return a format string formatting the *x*, *y* coordinates."""
  3135. if x is None:
  3136. xs = '???'
  3137. else:
  3138. xs = self.format_xdata(x)
  3139. if y is None:
  3140. ys = '???'
  3141. else:
  3142. ys = self.format_ydata(y)
  3143. return 'x=%s y=%s' % (xs, ys)
  3144. def minorticks_on(self):
  3145. """
  3146. Display minor ticks on the axes.
  3147. Displaying minor ticks may reduce performance; you may turn them off
  3148. using `minorticks_off()` if drawing speed is a problem.
  3149. """
  3150. for ax in (self.xaxis, self.yaxis):
  3151. scale = ax.get_scale()
  3152. if scale == 'log':
  3153. s = ax._scale
  3154. ax.set_minor_locator(mticker.LogLocator(s.base, s.subs))
  3155. elif scale == 'symlog':
  3156. s = ax._scale
  3157. ax.set_minor_locator(
  3158. mticker.SymmetricalLogLocator(s._transform, s.subs))
  3159. else:
  3160. ax.set_minor_locator(mticker.AutoMinorLocator())
  3161. def minorticks_off(self):
  3162. """Remove minor ticks from the axes."""
  3163. self.xaxis.set_minor_locator(mticker.NullLocator())
  3164. self.yaxis.set_minor_locator(mticker.NullLocator())
  3165. # Interactive manipulation
  3166. def can_zoom(self):
  3167. """
  3168. Return *True* if this axes supports the zoom box button functionality.
  3169. """
  3170. return True
  3171. def can_pan(self):
  3172. """
  3173. Return *True* if this axes supports any pan/zoom button functionality.
  3174. """
  3175. return True
  3176. def get_navigate(self):
  3177. """
  3178. Get whether the axes responds to navigation commands
  3179. """
  3180. return self._navigate
  3181. def set_navigate(self, b):
  3182. """
  3183. Set whether the axes responds to navigation toolbar commands
  3184. Parameters
  3185. ----------
  3186. b : bool
  3187. """
  3188. self._navigate = b
  3189. def get_navigate_mode(self):
  3190. """
  3191. Get the navigation toolbar button status: 'PAN', 'ZOOM', or None
  3192. """
  3193. return self._navigate_mode
  3194. def set_navigate_mode(self, b):
  3195. """
  3196. Set the navigation toolbar button status;
  3197. .. warning::
  3198. this is not a user-API function.
  3199. """
  3200. self._navigate_mode = b
  3201. def _get_view(self):
  3202. """
  3203. Save information required to reproduce the current view.
  3204. Called before a view is changed, such as during a pan or zoom
  3205. initiated by the user. You may return any information you deem
  3206. necessary to describe the view.
  3207. .. note::
  3208. Intended to be overridden by new projection types, but if not, the
  3209. default implementation saves the view limits. You *must* implement
  3210. :meth:`_set_view` if you implement this method.
  3211. """
  3212. xmin, xmax = self.get_xlim()
  3213. ymin, ymax = self.get_ylim()
  3214. return xmin, xmax, ymin, ymax
  3215. def _set_view(self, view):
  3216. """
  3217. Apply a previously saved view.
  3218. Called when restoring a view, such as with the navigation buttons.
  3219. .. note::
  3220. Intended to be overridden by new projection types, but if not, the
  3221. default implementation restores the view limits. You *must*
  3222. implement :meth:`_get_view` if you implement this method.
  3223. """
  3224. xmin, xmax, ymin, ymax = view
  3225. self.set_xlim((xmin, xmax))
  3226. self.set_ylim((ymin, ymax))
  3227. def _set_view_from_bbox(self, bbox, direction='in',
  3228. mode=None, twinx=False, twiny=False):
  3229. """
  3230. Update view from a selection bbox.
  3231. .. note::
  3232. Intended to be overridden by new projection types, but if not, the
  3233. default implementation sets the view limits to the bbox directly.
  3234. Parameters
  3235. ----------
  3236. bbox : 4-tuple or 3 tuple
  3237. * If bbox is a 4 tuple, it is the selected bounding box limits,
  3238. in *display* coordinates.
  3239. * If bbox is a 3 tuple, it is an (xp, yp, scl) triple, where
  3240. (xp, yp) is the center of zooming and scl the scale factor to
  3241. zoom by.
  3242. direction : str
  3243. The direction to apply the bounding box.
  3244. * `'in'` - The bounding box describes the view directly, i.e.,
  3245. it zooms in.
  3246. * `'out'` - The bounding box describes the size to make the
  3247. existing view, i.e., it zooms out.
  3248. mode : str or None
  3249. The selection mode, whether to apply the bounding box in only the
  3250. `'x'` direction, `'y'` direction or both (`None`).
  3251. twinx : bool
  3252. Whether this axis is twinned in the *x*-direction.
  3253. twiny : bool
  3254. Whether this axis is twinned in the *y*-direction.
  3255. """
  3256. if len(bbox) == 3:
  3257. Xmin, Xmax = self.get_xlim()
  3258. Ymin, Ymax = self.get_ylim()
  3259. xp, yp, scl = bbox # Zooming code
  3260. if scl == 0: # Should not happen
  3261. scl = 1.
  3262. if scl > 1:
  3263. direction = 'in'
  3264. else:
  3265. direction = 'out'
  3266. scl = 1/scl
  3267. # get the limits of the axes
  3268. tranD2C = self.transData.transform
  3269. xmin, ymin = tranD2C((Xmin, Ymin))
  3270. xmax, ymax = tranD2C((Xmax, Ymax))
  3271. # set the range
  3272. xwidth = xmax - xmin
  3273. ywidth = ymax - ymin
  3274. xcen = (xmax + xmin)*.5
  3275. ycen = (ymax + ymin)*.5
  3276. xzc = (xp*(scl - 1) + xcen)/scl
  3277. yzc = (yp*(scl - 1) + ycen)/scl
  3278. bbox = [xzc - xwidth/2./scl, yzc - ywidth/2./scl,
  3279. xzc + xwidth/2./scl, yzc + ywidth/2./scl]
  3280. elif len(bbox) != 4:
  3281. # should be len 3 or 4 but nothing else
  3282. cbook._warn_external(
  3283. "Warning in _set_view_from_bbox: bounding box is not a tuple "
  3284. "of length 3 or 4. Ignoring the view change.")
  3285. return
  3286. # Original limits.
  3287. xmin0, xmax0 = self.get_xbound()
  3288. ymin0, ymax0 = self.get_ybound()
  3289. # The zoom box in screen coords.
  3290. startx, starty, stopx, stopy = bbox
  3291. # Convert to data coords.
  3292. (startx, starty), (stopx, stopy) = self.transData.inverted().transform(
  3293. [(startx, starty), (stopx, stopy)])
  3294. # Clip to axes limits.
  3295. xmin, xmax = np.clip(sorted([startx, stopx]), xmin0, xmax0)
  3296. ymin, ymax = np.clip(sorted([starty, stopy]), ymin0, ymax0)
  3297. # Don't double-zoom twinned axes or if zooming only the other axis.
  3298. if twinx or mode == "y":
  3299. xmin, xmax = xmin0, xmax0
  3300. if twiny or mode == "x":
  3301. ymin, ymax = ymin0, ymax0
  3302. if direction == "in":
  3303. new_xbound = xmin, xmax
  3304. new_ybound = ymin, ymax
  3305. elif direction == "out":
  3306. x_trf = self.xaxis.get_transform()
  3307. sxmin0, sxmax0, sxmin, sxmax = x_trf.transform(
  3308. [xmin0, xmax0, xmin, xmax]) # To screen space.
  3309. factor = (sxmax0 - sxmin0) / (sxmax - sxmin) # Unzoom factor.
  3310. # Move original bounds away by
  3311. # (factor) x (distance between unzoom box and axes bbox).
  3312. sxmin1 = sxmin0 - factor * (sxmin - sxmin0)
  3313. sxmax1 = sxmax0 + factor * (sxmax0 - sxmax)
  3314. # And back to data space.
  3315. new_xbound = x_trf.inverted().transform([sxmin1, sxmax1])
  3316. y_trf = self.yaxis.get_transform()
  3317. symin0, symax0, symin, symax = y_trf.transform(
  3318. [ymin0, ymax0, ymin, ymax])
  3319. factor = (symax0 - symin0) / (symax - symin)
  3320. symin1 = symin0 - factor * (symin - symin0)
  3321. symax1 = symax0 + factor * (symax0 - symax)
  3322. new_ybound = y_trf.inverted().transform([symin1, symax1])
  3323. if not twinx and mode != "y":
  3324. self.set_xbound(new_xbound)
  3325. if not twiny and mode != "x":
  3326. self.set_ybound(new_ybound)
  3327. def start_pan(self, x, y, button):
  3328. """
  3329. Called when a pan operation has started.
  3330. Parameters
  3331. ----------
  3332. x, y : float
  3333. The mouse coordinates in display coords.
  3334. button : `.MouseButton`
  3335. The pressed mouse button.
  3336. Notes
  3337. -----
  3338. This is intended to be overridden by new projection types.
  3339. """
  3340. self._pan_start = types.SimpleNamespace(
  3341. lim=self.viewLim.frozen(),
  3342. trans=self.transData.frozen(),
  3343. trans_inverse=self.transData.inverted().frozen(),
  3344. bbox=self.bbox.frozen(),
  3345. x=x,
  3346. y=y)
  3347. def end_pan(self):
  3348. """
  3349. Called when a pan operation completes (when the mouse button is up.)
  3350. Notes
  3351. -----
  3352. This is intended to be overridden by new projection types.
  3353. """
  3354. del self._pan_start
  3355. def drag_pan(self, button, key, x, y):
  3356. """
  3357. Called when the mouse moves during a pan operation.
  3358. Parameters
  3359. ----------
  3360. button : `.MouseButton`
  3361. The pressed mouse button.
  3362. key : str or None
  3363. The pressed key, if any.
  3364. x, y : float
  3365. The mouse coordinates in display coords.
  3366. Notes
  3367. -----
  3368. This is intended to be overridden by new projection types.
  3369. """
  3370. def format_deltas(key, dx, dy):
  3371. if key == 'control':
  3372. if abs(dx) > abs(dy):
  3373. dy = dx
  3374. else:
  3375. dx = dy
  3376. elif key == 'x':
  3377. dy = 0
  3378. elif key == 'y':
  3379. dx = 0
  3380. elif key == 'shift':
  3381. if 2 * abs(dx) < abs(dy):
  3382. dx = 0
  3383. elif 2 * abs(dy) < abs(dx):
  3384. dy = 0
  3385. elif abs(dx) > abs(dy):
  3386. dy = dy / abs(dy) * abs(dx)
  3387. else:
  3388. dx = dx / abs(dx) * abs(dy)
  3389. return dx, dy
  3390. p = self._pan_start
  3391. dx = x - p.x
  3392. dy = y - p.y
  3393. if dx == dy == 0:
  3394. return
  3395. if button == 1:
  3396. dx, dy = format_deltas(key, dx, dy)
  3397. result = p.bbox.translated(-dx, -dy).transformed(p.trans_inverse)
  3398. elif button == 3:
  3399. try:
  3400. dx = -dx / self.bbox.width
  3401. dy = -dy / self.bbox.height
  3402. dx, dy = format_deltas(key, dx, dy)
  3403. if self.get_aspect() != 'auto':
  3404. dx = dy = 0.5 * (dx + dy)
  3405. alpha = np.power(10.0, (dx, dy))
  3406. start = np.array([p.x, p.y])
  3407. oldpoints = p.lim.transformed(p.trans)
  3408. newpoints = start + alpha * (oldpoints - start)
  3409. result = (mtransforms.Bbox(newpoints)
  3410. .transformed(p.trans_inverse))
  3411. except OverflowError:
  3412. cbook._warn_external('Overflow while panning')
  3413. return
  3414. else:
  3415. return
  3416. valid = np.isfinite(result.transformed(p.trans))
  3417. points = result.get_points().astype(object)
  3418. # Just ignore invalid limits (typically, underflow in log-scale).
  3419. points[~valid] = None
  3420. self.set_xlim(points[:, 0])
  3421. self.set_ylim(points[:, 1])
  3422. def get_children(self):
  3423. # docstring inherited.
  3424. return [
  3425. *self.collections,
  3426. *self.patches,
  3427. *self.lines,
  3428. *self.texts,
  3429. *self.artists,
  3430. *self.spines.values(),
  3431. *self._get_axis_list(),
  3432. self.title, self._left_title, self._right_title,
  3433. *self.tables,
  3434. *self.images,
  3435. *self.child_axes,
  3436. *([self.legend_] if self.legend_ is not None else []),
  3437. self.patch,
  3438. ]
  3439. def contains(self, mouseevent):
  3440. # docstring inherited.
  3441. inside, info = self._default_contains(mouseevent)
  3442. if inside is not None:
  3443. return inside, info
  3444. return self.patch.contains(mouseevent)
  3445. def contains_point(self, point):
  3446. """
  3447. Return whether *point* (pair of pixel coordinates) is inside the axes
  3448. patch.
  3449. """
  3450. return self.patch.contains_point(point, radius=1.0)
  3451. def get_default_bbox_extra_artists(self):
  3452. """
  3453. Return a default list of artists that are used for the bounding box
  3454. calculation.
  3455. Artists are excluded either by not being visible or
  3456. ``artist.set_in_layout(False)``.
  3457. """
  3458. artists = self.get_children()
  3459. if not (self.axison and self._frameon):
  3460. # don't do bbox on spines if frame not on.
  3461. for spine in self.spines.values():
  3462. artists.remove(spine)
  3463. if not self.axison:
  3464. for _axis in self._get_axis_list():
  3465. artists.remove(_axis)
  3466. artists.remove(self.title)
  3467. artists.remove(self._left_title)
  3468. artists.remove(self._right_title)
  3469. return [artist for artist in artists
  3470. if (artist.get_visible() and artist.get_in_layout())]
  3471. def get_tightbbox(self, renderer, call_axes_locator=True,
  3472. bbox_extra_artists=None, *, for_layout_only=False):
  3473. """
  3474. Return the tight bounding box of the axes, including axis and their
  3475. decorators (xlabel, title, etc).
  3476. Artists that have ``artist.set_in_layout(False)`` are not included
  3477. in the bbox.
  3478. Parameters
  3479. ----------
  3480. renderer : `.RendererBase` subclass
  3481. renderer that will be used to draw the figures (i.e.
  3482. ``fig.canvas.get_renderer()``)
  3483. bbox_extra_artists : list of `.Artist` or ``None``
  3484. List of artists to include in the tight bounding box. If
  3485. ``None`` (default), then all artist children of the axes are
  3486. included in the tight bounding box.
  3487. call_axes_locator : bool, default: True
  3488. If *call_axes_locator* is ``False``, it does not call the
  3489. ``_axes_locator`` attribute, which is necessary to get the correct
  3490. bounding box. ``call_axes_locator=False`` can be used if the
  3491. caller is only interested in the relative size of the tightbbox
  3492. compared to the axes bbox.
  3493. for_layout_only : default: False
  3494. The bounding box will *not* include the x-extent of the title and
  3495. the xlabel, or the y-extent of the ylabel.
  3496. Returns
  3497. -------
  3498. `.BboxBase`
  3499. Bounding box in figure pixel coordinates.
  3500. See Also
  3501. --------
  3502. matplotlib.axes.Axes.get_window_extent
  3503. matplotlib.axis.Axis.get_tightbbox
  3504. matplotlib.spines.Spine.get_window_extent
  3505. """
  3506. bb = []
  3507. if not self.get_visible():
  3508. return None
  3509. locator = self.get_axes_locator()
  3510. if locator and call_axes_locator:
  3511. pos = locator(self, renderer)
  3512. self.apply_aspect(pos)
  3513. else:
  3514. self.apply_aspect()
  3515. if self.axison:
  3516. if self.xaxis.get_visible():
  3517. try:
  3518. bb_xaxis = self.xaxis.get_tightbbox(
  3519. renderer, for_layout_only=for_layout_only)
  3520. except TypeError:
  3521. # in case downstream library has redefined axis:
  3522. bb_xaxis = self.xaxis.get_tightbbox(renderer)
  3523. if bb_xaxis:
  3524. bb.append(bb_xaxis)
  3525. if self.yaxis.get_visible():
  3526. try:
  3527. bb_yaxis = self.yaxis.get_tightbbox(
  3528. renderer, for_layout_only=for_layout_only)
  3529. except TypeError:
  3530. # in case downstream library has redefined axis:
  3531. bb_yaxis = self.yaxis.get_tightbbox(renderer)
  3532. if bb_yaxis:
  3533. bb.append(bb_yaxis)
  3534. self._update_title_position(renderer)
  3535. axbbox = self.get_window_extent(renderer)
  3536. bb.append(axbbox)
  3537. for title in [self.title, self._left_title, self._right_title]:
  3538. if title.get_visible():
  3539. bt = title.get_window_extent(renderer)
  3540. if for_layout_only and bt.width > 0:
  3541. # make the title bbox 1 pixel wide so its width
  3542. # is not accounted for in bbox calculations in
  3543. # tight/constrained_layout
  3544. bt.x0 = (bt.x0 + bt.x1) / 2 - 0.5
  3545. bt.x1 = bt.x0 + 1.0
  3546. bb.append(bt)
  3547. bbox_artists = bbox_extra_artists
  3548. if bbox_artists is None:
  3549. bbox_artists = self.get_default_bbox_extra_artists()
  3550. for a in bbox_artists:
  3551. # Extra check here to quickly see if clipping is on and
  3552. # contained in the axes. If it is, don't get the tightbbox for
  3553. # this artist because this can be expensive:
  3554. clip_extent = a._get_clipping_extent_bbox()
  3555. if clip_extent is not None:
  3556. clip_extent = mtransforms.Bbox.intersection(
  3557. clip_extent, axbbox)
  3558. if np.all(clip_extent.extents == axbbox.extents):
  3559. # clip extent is inside the axes bbox so don't check
  3560. # this artist
  3561. continue
  3562. bbox = a.get_tightbbox(renderer)
  3563. if (bbox is not None
  3564. and 0 < bbox.width < np.inf
  3565. and 0 < bbox.height < np.inf):
  3566. bb.append(bbox)
  3567. return mtransforms.Bbox.union(
  3568. [b for b in bb if b.width != 0 or b.height != 0])
  3569. def _make_twin_axes(self, *args, **kwargs):
  3570. """Make a twinx axes of self. This is used for twinx and twiny."""
  3571. # Typically, SubplotBase._make_twin_axes is called instead of this.
  3572. if 'sharex' in kwargs and 'sharey' in kwargs:
  3573. raise ValueError("Twinned Axes may share only one axis")
  3574. ax2 = self.figure.add_axes(self.get_position(True), *args, **kwargs)
  3575. self.set_adjustable('datalim')
  3576. ax2.set_adjustable('datalim')
  3577. self._twinned_axes.join(self, ax2)
  3578. return ax2
  3579. def twinx(self):
  3580. """
  3581. Create a twin Axes sharing the xaxis.
  3582. Create a new Axes with an invisible x-axis and an independent
  3583. y-axis positioned opposite to the original one (i.e. at right). The
  3584. x-axis autoscale setting will be inherited from the original
  3585. Axes. To ensure that the tick marks of both y-axes align, see
  3586. `~matplotlib.ticker.LinearLocator`.
  3587. Returns
  3588. -------
  3589. Axes
  3590. The newly created Axes instance
  3591. Notes
  3592. -----
  3593. For those who are 'picking' artists while using twinx, pick
  3594. events are only called for the artists in the top-most axes.
  3595. """
  3596. ax2 = self._make_twin_axes(sharex=self)
  3597. ax2.yaxis.tick_right()
  3598. ax2.yaxis.set_label_position('right')
  3599. ax2.yaxis.set_offset_position('right')
  3600. ax2.set_autoscalex_on(self.get_autoscalex_on())
  3601. self.yaxis.tick_left()
  3602. ax2.xaxis.set_visible(False)
  3603. ax2.patch.set_visible(False)
  3604. return ax2
  3605. def twiny(self):
  3606. """
  3607. Create a twin Axes sharing the yaxis.
  3608. Create a new Axes with an invisible y-axis and an independent
  3609. x-axis positioned opposite to the original one (i.e. at top). The
  3610. y-axis autoscale setting will be inherited from the original Axes.
  3611. To ensure that the tick marks of both x-axes align, see
  3612. `~matplotlib.ticker.LinearLocator`.
  3613. Returns
  3614. -------
  3615. Axes
  3616. The newly created Axes instance
  3617. Notes
  3618. -----
  3619. For those who are 'picking' artists while using twiny, pick
  3620. events are only called for the artists in the top-most axes.
  3621. """
  3622. ax2 = self._make_twin_axes(sharey=self)
  3623. ax2.xaxis.tick_top()
  3624. ax2.xaxis.set_label_position('top')
  3625. ax2.set_autoscaley_on(self.get_autoscaley_on())
  3626. self.xaxis.tick_bottom()
  3627. ax2.yaxis.set_visible(False)
  3628. ax2.patch.set_visible(False)
  3629. return ax2
  3630. def get_shared_x_axes(self):
  3631. """Return a reference to the shared axes Grouper object for x axes."""
  3632. return self._shared_x_axes
  3633. def get_shared_y_axes(self):
  3634. """Return a reference to the shared axes Grouper object for y axes."""
  3635. return self._shared_y_axes