# 製圖 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']
```

```python
colors = ["#80b1d3", "#fdb462", "#b3de69", "#fb8072",
"#bebada", "#adadad", "#fa9fb5"]
```

```python
colors = ['#ff40df', '#9240ff', '#40fff7', '#ffcd40',
'#ff4040', '#407bff', '#34ff5f', '#a4ff40']
```

## 統計檢定
```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

```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

```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

```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

```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()
```