Python 孔板(微量滴定板分析):数据结构----1

目前,几乎每个生物实验室都可以看到96孔板(也称为为微量滴定板)的存在,例如酶标仪和用于qRT-PCR的96孔板,通常这些96孔板能在较短的时间内得到大量数据,方便高效。需要注意的是96孔的格式可能是目前最常用的板,但也有很多其他布局的板,如果想让我们的代码具有较好的重复性,以便用于不同规格的板,我们就需要用到面向对象编程(OOP)。

现在让我们回顾一下96孔板的表示方法:

(1)坐标系位置1D(position1D):1,2,3,4,5,6,7,8,.......94,95,96;

(2)坐标系位置2D(position2D):(1,1), (1,2), (1,3),....... (8,10), (8,11), (8,12) ;

(3)坐标系统(wellID):'A01', 'A02', 'A03', ........'H10', 'H11', 'H12'。

代码的实现:

1.孔板类的构造

对于Plate类,我们有两个类变量rowLabels和mapPosition。

rowLabels变量定义了可用于板的行名。在这里,我们默认一块板永远不会超过26行,因此就用26个字母分别表示26行。

mapPosition中的position1D:0, position2D:1, wellID:2主要目的是后续可以利用字典的key所对应的value提取map()放回的元组中的值self.validate[key].append(m[Plate.mapPosition[key]]). 这样做的主要目的是代码具有较好的兼容性,即使我们更改了mapPosition的顺序,在提取元组时它也能精确提取,提取的顺序不会改变。

其他的一些解释详见代码中的注释部分。

import math,os,string,csv
import matplotlib as plt

class Plate():
    #孔板的行名
    rowLabels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    #mapPosition中的position1D:0, position2D:1, wellID:2主要目的是后续可以利用字典的key所对应的value提取map()放回的元组中的值,self.validate[key].append(m[Plate.mapPosition[key]])
    mapPosition = {'position1D':0, 'position2D':1, 'wellID':2}
    def __init__(self,id, rows, columns):
        self.id = id
        self.rows = rows
        self.columns = columns
        self.size = self.rows*self.columns
        #validate字典用于存储各个坐标系的映射关系,
        # 例如{‘position1D’:[1,2,3,4,....], 'position2D':[(1,1),(1,2),(1,3),...], 'wellID':['A01','A02','A03',...]}
        self.validate = {}
        #data字典用于后续数据,比如酶标板读取出来的数值
        self.data = {}
        # 利用for循环将validate中的每个key(position1D,position2D,wellID)先定义为空列表
        for key in Plate.mapPosition:
            self.validate[key] = []
        #利用for循环根据孔板大小构建三种不同坐标系的映射关系
        for n in range(1, self.size+1):
            self.data[n] = {}
            #map()函数返回的是一个元组(1,(1,1),‘A01’)分别对应三种不同坐标系的表示方法;
            #check=False是因为map()函数有双重功能,不仅提供输入坐标的坐标映射集(check=False),
            # 而且检查输入坐标对当前的Plate示例是否有效(check=True)
            m = self.map(n, check=False)
            #利用for循环根据位置提取相应的映射关系,并添加到列表里面
            for key in Plate.mapPosition:
                self.validate[key].append(m[Plate.mapPosition[key]])

2.孔板的遍历——Plate类中map()方法的构造

首先我们先看看map()方法的返回值(return (pos, (row+1,col+1), id)),返回的时一个包含三个值的元组,每个值表示一个坐标系,pos为position1D的坐标系、(row+1,col+1)为position2D的坐标系,id为wellID的坐标系。

代码中的if check是if check=True的一种简写。

其他的一些解释见代码中的注释。

    def map(self, loc, check=True):
        # 先利用type()初步检查是否为整数,若为整数则属于position1D,然后if check:检查输入的整数是否属于1-96之内
        if type(loc) == type(15):
            if check:
                if not loc in self.validate['position1D']:
                    raise Exception('Invalid 1D PLate Position: %s' %str(loc))
            #算出输入的整数所对应的行名和列名,但这里的行名和列名并不是真正,而是要用于计算最后的行名和列名的计算,所以会-1
            row = int(math.ceil(float(loc)/float(self.columns))) - 1
            col = loc - (row * self.columns) - 1
        #先利用type()初步检查是否为元组,若为元组则属于position2D,然后if check:检查输入的元组是否属于(1,1)---(8,12)里面
        elif type(loc) == type((3,2)):
            if check:
                if not loc in self.validate['position2D']:
                    raise Exception('Invalid 2D PLate Position: %s' %str(loc))
            #算出输入的元组所对应的行名和列名,但这里的行名和列名并不是真正,而是要用于计算最后的行名和列名的计算,所以会-1
            row = loc[0] - 1
            col = loc[1] - 1
        # 先利用type()初步检查是否为字符串,若为字符串则属于wellID,然后if check:检查输入的字符串是否属于A01---H12里面
        elif type(loc) == type('A07'):
            if check:
                if not loc in self.validate['wellID']:
                    raise Exception('Invalid Well ID : %s' % str(loc))
            #算出输入的字符串所对应的行名和列名,但这里的行名和列名并不是真正,而是要用于计算最后的行名和列名的计算,所以会-1
            row = Plate.rowLabels.index(loc[0])
            col = int(loc[1:]) - 1
        else:
            raise Exception('Unrecignized Plate Location Type: %s' % str(loc))
        #利用上面的row和col算出真正的行名和列名
        pos = self.columns * row + col +1
        id = '%s%02d' %(Plate.rowLabels[row], col+1)
        #返回不同的坐标系所对应的值(1,(1,1),‘A01’)
        return (pos, (row+1,col+1), id)
#实例化对象,测试到目前为止的代码是否有效
p = Plate('My 96-well Plate', 8, 12)
print(p.map(1))
print(p.map('A01'))
print(p.map((1,1)))


=======================运行结果=======================
(1, (1, 1), 'A01')
(1, (1, 1), 'A01')
(1, (1, 1), 'A01')

3.分配(set)和检索(get)孔板的数据

    #定义set()方法以分配每个孔板所对应的值
    def set(self, loc, propertyName, value):
        m = self.map(loc)
        pos = m[Plate.mapPosition['position1D']]
        self.data[pos][propertyName] = value
    #定义get()方法以查看每个空在set()时所设定的值
    def get(self, loc, propertyName):
        m = self.map(loc)
        pos = m[Plate.mapPosition['position1D']]
        if propertyName in self.data[pos]:
            return self.data[pos][propertyName]
        else:
            #这里return后面没有东西,指的就是return None
            return
#用法展示
p = Plate('My 96-well Plate', 8, 12)
p.set('B01', 'concentration', 0.87)
print(p.get(13,'concentration'))
print(p.get((2,1),'concentration'))
print(p.get('B01','concentration'))

==================运行结果====================
0.87
0.87
0.87

4.读取和写入CSV

CSV是一种纯文本格式,以逗号分隔。您也许知道可以通过某些应用程序(如Excel)导出CSV的格式的表格数据。如果您需要使表格数据更易于人类理解,或者将其输入到另一个无法处理Excel自身内部数据格式的应用程序时CSV格式特别有用。

代码中96plateCSV.txt的内容如上图所示

 

    #读取CSV文件,并将CSV文件里面对应的值利用set()方法填入孔板
    def readCSV(self, filePath, propertyName):
        try:
            nWell = 1
            #需要注意的是要根据自己的csv文件的编码方式来设施encoding
            with open(filePath, mode='r',encoding='utf-8') as csvFile:
                csvReader = csv.reader(csvFile)
                for row in csvReader:
                    for wellData in row:
                        #利用上面的set()方法将csv文件的值填入相应的孔板
                        self.set(nWell, propertyName, float(wellData))
                        nWell += 1
        except:
            print('CSV data could not be correctly read from: %s' %filePath)
            return
        return
#用法展示
p = Plate('My 96-well Plate', 8, 12)
p.readCSV(r'D:\pycharm\pythonProject3\生命科学家的Python\mapping\96plateCSV.txt','concentration')
print(p.get(1,'concentration'))

===========================运行结果===================================
0.32

5.孔板中的数学

孔板中的网格布局很自然地适合使用板的行和列进行孔板测定实验的组织。例如将每个样品及其对照放置在同一行上,或在同一行上分析给定的样品连续稀释液,或垂直方向上进行上述操作。由于这个原因,多孔板的数据通常可以按行和列进行有用的组织和分析。考虑到这一点,我们在Plate类中添加了另外三个方法getRow、getColumn和average,它们可以在三个受支持坐标系中的任何一个定位单个孔的为止,并返回该位置所在的行或列的列表,并求出平均值。代码如下:

#定义getRow函数,来提取某个孔所在的行上的所有值,并添加到row[]列表里面
    def getRow(self, loc):
        here = self.map(loc)
        row = []
        for n in range(0, self.size):
            there = self.map(n+1)
            if there[1][0] == here[1][0]:
                row.append(there)
        return row

    # 定义getColumn函数,来提取某个孔所在的列上的所有值,并添加到col[]列表里面
    def getColumn(self, loc):
        here = self.map(loc)
        col = []
        for n in range(0, self.size):
            there = self.map(n+1)
            if there[1][1] == here[1][1]:
                col.append(there)
        return col
    #分别计算某一个孔所在的行或列的平均值
    def average(self, propertyName, loc=None):
        if loc == None:
            total = 0
            for pos in range(0, self.size):
                total += self.get(pos+1, propertyName)
            return total/self.size
        row = self.getRow(loc)
        col = self.getColumn(loc)
        rowTotal = 0.0
        colTotal = 0.0
        for pos in row:
            rowTotal += self.get(pos[1], propertyName)
        rowMean = rowTotal/self.columns
        for pos in col:
            colTotal += self.get(pos[1], propertyName)
        colMean = colTotal/self.rows
        return (rowMean, colMean)

#用法展示
p = Plate('My 96-well Plate', 8, 12)
p.readCSV(r'D:\pycharm\pythonProject3\生命科学家的Python\mapping\96plateCSV.txt','concentration')
print(p.get(1,'concentration'))
print(p.getRow('B04'))
print(p.getColumn('B04'))
print(p.average('concentration','B04'))
print(p.average('concentration'))

==================================运行结果==================
0.32
[(13, (2, 1), 'B01'), (14, (2, 2), 'B02'), (15, (2, 3), 'B03'), (16, (2, 4), 'B04'), (17, (2, 5), 'B05'), (18, (2, 6), 'B06'), (19, (2, 7), 'B07'), (20, (2, 8), 'B08'), (21, (2, 9), 'B09'), (22, (2, 10), 'B10'), (23, (2, 11), 'B11'), (24, (2, 12), 'B12')]
[(4, (1, 4), 'A04'), (16, (2, 4), 'B04'), (28, (3, 4), 'C04'), (40, (4, 4), 'D04'), (52, (5, 4), 'E04'), (64, (6, 4), 'F04'), (76, (7, 4), 'G04'), (88, (8, 4), 'H04')]
(11.555833333333332, 46.33625)
43.52604166666666

在上述的average()方法中,loc参数是可选的,因为提供了默认值loc=None,如果没有将loc的值传递给average()方法中,将使用该默认值,以计算整个板的平均值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值