17
17
import matplotlib.colors as colors
18
18
from matplotlib.pyplot import boxplot
19
19
from numpy import array
20
from qiime.pycogent_backports.distribution_plots import _validate_input,\
21
_get_distribution_markers, _validate_x_values, _create_plot,\
22
_calc_data_point_locations, _set_axes_options, generate_box_plots,\
23
generate_comparative_plots, _calc_data_point_ticks, _plot_bar_data,\
24
_plot_scatter_data, _plot_box_data, _color_box_plot,\
25
_create_standard_legend, _create_box_plot_legend
20
from qiime.pycogent_backports.distribution_plots import (_validate_input,
21
_get_distribution_markers, _validate_x_values, _create_plot,
22
_calc_data_point_locations, _set_axes_options, generate_box_plots,
23
generate_comparative_plots, _calc_data_point_ticks, _plot_bar_data,
24
_plot_scatter_data, _plot_box_data, _color_box_plot,
25
_is_single_matplotlib_color, _create_legend, _set_figure_size)
26
26
from cogent.util.unit_test import TestCase, main
28
28
class DistributionPlotsTests(TestCase):
255
255
self.assertEqual(len(result['caps']), 2)
257
257
def test_plot_box_data_empty(self):
258
"""_plot_box_data() should not error when given empty list of data,
259
but should not plot anything."""
258
"""Should ignore empty distribution."""
260
259
fig, ax = _create_plot()
261
260
result = _plot_box_data(ax, [], 'blue', 0.33, 55, 1.5, 'stdv')
262
self.assertEqual(result.__class__.__name__, "dict")
263
self.assertEqual(len(result['boxes']), 0)
264
self.assertEqual(len(result['medians']), 0)
265
self.assertEqual(len(result['whiskers']), 0)
266
self.assertEqual(len(result['fliers']), 0)
267
self.assertEqual(len(result['caps']), 0)
261
self.assertTrue(result is None)
269
def test_calc_data_point_locations_invalid_widths(self):
270
"""_calc_data_point_locations() should raise a ValueError
271
exception when it encounters bad widths."""
272
self.assertRaises(ValueError, _calc_data_point_locations, [1, 2, 3],
274
self.assertRaises(ValueError, _calc_data_point_locations, [1, 2, 3],
263
def test_calc_data_point_locations_invalid_x_values(self):
264
"""Should raise error when invalid x_values are encountered."""
265
self.assertRaises(ValueError, _calc_data_point_locations, 3, [1, 10.5])
277
267
def test_calc_data_point_locations_default_spacing(self):
278
"""_calc_data_point_locations() should return an array containing
279
the x-axis locations for each data point, evenly spaced from 1..n."""
280
locs = _calc_data_point_locations(None, 4, 2, 0.25, 0.5)
281
self.assertEqual(locs, array([1.0, 2.0, 3.0, 4.0]))
268
"""Should return evenly-spaced x-axis locations."""
269
locs = _calc_data_point_locations(4)
270
self.assertEqual(locs, array([1, 2, 3, 4]))
283
272
def test_calc_data_point_locations_custom_spacing(self):
284
"""_calc_data_point_locations() should return an array containing
285
the x-axis locations for each data point, spaced according to a custom
287
locs = _calc_data_point_locations([3, 4, 10, 12], 4, 2, 0.25, 0.75)
288
self.assertEqual(locs, array([3.75, 5.0, 12.5, 15.0]))
273
"""Should return non-evenly-spaced x-axis locations."""
274
# Scaling down from 3..12 to 1..4.
275
locs = _calc_data_point_locations(4, [3, 4, 10, 12])
276
self.assertFloatEqual(locs, array([1, 1.33333333, 3.33333333, 4]))
278
# Sorted order shouldn't affect scaling.
279
locs = _calc_data_point_locations(4, [4, 3, 12, 10])
280
self.assertFloatEqual(locs, array([1.33333333, 1, 4, 3.33333333]))
282
# Scaling up from 0.001..0.87 to 1..3.
283
locs = _calc_data_point_locations(3, [0.001, 0.2543, 0.87])
284
self.assertFloatEqual(locs, array([1, 1.58296893, 3]))
290
286
def test_calc_data_point_ticks(self):
291
287
"""_calc_data_point_ticks() should return an array containing the
327
323
x_tick_labels=["T0", "T1", "T2"], y_min='car',
330
def test_create_standard_legend_invalid_input(self):
331
"""_create_standard_legend() should raise an exception when given a
332
list of distribution examples that contains one or more null values, or
333
if the lengths of the input lists don't match up."""
334
fig, ax = _create_plot()
335
self.assertRaises(ValueError, _create_standard_legend, [None, []], ax,
336
['^', '<'], 2, ['dist1', 'dist2'])
337
self.assertRaises(ValueError, _create_standard_legend, [[], []], ax,
338
['^', '<', '>'], 2, ['dist1', 'dist2'])
339
self.assertRaises(ValueError, _create_standard_legend, [[], []], ax,
340
['^', '<', '>'], 3, ['dist1', 'dist2', 'dist3'])
341
self.assertRaises(ValueError, _create_standard_legend, [[], [], []],
342
ax, ['^', '<', '>'], 3, ['dist1', 'dist2'])
344
def test_create_box_plot_legend_invalid_input(self):
345
"""_create_box_plot_legend() should raise an exception when the lengths
346
of the input lists don't match up."""
347
fig, ax = _create_plot()
348
self.assertRaises(ValueError, _create_box_plot_legend, [[], []], ax,
349
['b', 'm', 'r'], 2, ['dist1', 'dist2'])
350
self.assertRaises(ValueError, _create_box_plot_legend, [[], []], ax,
351
['b', 'm', 'r'], 3, ['dist1', 'dist2', 'dist3'])
352
self.assertRaises(ValueError, _create_box_plot_legend, [[], [], []],
353
ax, ['b', 'm', 'r'], 3, ['dist1', 'dist2'])
355
def test_create_box_plot_legend_valid_input(self):
326
def test_create_legend(self):
356
327
"""_create_box_plot_legend() should create a legend on valid input."""
357
328
fig, ax = _create_plot()
358
_create_box_plot_legend([[], []], ax, ['b', 'r'], 2,
329
_create_legend(ax, ['b', 'r'], ['dist1', 'dist2'], 'colors')
330
self.assertEqual(len(ax.get_legend().get_texts()), 2)
332
fig, ax = _create_plot()
333
_create_legend(ax, ['^', '<', '>'], ['dist1', 'dist2', 'dist3'],
335
self.assertEqual(len(ax.get_legend().get_texts()), 3)
337
def test_create_legend_invalid_input(self):
338
"""Test raises error on bad input."""
339
fig, ax = _create_plot()
340
self.assertRaises(ValueError, _create_legend, ax,
341
['^', '<', '>'], ['dist1', 'dist2'], 'symbols')
342
self.assertRaises(ValueError, _create_legend, ax, ['^', '<', '>'],
343
['dist1', 'dist2', 'dist3'], 'foo')
361
345
def test_generate_box_plots(self):
362
346
"""generate_box_plots() should return a valid Figure object."""
370
354
self.assertEqual(len(ax.get_xticklabels()), 3)
371
355
self.assertFloatEqual(ax.get_xticks(), [1, 4, 10])
357
def test_generate_box_plots_empty_distributions(self):
358
"""Test functions correctly with empty distributions."""
359
fig = generate_box_plots([[1, 2, 3], [], [4, 5, 6]], [1, 4, 10],
360
["Data 1", "Data 2", "Data 3"], "Test",
361
"x-axis label", "y-axis label")
362
ax = fig.get_axes()[0]
363
self.assertEqual(ax.get_title(), "Test")
364
self.assertEqual(ax.get_xlabel(), "x-axis label")
365
self.assertEqual(ax.get_ylabel(), "y-axis label")
366
self.assertEqual(len(ax.get_xticklabels()), 3)
367
self.assertFloatEqual(ax.get_xticks(), [1, 4, 10])
369
# All distributions are empty.
370
fig = generate_box_plots([[], [], []], [1, 4, 10],
371
["Data 1", "Data 2", "Data 3"], "Test",
372
"x-axis label", "y-axis label")
373
ax = fig.get_axes()[0]
374
self.assertEqual(ax.get_title(), "Test")
375
self.assertEqual(ax.get_xlabel(), "x-axis label")
376
self.assertEqual(ax.get_ylabel(), "y-axis label")
377
self.assertEqual(len(ax.get_xticklabels()), 3)
378
self.assertFloatEqual(ax.get_xticks(), [1, 4, 10])
380
def test_generate_box_plots_box_colors(self):
381
"""Test correctly handles coloring of box plots."""
382
# Coloring works with all empty distributions.
383
fig = generate_box_plots([[], [], []],
384
box_colors=['blue', 'red', 'yellow'])
385
ax = fig.get_axes()[0]
386
self.assertEqual(len(ax.get_xticklabels()), 3)
388
fig = generate_box_plots([[], [], []], box_colors='pink')
389
ax = fig.get_axes()[0]
390
self.assertEqual(len(ax.get_xticklabels()), 3)
392
# Coloring works with some empty distributions.
393
fig = generate_box_plots([[], [1, 2, 3.5], []],
394
box_colors=['blue', 'red', 'yellow'])
395
ax = fig.get_axes()[0]
396
self.assertEqual(len(ax.get_xticklabels()), 3)
398
def test_generate_box_plots_invalid_input(self):
399
"""Test correctly throws error on invalid input."""
400
# Non-numeric entries in distribution.
401
self.assertRaises(ValueError, generate_box_plots, [[1, 'foo', 3]])
403
# Number of colors doesn't match number of distributions.
404
self.assertRaises(ValueError, generate_box_plots, [[1, 2, 3], [],
405
[4, 5, 6]], box_colors=['blue', 'red'])
408
self.assertRaises(ValueError, generate_box_plots, [[1, 2, 3]],
409
legend=('foo', 'bar', 'baz'))
373
411
def test_generate_comparative_plots_bar(self):
374
"""generate_comparative_plots() should return a valid barchart Figure
412
"""Should return a valid barchart Figure object."""
376
413
fig = generate_comparative_plots('bar', self.ValidTypicalData,
377
414
[1, 4, 10, 11], ["T0", "T1", "T2", "T3"],
378
415
["Infants", "Children", "Teens"], ['b', 'r', 'g'],
472
508
"x-axis label", "y-axis label", "Test")
474
510
def test_color_box_plot(self):
475
"""_color_box_plot() should not throw an exception when passed the
477
fig, ax = _create_plot()
478
box_plot = boxplot(self.ValidTypicalBoxData)
479
_color_box_plot(ax, box_plot, 'blue')
511
"""Should not throw an exception when passed the proper input."""
512
fig, ax = _create_plot()
513
box_plot = boxplot(self.ValidTypicalBoxData)
514
_color_box_plot(ax, box_plot, ['blue', 'w', (1, 1, 0.9)])
516
# Some colors are None.
517
fig, ax = _create_plot()
518
box_plot = boxplot(self.ValidTypicalBoxData)
519
_color_box_plot(ax, box_plot, ['blue', None, (1, 1, 0.9)])
521
# All colors are None.
522
fig, ax = _create_plot()
523
box_plot = boxplot(self.ValidTypicalBoxData)
524
_color_box_plot(ax, box_plot, [None, None, None])
526
def test_color_box_plot_invalid_input(self):
527
"""Should throw an exception on invalid input."""
529
fig, ax = _create_plot()
530
box_plot = boxplot(self.ValidTypicalBoxData)
531
self.assertRaises(ValueError, _color_box_plot, ax, box_plot,
532
['red', 'foobarbaz', 'blue'])
534
# Wrong number of colors.
535
fig, ax = _create_plot()
536
box_plot = boxplot(self.ValidTypicalBoxData)
537
self.assertRaises(ValueError, _color_box_plot, ax, box_plot,
538
['blue', (1, 1, 0.9)])
540
def test_is_single_matplotlib_color(self):
541
"""Test correct identification of single versus multiple mpl colors."""
542
self.assertTrue(_is_single_matplotlib_color('w'))
543
self.assertTrue(_is_single_matplotlib_color('white'))
544
self.assertTrue(_is_single_matplotlib_color([1, 1, 1]))
545
self.assertTrue(_is_single_matplotlib_color([1, 1, 1, 1]))
546
self.assertTrue(_is_single_matplotlib_color((1, 1, 1)))
547
self.assertTrue(_is_single_matplotlib_color((1, 1, 1, 1)))
548
self.assertTrue(_is_single_matplotlib_color((1.0, 1.0, 1.0, 1.0)))
549
self.assertTrue(_is_single_matplotlib_color((1.0, 1, 1.0)))
550
self.assertTrue(_is_single_matplotlib_color((2.0, 1, 1.0)))
552
self.assertFalse(_is_single_matplotlib_color(['w', 'r']))
553
self.assertFalse(_is_single_matplotlib_color(['w']))
554
self.assertFalse(_is_single_matplotlib_color(('w',)))
555
self.assertFalse(_is_single_matplotlib_color(((1.0, 1.0, 1),)))
556
self.assertFalse(_is_single_matplotlib_color(((1.0, 1.0, 1),
559
def test_set_figure_size(self):
560
"""Test setting a valid figure size."""
561
fig, ax = _create_plot()
562
_set_axes_options(ax, 'foo', 'x_foo', 'y_foo',
563
x_tick_labels=['foofoofoo', 'barbarbar'],
564
x_tick_labels_orientation='vertical')
565
_set_figure_size(fig, 3, 4)
566
self.assertFloatEqual(fig.get_size_inches(), (3, 4))
568
def test_set_figure_size_defaults(self):
569
"""Test setting a figure size using matplotlib defaults."""
570
fig, ax = _create_plot()
571
_set_axes_options(ax, 'foo', 'x_foo', 'y_foo',
572
x_tick_labels=['foofoofoo', 'barbarbar'],
573
x_tick_labels_orientation='vertical')
574
orig_fig_size = fig.get_size_inches()
575
_set_figure_size(fig)
576
self.assertFloatEqual(fig.get_size_inches(), orig_fig_size)
578
def test_set_figure_size_invalid(self):
579
"""Test setting a figure size using invalid dimensions."""
580
fig, ax = _create_plot()
581
_set_axes_options(ax, 'foo', 'x_foo', 'y_foo',
582
x_tick_labels=['foofoofoo', 'barbarbar'],
583
x_tick_labels_orientation='vertical')
584
orig_fig_size = fig.get_size_inches()
585
_set_figure_size(fig, -1, 0)
586
self.assertFloatEqual(fig.get_size_inches(), orig_fig_size)
588
def test_set_figure_size_long_labels(self):
589
"""Test setting a figure size that has really long labels."""
590
saved_stdout = sys.stdout
595
fig, ax = _create_plot()
596
_set_axes_options(ax, 'foo', 'x_foo', 'y_foo',
597
x_tick_labels=['foofoofooooooooooooooooooooooooo'
598
'ooooooooooooooooooooooooooooooooooooooooooooooo'
599
'ooooooooooooooooooooo', 'barbarbar'],
600
x_tick_labels_orientation='vertical')
601
_set_figure_size(fig, 3, 3)
602
self.assertFloatEqual(fig.get_size_inches(), (3, 3))
603
output = out.getvalue().strip()
604
self.assertEqual(output,
605
"Warning: could not automatically resize plot to make room for "
606
"axes labels and plot title. This can happen if the labels or "
607
"title are extremely long and the plot size is too small. Your "
608
"plot may have its labels and/or title cut-off. To fix this, "
609
"try increasing the plot's size (in inches) and try again.")
611
sys.stdout = saved_stdout
481
614
if __name__ == '__main__':