[Python] 医学科研 CCK8表格处理
转载请注明:陈熹 chenx6542@foxmail.com (简书:半为花间酒)
若公众号内转载请联系公众号:早起Python这篇文章能学到的主要内容:
pandas
数据处理matplotlib
绘图- 彩蛋:利用
pyinstaller
将py文件打包为exe原始数据:
https://pan.baidu.com/s/1ZioPna4y6mzNx73ZR6OKZA
提取码:hfpc
今天分享的案例来源于生物医学研究生不会陌生的实验——Cell Counting Kit-8
首先,OD值检测后获得的原始数据如下图:
需要完成的工作有:
1. 去除各组所有重复中的最大值和最小值
2. 所有数据均根据D0的对应分组进行标准化(可以简单理解为DO批次5个组去除两个极值后各求平均值,这5个批次的5个组各自除于D0对应组的均值)
3. 各组计算均值和标准差,获得两个表格:均值汇总表和均值-标准差汇总表
4. 绘制折线图
数据准备和结果呈现
在自己的桌面上建一个文件夹命名data
,将原始数据data.xlsx
放进去即可
运行完本程序后文件夹会出现3个文件,如图:
分别是:
(1) 均值汇总表
(2) 均值-标准差汇总表
(3) 折线图
接下来就详细了解如何通过代码实现
写代码
- 导入库并调用函数获取桌面文件夹路径,写在全局
import pandas as pd
import matplotlib.pyplot as plt
import os
import random
def GetDesktopPath():
return os.path.join(os.path.expanduser("~"), 'Desktop')
path = GetDesktopPath() + '/data/'
- 导入原始数据并去除缺失值
dat = pd.read_excel(path + 'data.xlsx',
sheet_name=0,
header=None,
index_col=0)
dat = dat.dropna(how='any', axis=0)
- 获取重复次数,分组个数和天数
本原始数据有6天、5组、5次重复,虽然也可以直接使用这三个数据,但以后的实验这三个可能会更改,为了让代码能够复用,最好不要写死
# 获取分组个数
ngroup = dat.index.value_counts().shape[0]
# 获取列数即重复次数
nrep = dat.shape[1]
# 获取天数(操作的批次数)即用总行数除以组数,用整除是为了返回int
nd = dat.shape[0] // ngroup
- 去掉极大值和极小值
这里用的解决办法是逐行升序排序,然后去掉第一个和最后一个数据,这里用apply
+lambda
处理
df = dat.apply(lambda x: sorted(x)[1:nrep - 1], axis=1)
df = df.to_frame(name='total')
for i in range(nrep - 2):
df[f'{i + 1}'] = df['total'].str[i]
df.drop(columns=['total'], inplace=True)
用匿名函数排序返回的是Series
的升序列表,须有转换回DataFrame
再拆成三列,最后去掉原来返回那一列即可。因此有了如上代码
- 在常规列中添加分组信息和批次信息,便于后续做汇总表
df['group'] = df.index
day_lst = []
for i in range(nd):
day_lst.append(f'Day{i}')
# 用列表推导式做列表内元素重复并添加新列
df['day'] = [i for i in day_lst for _ in range(ngroup)]
效果如图:
- 根据D0的各组均值对所有数据标准化
# 根据组数取出D0的所有行数,然后按行求均值,会自动忽略文本信息
mean_lst = df.iloc[0:ngroup, :].mean(axis = 1).tolist()
# 由于接下来要按行进行迭代,且索引的分组信息已经有一个新列来表述,这里重置索引方便迭代
df.reset_index(drop=True, inplace=True)
# 迭代的内容看起来复杂实际上不难
# 本质上就是将迭代行的数据和D0对应分组均值相除
for index, i in df.iterrows():
df.iloc[index, 0:nrep - 2] = i[0:nrep - 2] / mean_lst[index % ngroup]
- 标准化结束后即可获取均值和标准差
# 同样mean和std均会忽略非数值列
# 谨慎一点用df['mean'] = df.iloc[:, 0:nrep - 2].mean(axis=1)也可以
df['mean'] = df.mean(axis=1)
df['std'] = df.std(axis=1)
# 获取最后四列
results = df.iloc[:, -4:]
- 制作数据透视表并导出
# 用round保留4位有效数字
tb1 = pd.pivot_table(data=results,
index='group',
columns='day',
values='mean').round(4)
tb2 = pd.pivot_table(data=results,
index='group',
columns='day',
values=['mean', 'std']).round(4)
tb1.to_excel(path + '/result(mean).xlsx',
index=True,
header=True)
tb2.to_excel(path + '/result(mean+std).xlsx',
index=True,
header=True)
在Jupyter Notebook呈现结果如下,在Excel的呈现如本文开头所示
- 利用
matplotlib
画图
补充两个细节,如果在Jupyter Notebook希望出图需要加上如下代码
%matplotlib inline
如果有中文字符需要呈现也同样需要用代码设置
plt.rcParams['font.sans-serif'] = ['SimHei']
汇总表的索引(组名)可以用做图像的标签
而颜色和折线上标记样式所用的测量是根据所需的个数随机无放回抽样
group_lst = tb1.index.tolist()
colors = ['b', 'g', 'r', 'c', 'm', 'y']
color_lst = random.sample(colors, ngroup)
markers = ['.', ',', 'o', 'v', '^', '<', '>',
'1', '2', '3', '4', 's', 'p', '*', 'h', 'H', '+', 'x', 'D', 'd']
marker_lst = random.sample(markers, ngroup)
最后的画图代码:
# 设置画布大小
plt.figure(figsize=(8, 5))
for i in range(ngroup):
plt.plot(tb1.iloc[i, :].tolist(),
f'{color_lst[i]}{marker_lst[i]}-', lw=2)
plt.xticks(range(0, nd), day_lst, fontsize=18)
plt.ylabel('Relative Cell Amount', fontsize=18)
plt.legend(group_lst, loc='best', fontsize=12)
# 让图像的显示分布正常
plt.tight_layout()
# 保存一定要在调用展示之前
plt.savefig(path + "/折线图.png")
plt.show()
完整代码
import pandas as pd
import matplotlib.pyplot as plt
import os
import random
def GetDesktopPath():
return os.path.join(os.path.expanduser("~"), 'Desktop')
path = GetDesktopPath() + '/data/'
dat = pd.read_excel(path + 'data.xlsx',
sheet_name=0,
header=None,
index_col=0)
dat = dat.dropna(how='any', axis=0)
ngroup = dat.index.value_counts().shape[0]
nrep = dat.shape[1]
nd = dat.shape[0] // ngroup
df = dat.apply(lambda x: sorted(x)[1:nrep - 1], axis=1)
df = df.to_frame(name='total')
for i in range(nrep - 2):
df[f'{i + 1}'] = df['total'].str[i]
df.drop(columns=['total'], inplace=True)
df['group'] = df.index
day_lst = []
for i in range(nd):
day_lst.append(f'Day{i}')
df['day'] = [i for i in day_lst for _ in range(ngroup)]
mean_lst = df.iloc[0:ngroup, :].mean(axis=1).tolist()
df.reset_index(drop=True, inplace=True)
for index, i in df.iterrows():
df.iloc[index, 0:nrep - 2] = i[0:nrep - 2] / mean_lst[index % ngroup]
df['mean'] = df.mean(axis=1)
df['std'] = df.iloc[:, 0:nrep - 2].std(axis=1)
results = df.iloc[:, -4:]
tb1 = pd.pivot_table(data=results,
index='group',
columns='day',
values='mean').round(4)
tb2 = pd.pivot_table(data=results,
index='group',
columns='day',
values=['mean', 'std']).round(4)
tb1.to_excel(path + '/result(mean).xlsx',
index=True,
header=True)
tb2.to_excel(path + '/result(mean+std).xlsx',
index=True,
header=True)
# %matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']
group_lst = tb1.index.tolist()
colors = ['b', 'g', 'r', 'c', 'm', 'y']
color_lst = random.sample(colors, ngroup)
markers = ['.', ',', 'o', 'v', '^', '<', '>',
'1', '2', '3', '4', 's', 'p', '*', 'h', 'H', '+', 'x', 'D', 'd']
marker_lst = random.sample(markers, ngroup)
plt.figure(figsize=(8, 5))
for i in range(ngroup):
plt.plot(tb1.iloc[i, :].tolist(),
f'{color_lst[i]}{marker_lst[i]}-', lw=2)
plt.xticks(range(0, nd), day_lst, fontsize=18)
plt.ylabel('Relative Cell Amount', fontsize=18)
plt.legend(group_lst, loc='best', fontsize=12)
plt.tight_layout()
plt.savefig(path + "/折线图.png")
plt.show()
彩蛋:打包成exe文件
首先在命令行输入:
pip install pyinstaller
将上述代码保存成py文件,这里我保存为cck8.py
,然后放在桌面上data
文件夹内
然后打开命令行,cd
进入该文件夹,然后调用第二行命令可以编译成exe
cd C:\Users\chenx\Desktop\data
pyinstaller --onefile --clean cck8.py
第二行的命令可以自定义如添加图标等等,这里不做介绍