11import numpy as np
22import pandas as pd
3+ from matplotlib import pyplot as plt
4+ from numpy .testing import assert_allclose
35
6+ from plotnine import (
7+ aes ,
8+ coord_radial ,
9+ element_blank ,
10+ element_line ,
11+ geom_col ,
12+ ggplot ,
13+ theme ,
14+ )
415from plotnine .coords .coord_polar import coord_polar
16+ from plotnine .scales import scale_x_continuous , scale_y_continuous
17+
18+
19+ def trained_scales (
20+ x = (0 , 10 ),
21+ y = (0 , 10 ),
22+ x_breaks = (0 , 5 , 10 ),
23+ y_breaks = (0 , 5 , 10 ),
24+ x_labels = ("0" , "5" , "10" ),
25+ y_labels = ("0" , "5" , "10" ),
26+ ):
27+ scale_x = scale_x_continuous (breaks = x_breaks , labels = x_labels )
28+ scale_y = scale_y_continuous (breaks = y_breaks , labels = y_labels )
29+ scale_x .train (x )
30+ scale_y .train (y )
31+ return scale_x , scale_y
32+
33+
34+ def test_coord_polar_setup_panel_params_theta_x ():
35+ scale_x , scale_y = trained_scales (
36+ y_breaks = (0 , 2 , 5 , 10 ),
37+ y_labels = ("0" , "2" , "5" , "10" ),
38+ )
39+ coord = coord_polar (theta = "x" , start = np .pi / 4 , expand = False )
40+
41+ panel_params = coord .setup_panel_params (scale_x , scale_y )
42+
43+ assert coord .params ["theta_range" ] == (0 , 10 )
44+ assert coord .params ["r_range" ] == (0 , 10 )
45+ assert panel_params .x .range == (np .pi / 4 , np .pi / 4 + 2 * np .pi )
46+ assert panel_params .x .breaks == []
47+ assert panel_params .x .labels == []
48+ assert panel_params .y .breaks == [0 , 2 , 5 , 10 ]
49+
50+
51+ def test_coord_polar_setup_panel_params_theta_y ():
52+ scale_x , scale_y = trained_scales (
53+ x_breaks = (0 , 2 , 5 , 10 ),
54+ x_labels = ("0" , "2" , "5" , "10" ),
55+ )
56+ coord = coord_polar (theta = "y" , expand = False )
57+
58+ panel_params = coord .setup_panel_params (scale_x , scale_y )
59+
60+ assert coord .params ["theta_range" ] == (0 , 10 )
61+ assert coord .params ["r_range" ] == (0 , 10 )
62+ assert panel_params .y .breaks == [0 , 2 , 5 , 10 ]
63+
64+
65+ def test_coord_polar_to_radians_zero_width_range ():
66+ coord = coord_polar ()
67+ coord .params = {"theta_range" : (1 , 1 )}
68+
69+ assert_allclose (coord ._to_radians (np .array ([1 , 2 , 3 ])), [0 , 0 , 0 ])
570
671
772def test_coord_polar_transforms_segment_endpoints_theta_x ():
@@ -28,3 +93,195 @@ def test_coord_polar_transforms_segment_endpoints_theta_y():
2893 assert out .loc [0 , "y" ] == 1
2994 assert np .isclose (out .loc [0 , "xend" ], 2 * np .pi )
3095 assert out .loc [0 , "yend" ] == 2
96+
97+
98+ def test_coord_polar_transforms_theta_y_without_endpoints ():
99+ coord = coord_polar (theta = "y" )
100+ coord .params = {"theta_range" : (0 , 10 ), "r_range" : (0 , 10 )}
101+ data = pd .DataFrame ({"x" : [1 ], "y" : [5 ]})
102+
103+ out = coord .transform (data , None )
104+
105+ assert_allclose (out .loc [0 , "x" ], np .pi )
106+ assert out .loc [0 , "y" ] == 1
107+
108+
109+ def test_coord_polar_munches_before_radian_transform ():
110+ coord = coord_polar ()
111+ coord .params = {"theta_range" : (0 , 10 ), "r_range" : (0 , 10 )}
112+ data = pd .DataFrame ({"x" : [0 , 10 ], "y" : [1 , 2 ], "group" : [1 , 1 ]})
113+
114+ out = coord .transform (data , None , munch = True )
115+
116+ assert len (out ) > len (data )
117+ assert out ["x" ].between (0 , 2 * np .pi ).all ()
118+
119+
120+ def test_coord_polar_leaves_non_position_data_unchanged ():
121+ coord = coord_polar ()
122+ data = pd .DataFrame ({"label" : ["A" ]})
123+
124+ assert coord .transform (data , None ) is data
125+
126+
127+ def test_coord_polar_distance_and_backtransform_theta_x ():
128+ coord = coord_polar ()
129+ coord .params = {"theta_range" : (0 , 10 ), "r_range" : (0 , 20 )}
130+
131+ distance = coord .distance (pd .Series ([0 , 10 ]), pd .Series ([0 , 10 ]), None )
132+
133+ assert_allclose (distance , [np .sqrt (1.25 )])
134+ assert coord .backtransform_range (None ).x == (0 , 10 )
135+ assert coord .backtransform_range (None ).y == (0 , 20 )
136+
137+
138+ def test_coord_polar_distance_and_backtransform_theta_y ():
139+ coord = coord_polar (theta = "y" )
140+ coord .params = {"theta_range" : (0 , 10 ), "r_range" : (0 , 20 )}
141+
142+ distance = coord .distance (pd .Series ([0 , 10 ]), pd .Series ([0 , 10 ]), None )
143+
144+ assert_allclose (distance , [np .sqrt (1.25 )])
145+ assert coord .backtransform_range (None ).x == (0 , 20 )
146+ assert coord .backtransform_range (None ).y == (0 , 10 )
147+
148+
149+ def test_coord_polar_draw_sets_polar_axis ():
150+ coord = coord_polar (direction = - 1 )
151+ coord .params = {"r_range" : (2 , 8 )}
152+ fig , ax = plt .subplots (subplot_kw = {"projection" : "polar" })
153+
154+ try :
155+ coord .draw ([ax ])
156+ assert ax .get_theta_direction () == 1
157+ assert_allclose (ax .get_ylim (), (2 , 8 ))
158+ finally :
159+ plt .close (fig )
160+
161+
162+ def test_coord_polar_aspect_is_square ():
163+ assert coord_polar ().aspect (None ) == 1
164+
165+
166+ def test_coord_polar_draw_uses_polar_axes_and_hides_blank_border ():
167+ data = pd .DataFrame ({"x" : ["a" , "b" ], "y" : [1 , 2 ]})
168+ p = (
169+ ggplot (data , aes ("x" , "y" ))
170+ + geom_col ()
171+ + coord_polar ()
172+ + theme (panel_border = element_blank (), axis_line = element_line ())
173+ )
174+
175+ fig = p .draw ()
176+
177+ try :
178+ ax = fig .axes [0 ]
179+ assert ax .name == "polar"
180+ assert not ax .spines ["polar" ].get_visible ()
181+ finally :
182+ plt .close (fig )
183+
184+
185+ def test_coord_radial_arc_uses_end_or_direction ():
186+ assert coord_radial (start = 1 , end = 4 )._arc == 3
187+ assert coord_radial (direction = - 1 )._arc == - 2 * np .pi
188+
189+
190+ def test_coord_radial_setup_panel_params_for_partial_arc ():
191+ scale_x , scale_y = trained_scales (
192+ y_breaks = (0 , 2 , 4 , 8 , 10 ),
193+ y_labels = ("0" , "2" , "4" , "8" , "10" ),
194+ )
195+ coord = coord_radial (
196+ start = 0 ,
197+ end = np .pi ,
198+ thetalim = (0 , 10 ),
199+ rlim = (2 , 8 ),
200+ expand = False ,
201+ )
202+
203+ panel_params = coord .setup_panel_params (scale_x , scale_y )
204+
205+ assert coord .params ["theta_range" ] == (0 , 10 )
206+ assert coord .params ["r_range" ] == (2 , 8 )
207+ assert_allclose (panel_params .x .breaks , [0 , np .pi / 2 , np .pi ])
208+ assert panel_params .x .labels == ["0" , "5" , "10" ]
209+ assert panel_params .x .range == (0 , np .pi )
210+ assert panel_params .y .range == (2 , 8 )
211+ assert panel_params .y .breaks == [2 , 4 , 8 ]
212+ assert panel_params .y .labels == ["2" , "4" , "8" ]
213+
214+
215+ def test_coord_radial_setup_panel_params_theta_y_with_labels ():
216+ scale_x , scale_y = trained_scales (
217+ y_breaks = (0 , 5 , 10 ),
218+ y_labels = ("low" , "mid" , "high" ),
219+ )
220+ coord = coord_radial (theta = "y" , theta_labels = True , expand = False )
221+
222+ panel_params = coord .setup_panel_params (scale_x , scale_y )
223+
224+ assert_allclose (panel_params .x .breaks , [0 , np .pi , 2 * np .pi ])
225+ assert panel_params .x .labels == ["low" , "mid" , "high" ]
226+
227+
228+ def test_coord_radial_to_radians_zero_width_range ():
229+ coord = coord_radial ()
230+ coord .params = {"theta_range" : (1 , 1 )}
231+
232+ assert_allclose (coord ._to_radians (np .array ([1 , 2 , 3 ])), [0 , 0 , 0 ])
233+
234+
235+ def test_coord_radial_transform_rotates_angle ():
236+ coord = coord_radial (rotate_angle = True )
237+ coord .params = {"theta_range" : (0 , 10 ), "r_range" : (0 , 10 )}
238+ data = pd .DataFrame ({"x" : [0 , 5 ], "y" : [1 , 1 ], "angle" : [10 , 20 ]})
239+
240+ out = coord .transform (data , None )
241+
242+ assert_allclose (out ["x" ], [0 , np .pi ])
243+ assert_allclose (out ["angle" ], [10 , 200 ])
244+
245+
246+ def test_coord_radial_draw_sets_arc_inner_radius_and_axis_position ():
247+ coord = coord_radial (
248+ start = np .pi / 4 ,
249+ end = 3 * np .pi / 4 ,
250+ inner_radius = 0.5 ,
251+ r_axis_inside = True ,
252+ )
253+ coord .params = {"r_range" : (2 , 10 )}
254+ fig , ax = plt .subplots (subplot_kw = {"projection" : "polar" })
255+
256+ try :
257+ coord .draw ([ax ])
258+ assert_allclose (ax .get_xlim (), (np .pi / 4 , 3 * np .pi / 4 ))
259+ assert_allclose (ax .get_rorigin (), - 6 )
260+ assert ax .get_rlabel_position () == 55
261+ finally :
262+ plt .close (fig )
263+
264+
265+ def test_coord_radial_draw_float_r_axis_position ():
266+ coord = coord_radial (r_axis_inside = np .pi / 2 )
267+ coord .params = {"r_range" : (0 , 10 )}
268+ fig , ax = plt .subplots (subplot_kw = {"projection" : "polar" })
269+
270+ try :
271+ coord .draw ([ax ])
272+ assert ax .get_rlabel_position () == 90
273+ finally :
274+ plt .close (fig )
275+
276+
277+ def test_coord_radial_post_setup_ax_sets_pad_and_unclips_text ():
278+ coord = coord_radial (theta_label_pad = 17 , theta_labels = True )
279+ fig , ax = plt .subplots (subplot_kw = {"projection" : "polar" })
280+ text = ax .text (0 , 1 , "label" , clip_on = True )
281+
282+ try :
283+ coord .post_setup_ax (ax )
284+ assert ax .xaxis .get_tick_params ()["pad" ] == 17
285+ assert not text .get_clip_on ()
286+ finally :
287+ plt .close (fig )
0 commit comments