# 製圖 2.0 ###### tags: `求生指引` ## Seaborn 常用繪圖 https://blog.csdn.net/qq_40195360/article/details/86605860 ## ```python import seaborn as sns import matplotlib.pyplot as plt ``` ```python sns.set_theme(style='ticks', font='Arial', font_scale=2) # style = ['white', 'dark', 'whitegrid', 'darkgrid', 'ticks'] ``` ## Subplot ```python fig, ax = plt.subplots(figsize=(10, 5)) ``` ```python fig, axes = plt.subplots(2, 2, figsize=(5, 5)) # axes[i, j] ``` * ### 修改底色 ```python fig.patch.set_facecolor('black') ax.set_facecolor('black') ``` * ### 設定不同比例的子圖 ```python import matplotlib.gridspec as gridspec fig = plt.figure(figsize=(25, 10)) gs = gridspec.GridSpec(nrows=1, ncols=4, wspace=0.5, hspace=0.5) ax1 = fig.add_subplot(gs[0, :3]) ax2 = fig.add_subplot(gs[0, 3:]) ``` 其實也可以用樓下的方法,只是要自己對齊位置跟高度,好處是更方便調整大小 (可參考 template) * ### 新增子圖 ```python fig, ax1 = plt.subplots(figsize=(15, 5)) ax2 = fig.add_axes([0.7, 0.15, 0.16, 0.36]) # [left, bottom, width, height] ``` ## Spine ```python for axis in ['top', 'bottom', 'left', 'right']: ax.spines[axis].set_linewidth(2) ax.spines[axis].set_color('black') sns.despine(ax=ax, top=True, right=True, left=False, bottom=False, offset=None) # sns.despine(ax=ax, offset=15) # 兩軸分開 ``` ## Ticks ```python ax.tick_params() ``` | Keyword | Arg | |:--------- |:------------------------ | | axis | `['x', 'y', 'both']` | | direction | `['in', 'out', 'inout']` | | length | | | width | | | pad | | | labelsize | | * ### 顯示 or 隱藏 ```python # sns.set_theme(style='white') ax.yaxis.set_ticks_position('left') ax.xaxis.set_ticks_position('bottom') ``` ```python # sns.set_theme(style='ticks') ax.tick_params(axis='both', width=0) ``` 直接改 style 就可以了 * ### 指定刻度 ```python ax.set_xticks([29, 328]) ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) ``` * ### 指定間隔 ```python import matplotlib.ticker as ticker ax.xaxis.set_major_locator(ticker.MultipleLocator(10)) ax.yaxis.set_major_locator(ticker.MultipleLocator(0.05)) ``` * ### 設定文字 ```python import matplotlib.ticker as ticker locator = ticker.FixedLocator([0, 0.5, 1.0]) ax.xaxis.set_major_locator(locator) ax.set_xticklabels(labels=['A', 'B', 'C'], fontsize=None, rotation=None) ``` 先設定 ticks 位置,再修改 labels (barplot 好像不用設定位置,直接改就可以了) * ### 文字旋轉 ```python fig.autofmt_xdate(rotation=45) ``` ```python ax.set_xticklabels(labels=labels, rotation=45, ha='right', rotation_mode='anchor') ``` 如果要單獨旋轉某張子圖,要用第二種寫法 rotation_mode 可以改對齊方式(for 期刊) https://stackoverflow.com/questions/43152502/how-can-i-rotate-xticklabels-in-so-the-spacing-between-each-xticklabel-is-equal * ### 上下限 ```python ax.set(xlim=[None, None], ylim=[None, None]) ``` ## Title & Label ```python ax.set(ylabel='Ylabel', xlabel='Xlabel', title='Title') ``` ```python fig.suptitle('Suptitle', x=None, y=None) # 大標 ax.set_title('Title', fontsize=None, pad=None) ax.set_xlabel('Xlabel', fontsize=None, labelpad=None) ax.set_ylabel('Ylabel', fontsize=None, labelpad=None) ``` 需要個別調整的要分開寫 ## Legend https://blog.csdn.net/helunqu2017/article/details/78641290 ```python sns.move_legend(ax, loc='best', title=None, frameon=False) ax.legend(loc='best', title=None, frameon=False) ax.legend(loc='lower left', bbox_to_anchor=(0, -0.3)) # legend 在下方(外) ax.legend(loc='lower left', bbox_to_anchor=(1.04, 0)) # legend 在右方(外) ``` | Keyword | Describe | |:-------------- |:------------------------ | | loc | | | title | | | fontsize | | | frameon | | | framalpha | | | ncol | | | bbox_to_anchor | `(x, y)` | | markerscale | | | handletextpad | 調整文字和圖例之間的間距 | | columnspacing | 調整各組間的橫向距離 | * ### 改圖例線條寬度 ```python legend = ax.legend(loc='best', title=None, frameon=False) for line in legend.get_lines(): line.set_linewidth(5) ``` 樓上的 markerscale 只能改圓點圖例的大小 * ### 改文字 ```python ax.legend(ax.get_legend_handles_labels()[0], ['Legend 1', 'Legend 2']) ``` 友善提示:不要分開呼叫兩次 ax.legend,會衝突! ```python handles, labels = ax.get_legend_handles_labels() for i, label in enumerate(labels): if label == 'source': labels[i] = '1-15' handles[i].set_sizes([10]) elif label == 'target': labels[i] = f'{session}' handles[i].set_sizes([50]) ax.legend(handles, labels,markerscale=10, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0) ``` * ### Remove ```python ax.get_legend().remove() ``` ```python ax.legend().set_visible(False) ``` ## Grid ```python ax.grid(axis='both') ax.xaxis.grid() ax.yaxis.grid() ``` | Keyword | Arg | |:--------- |:------- | | color | | | alpha | | | linewidth | | | linestyle | | ## Color Matplotlib-colormaps https://matplotlib.org/stable/tutorials/colors/colormaps.html#sphx-glr-tutorials-colors-colormaps-py ColorBrewer https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=11 挑選圖片上的顏色 https://www.ginifab.com.tw/tools/colors/color_picker_from_image.php * ### 參考色碼 ```python sns.palplot(sns.color_palette(colors)) ``` ```python colors = ['#dfc27d', '#bebada', '#ffed6f', '#fface0', '#fb8072', '#8dd3c7', '#adadad', '#fdab72', '#7cd989', '#66afd5'] ``` ![](https://i.imgur.com/GQuyWdW.png) ```python colors = ["#80b1d3", "#fdb462", "#b3de69", "#fb8072", "#bebada", "#adadad", "#fa9fb5"] ``` ![](https://hackmd.io/_uploads/SktfMXdt3.png) ```python colors = ['#ff40df', '#9240ff', '#40fff7', '#ffcd40', '#ff4040', '#407bff', '#34ff5f', '#a4ff40'] ``` ![](https://hackmd.io/_uploads/ryKnC8Lo2.png) ## 統計檢定 ```python from statannotations.Annotator import Annotator annot = Annotator(ax=ax, data=df, x="method", y="cc_weighted", order=['Baseline', 'NeuRA', 'QRNN', 'LFDA'], pairs=[('Baseline', 'LFDA'), ('NeuRA', 'LFDA'), ('QRNN', 'LFDA')]) annot.configure(test='Wilcoxon', text_format='star', loc='outside', verbose=True, fontsize=20, line_width=2, line_height=0, line_offset=0, line_offset_to_group=0, text_offset=0, pvalue_thresholds=[[1e-3, '***'], [1e-2, '**'], [0.05, '*'], [1, 'ns']]) annot.apply_test().annotate() ``` Seaborn ≥ v0.12 is not officially supported, we know there are at least some bugs. 友善提示:如果本來 seaborn 版本是 0.12.2,安裝此套件時會自動降成 0.11.2 ## 存檔 ```python fig.savefig('figure.png', dpi=600, bbox_inches='tight', pad_inches=0.2) ``` pad_inches 可調整白邊 # Some Tips ## 讓兩張子圖分開適當距離 ```python fig.tight_layout() ``` ## 更改 barplot 寬度 ```python def change_width(ax, new_value): for patch in ax.patches: current_width = patch.get_width() diff = current_width - new_value patch.set_width(new_value) # we change the bar width patch.set_x(patch.get_x() + diff*.5) # we recenter the bar ``` ```python change_width(ax, 0.6) ``` 註:seaborn 0.12.2 可以直接用 width 調寬度 ## 在 barplot 的 bar 上顯示數字 ``` python for container in ax.containers: ax.bar_label(container, fmt='%.2f', fontsize=10, padding=10) ``` ``` python for container in ax.containers: ax.bar_label(container, fmt='%.2f', label_type='center', fontsize=10) ``` 讓數字很漂亮的顯示在 bar 上 ~~但通常顯示數字是給自己看的,不太會用在正式的圖上面~~ ## 在 boxplot 加上平均點 ```python sns.boxplot(ax=ax, data=df, x='time', y='pulse', boxprops={'facecolor': 'none'}, width=0.7, linewidth=2, showfliers=False, showmeans=True, meanprops={'marker': 'x', 'markeredgecolor': 'k', 'markersize': 12, "markeredgewidth": 2}) ``` 可參考 template ## 畫水平虛線 ```python ax.axhline(y=0.7, linestyle='--', color='k') ``` ## 調整 ax 等比例 ```python ax.set_aspect('equal', adjustable='box') ``` ## 調整統計檢定位置、大小 line_offset:每個 pair 的間距 line_offset_to_group: 與圖的距離 text_offset: 文字和線條的間距 BUT! 有時候怎麼調都沒有反應 ~~很玄~~ 小技巧:用 fontsize 控制線條的距離,之後再另外修改文字(可參考 template) 用套件統一調整大小、位置,發現還是不滿意時,可用 if 單獨抓出來改 ```python for text in ax.texts: if text.get_text() == 'ns': text.set_fontsize(25) if text.get_text() in ['*', '**', '***']: x, y = text.get_position() text.set_position((x, y - 2)) ``` # Some Templates :::warning :warning: 以下範例皆為 seaborn 0.12.2,使用其他版本可能或許會跳錯誤 ::: ## #1 ![](https://hackmd.io/_uploads/HJ8Y0GR22.png) ```python df = sns.load_dataset('exercise') sns.set_theme(style='ticks', font_scale=2) fig, ax1 = plt.subplots(figsize=(10, 5)) pos = ax1.get_position() ax2 = fig.add_axes([0.98, pos.y0, 0.25, pos.height]) colors = ['#fdab72', '#66afd5', '#7cd989'] markers = ['o', '^', 's'] order = ['1 min', '15 min', '30 min'] sns.regplot(ax=ax1, data=df[df['time'] == '1 min'], x='id', y='pulse', order=1, color=colors[0], scatter=False, ci=None, line_kws={'linewidth': 2}) sns.regplot(ax=ax1, data=df[df['time'] == '15 min'], x='id', y='pulse', order=1, color=colors[1], scatter=False, ci=None, line_kws={'linewidth': 2}) sns.regplot(ax=ax1, data=df[df['time'] == '30 min'], x='id', y='pulse', order=1, color=colors[2], scatter=False, ci=None, line_kws={'linewidth': 2}) sns.lineplot(ax=ax1, data=df, x='id', y='pulse', hue='time', style='time', palette=colors, markers=markers, dashes=False, lw=0, ms=10) sns.boxplot(ax=ax2, data=df, x='time', y='pulse', boxprops={'facecolor': 'none'}, width=0.7, linewidth=2, showfliers=False, showmeans=True, meanprops={'marker': 'x', 'markeredgecolor': 'k', 'markersize': 10, "markeredgewidth": 2}) sns.stripplot(ax=ax2, data=df, x='time', y='pulse', hue='time', palette=colors, legend=False, jitter=0.1, zorder=0) annot = Annotator(ax=ax2, data=df, x='time', y='pulse', order=['1 min', '15 min', '30 min'], pairs=[('1 min', '30 min'), ('15 min', '30 min')]) annot.configure(test='t-test_paired', text_format='star', loc='outside', verbose=False, fontsize=30, line_width=2, line_height=0, pvalue_thresholds=[[1e-3, '***'], [1e-2, '**'], [0.05, '*'], [1, 'ns']]) annot.apply_test().annotate() for text in ax2.texts: if text.get_text() in ['ns', '*', '**', '***']: text.set_fontsize(20) ax1.set_title('', fontsize=None, pad=None) ax1.set_xlabel('Sample ID', fontsize=None, labelpad=None) ax1.set_ylabel('Pulse (npm)', fontsize=None, labelpad=None) ax1.set(xlim=[None, None], ylim=[-10, 175]) legend = ax1.legend(loc='lower left', title=None, frameon=False, markerscale=1.5) for line in legend.get_lines(): line.set_linewidth(3) ax2.set_title('', fontsize=None, pad=None) ax2.set_xlabel('', fontsize=None, labelpad=None) ax2.set_ylabel('', fontsize=None, labelpad=None) ax2.set(ylim=[-10, 175]) ax2.set_xticklabels(labels=order, rotation=45, ha='right') for axis in ['top', 'bottom', 'left', 'right']: ax1.spines[axis].set_linewidth(2) ax1.spines[axis].set_color('black') ax2.spines[axis].set_linewidth(2) ax2.spines[axis].set_color('black') sns.despine(ax=ax1, top=True, right=True) sns.despine(ax=ax2, top=True, right=True) # fig.savefig('figure.png', dpi=600, bbox_inches='tight', pad_inches=0.2) fig.show() ``` ## #2 ![](https://hackmd.io/_uploads/rkgc2M0n3.png) ```python df = sns.load_dataset('exercise') sns.set_theme(style='ticks', font_scale=2) fig, ax1 = plt.subplots(figsize=(15, 5)) ax2 = fig.add_axes([0.7, 0.15, 0.16, 0.36]) colors = ['#fdab72', '#66afd5', '#7cd989'] markers = ['o', '^', 's'] sns.lineplot(ax=ax1, data=df, x='id', y='pulse', hue='time', style='time', palette=colors, markers=markers, dashes=False, lw=2, ms=10) sns.boxplot(ax=ax2, data=df, x='time', y='pulse', palette=colors, width=0.7, linewidth=2, showmeans=True, meanprops={'marker': 'x', 'markeredgecolor': 'k', 'markersize': 10, "markeredgewidth": 2}) annot = Annotator(ax=ax2, data=df, x='time', y='pulse', order=['1 min', '15 min', '30 min'], pairs=[('1 min', '30 min'), ('15 min', '30 min')]) annot.configure(test='t-test_paired', text_format='star', loc='outside', verbose=False, fontsize=15, line_width=2, line_height=0, pvalue_thresholds=[[1e-3, '***'], [1e-2, '**'], [0.05, '*'], [1, 'ns']]) annot.apply_test().annotate() for text in ax2.texts: if text.get_text() in ['ns', '*', '**', '***']: text.set_fontsize(20) ax1.set_title('', fontsize=None, pad=None) ax1.set_xlabel('Sample ID', fontsize=None, labelpad=None) ax1.set_ylabel('Pulse (bpm)', fontsize=None, labelpad=None) ax1.set(xlim=[None, None], ylim=[-10, 155]) legend = ax1.legend(loc='lower left', title=None, frameon=False, markerscale=1.5) for line in legend.get_lines(): line.set_linewidth(3) ax2.set_title('', fontsize=None, pad=None) ax2.set_xlabel('', fontsize=None, labelpad=None) ax2.set_ylabel('Pulse', fontsize=None, labelpad=None) ax2.set(ylim=[None, 175]) ax2.set_xticks([]) for axis in ['top', 'bottom', 'left', 'right']: ax1.spines[axis].set_linewidth(2) ax1.spines[axis].set_color('black') ax2.spines[axis].set_linewidth(2) ax2.spines[axis].set_color('black') sns.despine(ax=ax1, top=True, right=True) sns.despine(ax=ax2, top=True, right=True) # fig.savefig('figure.png', dpi=600, bbox_inches='tight', pad_inches=0.2) fig.show() ``` ## #3 ![](https://hackmd.io/_uploads/rJjrsG02h.png) ```python df = sns.load_dataset('exercise') sns.set_theme(style='ticks', font_scale=2) fig, ax = plt.subplots(figsize=(5, 5)) colors = ['#fdab72', '#66afd5', '#7cd989'] sns.boxplot(ax=ax, data=df, x='time', y='pulse', boxprops={'facecolor': 'none'}, width=0.7, linewidth=2, showfliers=False, showmeans=True, meanprops={'marker': 'x', 'markeredgecolor': 'k', 'markersize': 12, "markeredgewidth": 2}) sns.stripplot(ax=ax, data=df, x='time', y='pulse', hue='time', palette=colors, legend=False, jitter=0.1, zorder=0) annot = Annotator(ax=ax, data=df, x='time', y='pulse', order=['1 min', '15 min', '30 min'], pairs=[('1 min', '30 min'), ('15 min', '30 min')]) annot.configure(test='t-test_paired', text_format='star', loc='outside', verbose=False, fontsize=20, line_width=2, line_height=0, pvalue_thresholds=[[1e-3, '***'], [1e-2, '**'], [0.05, '*'], [1, 'ns']]) annot.apply_test().annotate() ax.set_title('', fontsize=None, pad=None) ax.set_xlabel('', fontsize=None, labelpad=None) ax.set_ylabel('Pulse (bpm)', fontsize=None, labelpad=None) ax.set(ylim=[None, 165]) for axis in ['top', 'bottom', 'left', 'right']: ax.spines[axis].set_linewidth(2) ax.spines[axis].set_color('black') sns.despine(ax=ax, top=True, right=True) # fig.savefig('figure.png', dpi=600, bbox_inches='tight', pad_inches=0.2) fig.show() ``` ## #4 ![](https://hackmd.io/_uploads/B1scG4033.png) ```python df = sns.load_dataset('tips') sns.set_theme(style='ticks', font_scale=2) fig, ax = plt.subplots(figsize=(10, 5)) colors = ['#fdab72', '#7cd989'] markers = ['o', 's'] sns.pointplot(ax=ax, data=df, x='day', y='tip', hue='sex', palette=colors, markers=markers, capsize=.1, dodge=True) sns.lineplot(ax=ax, data=df, x='day', y='tip', hue='sex', style='sex', palette=colors, markers=markers, lw=0, ms=0, errorbar=None, dashes=False) handles, labels = ax.get_legend_handles_labels() legend = ax.legend(handles[2:], labels[2:], markerscale=1.5, loc='best', title=None, frameon=False) for line in legend.get_lines(): line.set_linewidth(3) ax.set_title('', fontsize=None, pad=None) ax.set_xlabel('Day of the Week', fontsize=None, labelpad=None) ax.set_ylabel('Tip Amount Given', fontsize=None, labelpad=None) for axis in ['top', 'bottom', 'left', 'right']: ax.spines[axis].set_linewidth(2) ax.spines[axis].set_color('black') sns.despine(ax=ax, top=True, right=True) # fig.savefig('figure.png', dpi=600, bbox_inches='tight', pad_inches=0.2) fig.show() ```