Skip to content

Commit f966727

Browse files
committed
merge
2 parents 7879c70 + 81dd0b1 commit f966727

File tree

9 files changed

+270
-25
lines changed

9 files changed

+270
-25
lines changed

ggplot/geoms/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .geom_now_its_art import geom_now_its_art
1717
from .geom_path import geom_path
1818
from .geom_point import geom_point
19+
from .geom_pointrange import geom_pointrange
1920
from .geom_rect import geom_rect
2021
from .geom_smooth import geom_smooth
2122
from .geom_step import geom_step
@@ -31,8 +32,8 @@
3132
__facet__ = ['facet_grid', 'facet_wrap']
3233
__geoms__ = ['geom_abline', 'geom_area', 'geom_bar', 'geom_boxplot', 'geom_density',
3334
'geom_dotplot', 'geom_blank', 'geom_linerange', 'geom_pointrange',
34-
'geom_histogram', 'geom_hline', 'geom_jitter', 'geom_line',
35-
'geom_now_its_art', 'geom_path', 'geom_point', 'geom_rect',
35+
'geom_histogram', 'geom_hline', 'geom_jitter', 'geom_line', 'geom_linerange',
36+
'geom_now_its_art', 'geom_path', 'geom_point', 'geom_pointrange', 'geom_rect',
3637
'geom_step', 'geom_smooth', 'geom_text', 'geom_tile',
3738
'geom_vline']
3839
__components__ = ['ylab', 'xlab', 'ylim', 'xlim', 'labs', 'ggtitle']

ggplot/geoms/geom_linerange.py

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,60 @@
11
from __future__ import (absolute_import, division, print_function,
22
unicode_literals)
33
import sys
4+
45
from .geom import geom
6+
from ggplot.utils import is_categorical
57
import numpy as np
68

79

810
class geom_linerange(geom):
9-
DEFAULT_AES = {'color': 'black', 'alpha': None, 'linetype': 'solid', 'size': 1.0}
11+
"""Plot intervals represented by vertical lines
12+
13+
Parameters
14+
---------
15+
16+
x
17+
x values of data
18+
ymin
19+
lower end of the interval for each x
20+
ymax
21+
upper end of the interval for each x
22+
alpha : float
23+
alpha value, defaults to 1
24+
color : string
25+
line color, defaults to 'black'
26+
linetype : string
27+
line type, defaults to 'solid'
28+
size : string
29+
width of the line, defaults to 2
30+
31+
Examples
32+
--------
33+
34+
.. plot::
35+
:include-source:
36+
37+
import numpy as np
38+
import pandas as pd
39+
from ggplot import *
40+
41+
np.random.seed(42)
42+
x = np.linspace(0.5, 9.5, num=10)
43+
y = np.random.randn(10)
44+
ymin = y - np.random.uniform(0,1, size=10)
45+
ymax = y + np.random.uniform(0,1, size=10)
46+
47+
data = pd.DataFrame({'x': x, 'ymin': ymin, 'ymax': ymax})
48+
49+
ggplot(aes(x='x', ymin='ymin', ymax='ymax'), data) \
50+
+ geom_linerange()
51+
52+
"""
53+
DEFAULT_AES = {'alpha': 1, 'color': 'black',
54+
'linetype': 'solid',
55+
'size': 2}
1056
REQUIRED_AES = {'x', 'ymin', 'ymax'}
11-
DEFAULT_PARAMS = {'stat': 'identity', 'position': 'identity'}
57+
DEFAULT_PARAMS = {'stat': 'identity', 'position': 'identity', 'cmap': None}
1258

1359
_aes_renames = {'size': 'linewidth', 'linetype': 'linestyle'}
1460
_units = {'alpha', 'color', 'linestyle'}
@@ -18,30 +64,31 @@ def __init__(self, *args, **kwargs):
1864
self._warning_printed = False
1965

2066
def _plot_unit(self, pinfo, ax):
67+
# If x is categorical, calculate positions to plot
68+
categorical = is_categorical(pinfo['x'])
69+
if categorical:
70+
x = pinfo.pop('x')
71+
new_x = np.arange(len(x))
72+
ax.set_xticks(new_x)
73+
ax.set_xticklabels(x)
74+
pinfo['x'] = new_x
75+
2176
if 'linewidth' in pinfo and isinstance(pinfo['linewidth'], list):
2277
# ggplot also supports aes(size=...) but the current mathplotlib
2378
# is not. See https://github.com/matplotlib/matplotlib/issues/2658
2479
pinfo['linewidth'] = 4
2580
if not self._warning_printed:
26-
msg = "'geom_lineragne()' currenty does not support the mapping of " +\
81+
msg = "'geom_line()' currenty does not support the mapping of " +\
2782
"size ('aes(size=<var>'), using size=4 as a replacement.\n" +\
28-
"Use 'geom_linerange(size=x)' to set the size for the whole line.\n"
83+
"Use 'geom_line(size=x)' to set the size for the whole line.\n"
2984
sys.stderr.write(msg)
3085
self._warning_printed = True
3186

3287
x = pinfo.pop('x')
88+
x = np.vstack([x, x])
89+
3390
ymin = pinfo.pop('ymin')
3491
ymax = pinfo.pop('ymax')
92+
y = np.vstack([ymin, ymax])
3593

36-
_left_gap = 0
37-
_spacing_factor = 0
38-
_sep = 0
39-
40-
left = np.arange(0, len(x) + 2)
41-
i = 0
42-
for (_x, _ymin, _ymax) in zip(x, ymin, ymax):
43-
i += 1
44-
ax.plot([i, i], [_ymin, _ymax], **pinfo)
45-
46-
ax.set_xticks(left)
47-
ax.set_xticklabels([""] + x)
94+
ax.plot(x, y, **pinfo)

ggplot/geoms/geom_point.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ class geom_point(geom):
1515

1616
def _plot_unit(self, pinfo, ax):
1717

18-
_abscent = {None: pinfo['color'], False: ''}
19-
try:
20-
if pinfo['facecolor'] in _abscent:
21-
pinfo['facecolor'] = _abscent[pinfo['facecolor']]
22-
except TypeError:
23-
pass
18+
fc = pinfo['facecolor']
19+
if fc is None:
20+
# default to color
21+
pinfo['facecolor'] = pinfo['color']
22+
elif fc is False:
23+
# Matlab expects empty string instead of False
24+
pinfo['facecolor'] = ''
2425

2526
# for some reason, scatter doesn't default to the same color styles
2627
# as the axes.color_cycle

ggplot/geoms/geom_pointrange.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
from __future__ import (absolute_import, division, print_function,
2+
unicode_literals)
3+
4+
import sys
5+
6+
import matplotlib as mpl
7+
from .geom import geom
8+
from ggplot.utils import is_categorical
9+
import numpy as np
10+
11+
12+
class geom_pointrange(geom):
13+
"""Plot intervals represented by vertical lines with a point in each interval
14+
15+
Parameters
16+
---------
17+
18+
x
19+
x values of data
20+
y
21+
y value of the point for each x
22+
ymin
23+
lower end of the interval for each x
24+
ymax
25+
upper end of the interval for each x
26+
alpha : float
27+
alpha value, defaults to 1
28+
color : string
29+
line color, defaults to 'black'
30+
fill : string
31+
Fill type for the points, defaults to 'None'
32+
linetype : string
33+
line type, defaults to 'solid'
34+
shape : string
35+
shape of the points, defaults to 'o' (i.e. circles)
36+
size : string
37+
width of the line and size of the point, defaults to 2
38+
39+
Examples
40+
--------
41+
42+
.. plot::
43+
:include-source:
44+
45+
import numpy as np
46+
import pandas as pd
47+
from ggplot import *
48+
49+
np.random.seed(42)
50+
x = np.linspace(0.5, 9.5, num=10)
51+
y = np.random.randn(10)
52+
ymin = y - np.random.uniform(0,1, size=10)
53+
ymax = y + np.random.uniform(0,1, size=10)
54+
55+
data = pd.DataFrame({'x': x, 'y': y, 'ymin': ymin, 'ymax': ymax})
56+
57+
ggplot(aes(x='x', y='y', ymin='ymin', ymax='ymax'), data) \
58+
+ geom_pointrange()
59+
60+
"""
61+
62+
DEFAULT_AES = {'alpha': 1, 'color': 'black', 'fill': None,
63+
'linetype': 'solid',
64+
'shape': 'o',
65+
'size': 2}
66+
REQUIRED_AES = {'x', 'y', 'ymin', 'ymax'}
67+
DEFAULT_PARAMS = {'stat': 'identity', 'position': 'identity', 'cmap': None}
68+
69+
_aes_renames = {'size': 'linewidth', 'linetype': 'linestyle', 'shape': 'marker', 'fill': 'facecolor'}
70+
_units = {'alpha', 'color', 'linestyle', 'marker'}
71+
72+
def __init__(self, *args, **kwargs):
73+
super(geom_pointrange, self).__init__(*args, **kwargs)
74+
self._warning_printed = False
75+
76+
def _plot_unit(self, pinfo, ax):
77+
# If x is categorical, calculate positions to plot
78+
categorical = is_categorical(pinfo['x'])
79+
if categorical:
80+
x = pinfo.pop('x')
81+
new_x = np.arange(len(x))
82+
ax.set_xticks(new_x)
83+
ax.set_xticklabels(x)
84+
pinfo['x'] = new_x
85+
86+
if 'linewidth' in pinfo and isinstance(pinfo['linewidth'], list):
87+
# ggplot also supports aes(size=...) but the current mathplotlib
88+
# is not. See https://github.com/matplotlib/matplotlib/issues/2658
89+
pinfo['linewidth'] = 4
90+
if not self._warning_printed:
91+
msg = "'geom_line()' currenty does not support the mapping of " +\
92+
"size ('aes(size=<var>'), using size=4 as a replacement.\n" +\
93+
"Use 'geom_line(size=x)' to set the size for the whole line.\n"
94+
sys.stderr.write(msg)
95+
self._warning_printed = True
96+
97+
# Plotting the line
98+
pinfoline = dict(pinfo)
99+
del pinfoline['marker']
100+
del pinfoline['facecolor']
101+
del pinfoline['y']
102+
103+
x = pinfoline.pop('x')
104+
x = np.vstack([x, x])
105+
106+
ymin = pinfoline.pop('ymin')
107+
ymax = pinfoline.pop('ymax')
108+
y = np.vstack([ymin, ymax])
109+
110+
ax.plot(x, y, **pinfoline)
111+
112+
# Plotting the points
113+
pinfopoint = dict(pinfo)
114+
del pinfopoint['ymin']
115+
del pinfopoint['ymax']
116+
del pinfopoint['linestyle']
117+
118+
fc = pinfopoint['facecolor']
119+
if fc is None:
120+
# default to color
121+
pinfopoint['facecolor'] = pinfopoint['color']
122+
elif fc is False:
123+
# Matlab expects empty string instead of False
124+
pinfopoint['facecolor'] = ''
125+
126+
# for some reason, scatter doesn't default to the same color styles
127+
# as the axes.color_cycle
128+
if "color" not in pinfopoint and self.params['cmap'] is None:
129+
pinfopoint["color"] = mpl.rcParams.get("axes.color_cycle", ["#333333"])[0]
130+
131+
pinfopoint['s'] = pinfopoint.pop('linewidth')**2*4
132+
133+
ax.scatter(**pinfopoint)

ggplot/tests/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def teardown_package():
3535
'ggplot.tests.test_geom_bar',
3636
'ggplot.tests.test_qplot',
3737
'ggplot.tests.test_geom_lines',
38+
'ggplot.tests.test_geom_linerange',
39+
'ggplot.tests.test_geom_pointrange',
3840
'ggplot.tests.test_faceting',
3941
'ggplot.tests.test_stat_function',
4042
'ggplot.tests.test_scale_facet_wrap',
@@ -102,11 +104,14 @@ def make_test_fn(fname, purpose):
102104
base, ext = os.path.splitext(fname)
103105
return '%s-%s%s' % (base, purpose, ext)
104106
expected_fname = make_test_fn(actual_fname, 'expected')
107+
# Save the figure before testing whether the original image
108+
# actually exists. This make creating new tests much easier,
109+
# as the result image can afterwards just be copied.
110+
fig.savefig(actual_fname)
105111
if os.path.exists(orig_expected_fname):
106112
shutil.copyfile(orig_expected_fname, expected_fname)
107113
else:
108114
raise Exception("Baseline image %s is missing" % orig_expected_fname)
109-
fig.savefig(actual_fname)
110115
err = compare_images(expected_fname, actual_fname,
111116
tol, in_decorator=True)
112117
if err:
Loading
Loading

ggplot/tests/test_geom_linerange.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import (absolute_import, division, print_function,
2+
unicode_literals)
3+
4+
from . import get_assert_same_ggplot, cleanup
5+
assert_same_ggplot = get_assert_same_ggplot(__file__)
6+
7+
from ggplot import *
8+
9+
import numpy as np
10+
import pandas as pd
11+
12+
13+
def _build_testing_df():
14+
rst = np.random.RandomState(42)
15+
x = np.linspace(0.5, 9.5, num=10)
16+
y = rst.randn(10)
17+
ymin = y - rst.uniform(0, 1, size=10)
18+
ymax = y + rst.uniform(0, 1, size=10)
19+
20+
df = pd.DataFrame({'x': x, 'y': y, 'ymin': ymin, 'ymax': ymax})
21+
return df
22+
23+
24+
@cleanup
25+
def test_geom_linerange():
26+
df = _build_testing_df()
27+
gg = ggplot(aes(x="x", y="y", ymin="ymin", ymax="ymax"), data=df)
28+
gg = gg + geom_linerange()
29+
assert_same_ggplot(gg, "geom_linerange")

ggplot/tests/test_geom_pointrange.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import (absolute_import, division, print_function,
2+
unicode_literals)
3+
4+
from . import get_assert_same_ggplot, cleanup
5+
assert_same_ggplot = get_assert_same_ggplot(__file__)
6+
7+
from ggplot import *
8+
9+
import numpy as np
10+
import pandas as pd
11+
12+
13+
def _build_testing_df():
14+
rst = np.random.RandomState(42)
15+
x = np.linspace(0.5, 9.5, num=10)
16+
y = rst.randn(10)
17+
ymin = y - rst.uniform(0, 1, size=10)
18+
ymax = y + rst.uniform(0, 1, size=10)
19+
20+
df = pd.DataFrame({'x': x, 'y': y, 'ymin': ymin, 'ymax': ymax})
21+
return df
22+
23+
24+
@cleanup
25+
def test_geom_pointrange():
26+
df = _build_testing_df()
27+
gg = ggplot(aes(x="x", y="y", ymin="ymin", ymax="ymax"), data=df)
28+
gg = gg + geom_pointrange()
29+
assert_same_ggplot(gg, "geom_pointrange")

0 commit comments

Comments
 (0)