def test_nonsymmetric_groups(self): """test that when only receiving connections, all the group's space is occupied by the thickness""" df = pd.DataFrame( OrderedDict([(MEAN, [1, 2]), ('pre', ['a', 'b']), ('post', ['c', 'c'])])) plotter = CirclePlot() source_angles, dest_angles = plotter.__plot_components__(df)[3] exp_source = { 'a': { 'c': (0 / 3 * np.pi, 1 / 3 * np.pi) }, 'b': { 'c': (1 / 3 * np.pi, 3 / 3 * np.pi) } } exp_dest = { 'a': { 'c': (6 / 3 * np.pi, 5 / 3 * np.pi) }, 'b': { 'c': (5 / 3 * np.pi, 3 / 3 * np.pi) } } assert_nested_dicts_approx_equal(source_angles, exp_source) assert_nested_dicts_approx_equal(dest_angles, exp_dest)
def test_connection_angles_NaN(self): """nan connections are not drawn""" df = pd.DataFrame( OrderedDict([('pre: mtype', ['a', 'a', 'b', 'b']), ('post: mtype', ['a', 'b', 'a', 'b']), (MEAN, [1, 2, 1, np.nan])])) plotter = CirclePlot() source_angles, dest_angles = plotter.__plot_components__(df)[3] exp_source = { 'a': { 'a': (2 / 4 * np.pi, 3 / 4 * np.pi), 'b': (3 / 4 * np.pi, 5 / 4 * np.pi) }, 'b': { 'a': (7 / 4 * np.pi, 8 / 4 * np.pi) } } exp_dest = { 'b': { 'a': (1 / 4 * np.pi, 0) }, 'a': { 'a': (2 / 4 * np.pi, 1 / 4 * np.pi), 'b': (7 / 4 * np.pi, 5 / 4 * np.pi) } } assert_nested_dicts_approx_equal(source_angles, exp_source) assert_nested_dicts_approx_equal(dest_angles, exp_dest)
def test_overlarge_space_between(self): """If the space_between groups is too large to plot the image, raise a ValueError""" df = pd.DataFrame({ 'pre: mtype': ['a', 'a', 'c', 'g'], 'post: mtype': ['a', 'b', 'a', 'd'], MEAN: [1, 2, 1, 2] }) with pyt.raises(ValueError): CirclePlot(space_between=np.pi) with pyt.raises(ValueError): CirclePlot(space_between=np.pi / 2).__plot_components__(df)[2][0]
def test_no_MEAN(self): """ if there is no MEAN column, we cannot plot. raise a ValueError """ df = pd.DataFrame({'a': [1, 2], 'b': [2, 3]}) with pyt.raises(ValueError): CirclePlot().plot(df)
def test_exclude_tiny_angles(self): """test a parameter which removes small connections from the plot """ plotter = CirclePlot(min_conn_size=np.pi / 32) df = pd.DataFrame( OrderedDict([('pre: mtype', [ 'a', 'a', 'b', 'b', 'c', ]), ('post: mtype', ['a', 'b', 'a', 'b', 'c']), (MEAN, [10, 20, 10, 20, np.nan])])) src, dst = plotter.__plot_components__(df)[3] assert src['c'] == {} assert dst['c'] == {}
def test_group_angles_asymmetric(self): """groups that only show up in 'to' column should occur after (clockwise of) the groups occurring in 'from'""" df = pd.DataFrame( OrderedDict([(MEAN, [1, 2]), ('pre', ['a', 'b']), ('post', ['c', 'c'])])) plotter = CirclePlot() group_angles = plotter.__plot_components__(df)[2][0] print(*group_angles.keys()) exp_angles = { 'a': (0, 1 / 3 * np.pi), 'b': (1 / 3 * np.pi, 3 / 3 * np.pi), 'c': (3 / 3 * np.pi, 6 / 3 * np.pi) } for f in ['a', 'b']: assert group_angles[f] == pyt.approx(exp_angles[f])
def test_group_angles_NaN(self): """test that nan is treated as 0 in terms of group size""" df = pd.DataFrame({ 'pre: mtype': ['a', 'a', 'b', 'b'], 'post: mtype': ['a', 'b', 'a', 'b'], MEAN: [1, 2, 1, np.nan] }) plotter = CirclePlot() group_angles = plotter.__plot_components__(df)[2][0] exp_angles = { 'a': (0, 5 / 4 * np.pi), 'b': (5 / 4 * np.pi, 8 / 4 * np.pi) } for f in ['a', 'b']: assert group_angles[f] == pyt.approx(exp_angles[f])
def test_group_angles_space(self): """check that the size of groups shrinks appropriately when spaces are put between them""" df = pd.DataFrame({ 'pre: mtype': ['a', 'a', 'b', 'b'], 'post: mtype': ['a', 'b', 'a', 'b'], MEAN: [1, 2, 1, 2] }) plotter = CirclePlot(space_between=np.pi / 4) group_angles = plotter.__plot_components__(df)[2][0] unit_size = 1.5 / 12 exp_angles = { 'a': (np.pi / 8, (1 / 8 + 5 * unit_size) * np.pi), 'b': ((5 * unit_size + 3 / 8) * np.pi, (2 - 1 / 8) * np.pi) } for f in ['a', 'b']: assert group_angles[f] == pyt.approx(exp_angles[f])
def test_basic(self): df = pd.DataFrame({ MEAN: [1, 2, 1, 2], 'pre': ['a', 'a', 'b', 'b'], 'post': ['a', 'b', 'a', 'b'] }) f, a = CirclePlot().plot(df) assert isinstance(f, plt.Figure) assert isinstance(a, plt.Axes) plt.clf()
def test_multilevel(self): df = pd.DataFrame( OrderedDict([((MEAN, '', '', ''), [1, 2, 1, 2]), (('pre', 'm', 'a', 'l'), [1, 1, None, None]), (('pre', 'm', 'b', 'l'), [None, None, 2, 2]), (('post', 'm', 'a', 'l'), [1, None, 1, None]), (('post', 'm', 'a', 'l'), [None, 2, None, 2])])) f, a = CirclePlot().plot(df) assert isinstance(f, plt.Figure) assert isinstance(a, plt.Axes)
def test_nan(self): df = pd.DataFrame({ 'pre: mtype': ['a', 'a', 'b', 'b'], 'post: mtype': ['a', 'b', 'a', 'b'], MEAN: [1, 2, 1, np.nan] }) f, a = CirclePlot().plot(df) assert isinstance(f, plt.Figure) assert isinstance(a, plt.Axes) plt.clf()
def test_connection_angles(self): """basic test for two groups connections 'from' a group have their source on the clockwise side of the group. Angles 'to' a group have their destination on the counterclockwise side of a group. The thickness of a connection (the difference in the two source angles, and in the two destination angles) is proportional to the strength of the connection. """ df = pd.DataFrame( OrderedDict([('pre: mtype', ['a', 'a', 'b', 'b']), ('post: mtype', ['a', 'b', 'a', 'b']), (MEAN, [1, 2, 1, 2])])) plotter = CirclePlot() source_angles, dest_angles = plotter.__plot_components__(df)[3] exp_source = { 'a': { 'a': (2 / 6 * np.pi, 3 / 6 * np.pi), 'b': (3 / 6 * np.pi, 5 / 6 * np.pi) }, 'b': { 'a': (11 / 6 * np.pi, 12 / 6 * np.pi), 'b': (9 / 6 * np.pi, 11 / 6 * np.pi) } } exp_dest = { 'b': { 'a': (1 / 6 * np.pi, 0), 'b': (9 / 6 * np.pi, 7 / 6 * np.pi) }, 'a': { 'a': (2 / 6 * np.pi, 1 / 6 * np.pi), 'b': (7 / 6 * np.pi, 5 / 6 * np.pi) } } assert_nested_dicts_approx_equal(source_angles, exp_source) assert_nested_dicts_approx_equal(dest_angles, exp_dest)
def test_more_nondata_cols(self): """ if there are more than two columns which may contain group parameters, raise a ValueError """ df = pd.DataFrame({ MEAN: [1, 2], 'a': [1, 2], 'b': [2, 3], 'c': [4, 5] }) with pyt.raises(ValueError): CirclePlot().plot(df)
def test_group_angles(self): """ test basic with two groups, patch size should be proportional to the number of connections to and from a group groups start at top of circle, an show clockwise in order of occurrence in the 'from' column """ df = pd.DataFrame({ 'pre: mtype': ['a', 'a', 'b', 'b'], 'post: mtype': ['a', 'b', 'a', 'b'], MEAN: [1, 2, 1, 2] }) plotter = CirclePlot() group_angles = plotter.__plot_components__(df)[2][0] exp_angles = { 'a': (0, 5 / 6 * np.pi), 'b': (5 / 6 * np.pi, 12 / 6 * np.pi) } for f in ['a', 'b']: assert group_angles[f] == pyt.approx(exp_angles[f])
def test_dict_prepost(self): """test that it automatically converts to multilevel_dataframe""" df = pd.DataFrame({ MEAN: [1, 2, 1, 2], 'pre': [{ 'm': 'a', 'l': 1 }, { 'm': 'a', 'l': 1 }, { 'm': 'b', 'l': 2 }, { 'm': 'b', 'l': 2 }], 'post': [{ 'm': 'a', 'l': 1 }, { 'm': 'b', 'l': 2 }, { 'm': 'a', 'l': 1 }, { 'm': 'b', 'l': 2 }] }) f, a = CirclePlot().plot(df) assert isinstance(f, plt.Figure) assert isinstance(a, plt.Axes) plt.clf()
def test_assign_group_labels(self): """ test using a callback to create custom group labels """ df = pd.DataFrame( OrderedDict([(('a', 'b'), ['1', '2']), (('a', 'c'), ['3', '4']), (('g', 'f'), ['5', '6']), ((MEAN, ''), [3, 4])])) default = CirclePlot().__plot_components__(df)[0] assert all([ grp == exp for grp, exp in zip(default.keys(), ['1, 3', '2, 4', '5', '6']) ]) custom = CirclePlot( value_callback=lambda row: ''.join(row.values))\ .__plot_components__(df)[0] assert all([ grp == exp for grp, exp in zip(custom.keys(), ['13', '24', '5', '6']) ])
def test_default_and_custom(self): """ the default colors should use the matplotlib default cm but passing a callback should allow overriding this """ df = pd.DataFrame( OrderedDict([ (('pre', 'mtype'), ['L23_MC', 'L23_PC', 'L4_MC', 'L4_PC']), (('pre', 'sclass'), ['INH', 'EXC', 'INH', 'EXC']), (('pre', 'layer'), ['L2', 'L2', 'L4', 'L4']), (('post', 'mtype'), ['L4_MC', 'L23_MC', 'L4_PC', 'L23_PC']), (('post', 'sclass'), ['INH', 'INH', 'EXC', 'EXC']), (('post', 'layer'), ['L4', 'L2', 'L4', 'L2']), (('mean', ''), [1, 2, 3, 4]) ])) default_colors = CirclePlot().__plot_components__(df)[2][1] default_cmap = matplotlib.cm.get_cmap() assert default_colors == { 'L23_MC, INH, L2': { 'color': default_cmap(0.) }, 'L23_PC, EXC, L2': { 'color': default_cmap(1 / 3) }, "L4_MC, INH, L4": { 'color': default_cmap(2 / 3) }, "L4_PC, EXC, L4": { 'color': default_cmap(1.) } } cmap = matplotlib.cm.get_cmap('gist_rainbow') def custom_color_callback(groups): print(pd.DataFrame(list(groups.values()))) layers = list( pd.DataFrame(list(groups.values())).layer.unique()) nlayers = len(layers) max_color = nlayers - 1 return { grp: { 'color': tuple(v for v in ( cmap(layers.index(params['layer']) / max_color) * (np.array([0.5, 0.5, 0.5, 1.0]) if params['sclass'] == 'INH' else np.array([1.0, 1.0, 1.0, 1.0])))) } for grp, params in groups.items() } grps, _, custom_ptch =\ CirclePlot(color_callback=custom_color_callback)\ .__plot_components__(df)[:3] custom_colors = custom_ptch[1] assert custom_colors == { 'L23_MC, INH, L2': { 'color': tuple(v * 0.5 if i < 3 else v for i, v in enumerate(cmap(0.))) }, 'L23_PC, EXC, L2': { 'color': cmap(0.) }, "L4_MC, INH, L4": { 'color': tuple(v * 0.5 if i < 3 else v for i, v in enumerate(cmap(1.))) }, "L4_PC, EXC, L4": { 'color': cmap(1.) } }