diff --git a/Figures/firing_characterization-jb.py b/Figures/firing_characterization-jb.py new file mode 100644 index 0000000..31381dc --- /dev/null +++ b/Figures/firing_characterization-jb.py @@ -0,0 +1,297 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec +import matplotlib.ticker as ticker +from matplotlib.colors import colorConverter as cc +from matplotlib.colors import to_hex +import string +# from plotstyle import plot_style +from plotstyle import scheme_style + +colorslist = ['#40A787', # cyan'# + '#F0D730', # yellow + '#C02717', # red + '#007030', # dark green + '#AAB71B', # lightgreen + '#008797', # light blue + '#F78017', # orange + '#478010', # green + '#53379B', # purple + '#2060A7', # blue + '#873770', # magenta + '#D03050' # pink + ] + +def cm2inch(*tupl): + inch = 2.54 + if isinstance(tupl[0], tuple): + return tuple(i/inch for i in tupl[0]) + else: + return tuple(i/inch for i in tupl) + + +def show_spines(ax, spines='lrtb'): + """ Show and hide spines. + + From github.com/janscience/plottools.git spines.py + + Parameters + ---------- + ax: matplotlib figure, matplotlib axis, or list of matplotlib axes + Axis on which spine and ticks visibility is manipulated. + If figure, then apply manipulations on all axes of the figure. + If list of axes, apply manipulations on each of the given axes. + spines: string + Specify which spines and ticks should be shown. + All other ones or hidden. + 'l' is the left spine, 'r' the right spine, + 't' the top one and 'b' the bottom one. + E.g. 'lb' shows the left and bottom spine, and hides the top + and and right spines, as well as their tick marks and labels. + '' shows no spines at all. + 'lrtb' shows all spines and tick marks. + + Examples + -------- + ```py + import matplotlib.pyplot as plt + import plottools.spines + + fig, (ax0, ax1, ax2) = plt.subplots(1, 3) + ax0.show_spines('lb') + ax1.show_spines('bt') + ax2.show_spines('tr') + ``` + ![show](figures/spines-show.png) + """ + # collect spine visibility: + xspines = [] + if 't' in spines: + xspines.append('top') + if 'b' in spines: + xspines.append('bottom') + yspines = [] + if 'l' in spines: + yspines.append('left') + if 'r' in spines: + yspines.append('right') + # collect axes: + if isinstance(ax, (list, tuple, np.ndarray)): + axs = ax + elif hasattr(ax, 'get_axes'): + # ax is figure: + axs = ax.get_axes() + else: + axs = [ax] + if not isinstance(axs, (list, tuple)): + axs = [axs] + for ax in axs: + # hide spines: + ax.spines['top'].set_visible('top' in xspines) + ax.spines['bottom'].set_visible('bottom' in xspines) + ax.spines['left'].set_visible('left' in yspines) + ax.spines['right'].set_visible('right' in yspines) + # ticks: + if len(xspines) == 0: + ax.xaxis.set_ticks_position('none') + ax.xaxis.label.set_visible(False) + ax.xaxis._orig_major_locator = ax.xaxis.get_major_locator() + ax.xaxis.set_major_locator(ticker.NullLocator()) + else: + if hasattr(ax.xaxis, '_orig_major_locator'): + ax.xaxis.set_major_locator(ax.xaxis._orig_major_locator) + delattr(ax.xaxis, '_orig_major_locator') + elif isinstance(ax.xaxis.get_major_locator(), ticker.NullLocator): + ax.xaxis.set_major_locator(ticker.AutoLocator()) + if len(xspines) == 1: + ax.xaxis.set_ticks_position(xspines[0]) + ax.xaxis.set_label_position(xspines[0]) + else: + ax.xaxis.set_ticks_position('both') + ax.xaxis.set_label_position('bottom') + if len(yspines) == 0: + ax.yaxis.set_ticks_position('none') + ax.yaxis.label.set_visible(False) + ax.yaxis._orig_major_locator = ax.yaxis.get_major_locator() + ax.yaxis.set_major_locator(ticker.NullLocator()) + else: + if hasattr(ax.yaxis, '_orig_major_locator'): + ax.yaxis.set_major_locator(ax.yaxis._orig_major_locator) + delattr(ax.yaxis, '_orig_major_locator') + elif isinstance(ax.yaxis.get_major_locator(), ticker.NullLocator): + ax.yaxis.set_major_locator(ticker.AutoLocator()) + if len(yspines) == 1: + ax.yaxis.set_ticks_position(yspines[0]) + ax.yaxis.set_label_position(yspines[0]) + else: + ax.yaxis.set_ticks_position('both') + ax.yaxis.set_label_position('left') + + + +def lighter(color, lightness): + """ Make a color lighter. + + From github.com/janscience/plottools.git colors.py + + ![lighter](figures/colors-lighter.png) + + Parameters + ---------- + color: dict or matplotlib color spec + A matplotlib color (hex string, name color string, rgb tuple) + or a dictionary with an 'color' or 'facecolor' key. + lightness: float + The smaller the lightness, the lighter the returned color. + A lightness of 0 returns white. + A lightness of 1 leaves the color untouched. + A lightness of 2 returns black. + + Returns + ------- + color: string or dict + The lighter color as a hexadecimal RGB string (e.g. '#rrggbb'). + If `color` is a dictionary, a copy of the dictionary is returned + with the value of 'color' or 'facecolor' set to the lighter color. + + Examples + -------- + For 40% lightness of blue do + ```py + import plottools.colors as c + colors = c.palettes['muted'] + lightblue = c.lighter(colors['blue'], 0.4) + ``` + """ + try: + c = color['color'] + cd = dict(**color) + cd['color'] = lighter(c, lightness) + return cd + except (KeyError, TypeError): + try: + c = color['facecolor'] + cd = dict(**color) + cd['facecolor'] = lighter(c, lightness) + return cd + except (KeyError, TypeError): + if lightness > 2: + lightness = 2 + if lightness > 1: + return darker(color, 2.0-lightness) + if lightness < 0: + lightness = 0 + r, g, b = cc.to_rgb(color) + rl = r + (1.0-lightness)*(1.0 - r) + gl = g + (1.0-lightness)*(1.0 - g) + bl = b + (1.0-lightness)*(1.0 - b) + return to_hex((rl, gl, bl)).upper() + + +def plot_sqrt(ax, a=1, b=0.2, c=100, d=0): + x = np.linspace(0, 1, 10000) + y = c*np.sqrt(a*(x - b)) + d + ax.plot(x, y) + ax.set_xlabel('Current [nA]') + ax.set_ylabel('Frequency [Hz]') + ax.set_xlim(0,1) + ax.set_ylim(0, ax.get_ylim()[1]) + +def plot_AUC(ax, a=1, b=0.2, c=180, d=0, width=0.2): + x = np.linspace(0, 1, 1000) + y = c*np.sqrt(a*(x - b)) + d + ax.plot(x, y, colorslist[2]) + ax.set_xlabel('Current [nA]') + ax.set_ylabel('Frequency [Hz]') + ax.fill_between(x, y, where=(x<=b+width), color=lighter(colorslist[2], 0.3)) + ax.text(0.3, 15, 'AUC', ha='center') + ax.annotate('', (0.2, 10), (0, 10), arrowprops=dict(arrowstyle="<->")) + ax.text(0.1, 20, 'rheobase', ha='center') + ax.set_xlim(0, 0.5) + ax.set_ylim(0, 100) + ax.yaxis.set_major_locator(ticker.MultipleLocator(50)) + +def plot_diff_sqrt(ax, a=1, b=0.2, c=100, d=0, a2=1, b2=0.2, c2=100, d2=0): + show_spines(ax, 'lb') + x = np.linspace(0, 1, 10000) + y = c*np.sqrt(a*(x - b)) + d + y2 = c2 * np.sqrt(a2 * (x - b2)) + d2 + ax.plot(x, y, colorslist[9]) + ax.plot(x, y2, colorslist[2]) + ax.set_xlim(0,1) + ax.set_ylim(0, ax.get_ylim()[1]) + ax.set_xticks([]) + ax.set_yticks([]) + +def plot_quadrant(ax): + ax.spines['left'].set_position('zero') + ax.spines['bottom'].set_position('zero') + ax.text(1.15, -0.15, '$\\Delta$ rheobase', ha='right') + ax.text(-0.05, 0.9, '$\\Delta$ AUC', ha='right') + ax.tick_params(length=0) + ax.set_xlim(-1, 1) + ax.set_ylim(-1, 1) + ax.annotate('', (1, 0), (-1, 0), arrowprops=dict(arrowstyle="->")) + ax.annotate('', (0, 1), (0, -1), arrowprops=dict(arrowstyle="->")) + ax.set_xticks([-0.4, 0.4]) + a = ax.get_xticks().tolist() + a[0] = '\u2212' + a[1] = '+' + ax.set_xticklabels(a) + ax.set_yticks([-0.4, 0.4]) + b = ax.get_xticks().tolist() + b[0] = '\u2212' + b[1] = '+' + ax.set_yticklabels(b) + + # ax.tick_params(labelsize=16) + # ax.set_yticks([0]) + # ax.axis('off') + + +scheme_style() + +fig = plt.figure(figsize=cm2inch(7, 12)) +gs = gridspec.GridSpec(3,2, top=0.95, bottom=0.1, left=0.15, right = 0.95, hspace=0.8, wspace=0.6) +ax1 = fig.add_subplot(gs[0,:]) +show_spines(ax1, 'lb') +plot_AUC(ax1, width=0.2) +ax3 = fig.add_subplot(gs[1:, :]) + +# add panel letter labels +ax_list = fig.axes +i =0 +for a in ax_list: + a.text(-0.25, 1.08, string.ascii_uppercase[i], transform=a.transAxes,size=16, weight='bold') + i += 1 + +show_spines(ax3, '') +ax3.set_ylabel('$\Delta$ AUC') +ax3.set_xlabel('$\Delta$ rheobase') +plot_quadrant(ax3) # plot delineation into quadrants + + +inset_ylim = (0, 100) +# top left +lfsize = 8 +ax3.text(x=-0.9, y=0.7, s='$\\uparrow$ AUC\n$\\downarrow$ rheobase', fontsize=lfsize) +ax3_TL = ax3.inset_axes([0.07, 0.6, 0.3, 0.2]) +plot_diff_sqrt(ax3_TL, b2=0.1, c2=200) +ax3_TL.set_ylim(inset_ylim) +# top right +ax3.text(x=0.22, y=0.7, s='$\\uparrow$ AUC\n$\\uparrow$ rheobase', fontsize=lfsize) +ax3_TR = ax3.inset_axes([0.63, 0.6, 0.3, 0.2]) +plot_diff_sqrt(ax3_TR, b2=0.4, c2=200) +ax3_TR.set_ylim(inset_ylim) +# bottom left +ax3.text(x=-0.9, y=-0.95, s='$\\downarrow$ AUC\n$\downarrow$ rheobase', fontsize=lfsize) +ax3_BL = ax3.inset_axes([0.07, 0.15, 0.3, 0.2]) +plot_diff_sqrt(ax3_BL, b2=0.1, c2=75) +ax3_BL.set_ylim(inset_ylim) +# bottom right +ax3.text(x=0.22, y=-0.95, s='$\\downarrow$ AUC\n$\\uparrow$ rheobase', fontsize=lfsize) +ax3_BR = ax3.inset_axes([0.63, 0.15, 0.3, 0.2]) +plot_diff_sqrt(ax3_BR, b2=0.4, c2=75) +ax3_BR.set_ylim(inset_ylim) +fig.set_size_inches(cm2inch(8.17,12)) +fig.savefig('./Figures/firing_characterization.pdf', dpi=fig.dpi) #bbox_inches='tight', dpi=fig.dpi diff --git a/Figures/firing_characterization.pdf b/Figures/firing_characterization.pdf index 8ce0591..591b1bd 100644 Binary files a/Figures/firing_characterization.pdf and b/Figures/firing_characterization.pdf differ diff --git a/Figures/firing_characterization.py b/Figures/firing_characterization.py index 521c9fd..f6d4617 100644 --- a/Figures/firing_characterization.py +++ b/Figures/firing_characterization.py @@ -29,7 +29,8 @@ def plot_AUC(ax, a=1, b=0.2, c=100, d=0, width=0.2): ax.set_ylabel('Frequency [Hz]') ax.fill_between(x,y, where=(x<=b+width), color='c') ax.set_xlim(0,1) - ax.set_ylim(0, ax.get_ylim()[1]) + ax.set_ylim(0, 100) + ax.yaxis.set_major_locator(ticker.MultipleLocator(50)) def plot_diff_sqrt(ax, a=1, b=0.2, c=100, d=0, a2=1, b2=0.2, c2=100, d2=0): x = np.linspace(0, 1, 10000) diff --git a/manuscript.tex b/manuscript.tex index 7b2827d..99bda5c 100644 --- a/manuscript.tex +++ b/manuscript.tex @@ -276,7 +276,6 @@ To examine the role of cell-type specific ionic current environments on the impa \begin{figure}[tp] \centering \includegraphics[width=0.5\linewidth]{Figures/firing_characterization.pdf} - \\\notejb{Ok, thanks. plotstyle.py is missing in the repository. Can you please add it?}\notenk{Sorry it should now be in \path{./Figures},} \linespread{1.}\selectfont \caption[]{Characterization of firing with AUC and rheobase. (A) The area under the curve (AUC) of the repetitive firing frequency-current (fI) curve. (B) Changes in firing as characterized by \(\Delta\)AUC and \(\Delta\)rheobase occupy 4 quadrants separated by no changes in AUC and rheobase. Representative schematic fI curves in blue with respect to a reference fI curve (black) depict the general changes associated with each quadrant.}