(保守群组测试 非保守群组测试 二次重复测试 自适应二次重复测试)四种群体测试的C++代码

223 阅读4分钟

目录

原理

假设该病在人群中的患病率(先验概率)为p,我们想用群体检验法检验N个独立样本组大小n,即样本数量。
单个试验的诊断方法的FNR用fN表示,FPR可忽略不计。
用G表示一组样本,G=0表示G中的所有样本均未感染。相反,G=1意味着G是一个阳性组;即,它至少有一个感染标本。通过观察不同方法的分组试验结果,我们将一个组分为感染组和未感染组,分别用Gˆ=1和Gˆ=0表示。

保守组检测

在这里插入图片描述
在这里插入图片描述

非保守组检测

在这里插入图片描述

步骤如下:
在这里插入图片描述
每个Group由三个sample人的血液组成。
这样能保证每个Group能被测试两次(也代表着每个sample人的血液能够被测试两次),fN表示单个试验的FNR(false negative rate),这样就能保证Sg(群体水平敏感度)= 1 - fN^2;
总的来说,Gi、Gi+1为阳性,则Mi,i+1也为阳性。
这样,图1中的M2,3和M3,4都是正的,很可能是因为G3=1。尽管在G2=1和G4=1的情况下也会出现这些阳性结果,但缺少M1,2=1和M4,1=1使得G2和G4都不太可能等于1。在这种方法中,例如,如果G2为真阳性,M1,2=0为假阴性结果,我们将忽略G2=1,从而导致灵敏度降低。

在非保守组检测中有以下规则:
在这里插入图片描述

二次重复测试

简单来说就是对分组之后检测的所有组都检测两遍,只要有一次为阳则说明这个组中有阳性。
在这里插入图片描述

自适应二次重复测试

第二次测试将第一次测试中为阴性的进行测试。
在这里插入图片描述

四种测试方法的核心代码

【0】保守群组测试 【1】非保守群组测试 【2】二次重复测试 【3】二次自适应重复测试

保守群组测试

//非保守测试
//输入:splited_group 、 分组混合血液 、 假阴率 fnr
//输出:检测出来的阳性样本数、总检测次数
int No_Conservative_Group_Detection(vector<vector<int>>& splited_group,vector<int>& result, float fnr, vector<int>& Positive_Group, vector<int>& PositiveSamples)
{
	//将Gi、Gi+1混合到一起为Mi
	//如果相邻两个M(Mj,Mj+1)检测结果是阳性,则认定是Gj+1是阳性。
	//如果只有单独的Mj检测结果是阳性,则将Gj与Gj+1都检测一遍。

	int All_Test_Times = 0;

	//【step1】将G血液结果两两混合到M中
	vector<int> Mixed_Group_Result;
	for (int i = 0; i < result.size() - 1; i++)
	{
		if (result[i] == 1 || result[i + 1] == 1)
			Mixed_Group_Result.emplace_back(1);
		else 
			Mixed_Group_Result.emplace_back(0);
	}
	//第一个样本与最后一个样本混合
	if(result[0] == 1 || result[result.size()-1] == 1) Mixed_Group_Result.emplace_back(1);
	else Mixed_Group_Result.emplace_back(0);

	//【step2】对M结果进行检测,需要考虑到假阴性影响,得到阳性G序号
	vector<int> Blured_Mixed_Group_Result;
	Test_Synthetic_Blood(Mixed_Group_Result, Blured_Mixed_Group_Result, fnr);
	All_Test_Times += Mixed_Group_Result.size();
	//寻找检测结果为阳性的M
	for (int j = 0; j < Blured_Mixed_Group_Result.size() - 1; j++)
	{
		//如果是两个连在一起的为阳性,取交集的G
		if (Blured_Mixed_Group_Result[j] == 1 && Blured_Mixed_Group_Result[j + 1] == 1)
		{
			Positive_Group.emplace_back(j+1);
		}
		else if(Blured_Mixed_Group_Result[j] == 1 && Blured_Mixed_Group_Result[j + 1] == 0)
		{
			//如果Gj已经被送入结果,那么只需要再送入Gj+1
			if (!Positive_Group.empty() && Positive_Group.back() == j)
			{
				Positive_Group.emplace_back(j + 1);
			}
			//否则,将两个都送入
			else
			{
				Positive_Group.emplace_back(j);
				Positive_Group.emplace_back(j + 1);
			}
		}
	}
	cout << "非保守测试可能为阳性的Group个数" << Positive_Group.size() << endl;
	//对每个可能阳性的Group进行核酸检测
	for (int i = 0; i < Positive_Group.size(); i++)
	{
		int pos_index = Positive_Group[i];
		//对可能阳性Group中每个样本都进行检测,要考虑到假阴性的影响,得到阳性的样本的序号。
		vector<int> Blured_Splited_GroupResult;
		Test_Synthetic_Blood(splited_group[pos_index], Blured_Splited_GroupResult, fnr);
		//对检测结果进行记录,记录下在原序列中的坐标
		for (int j = 0; j < Blured_Splited_GroupResult.size(); j++)
		{
			if (Blured_Splited_GroupResult[j] == 1)
			{
				PositiveSamples.emplace_back(pos_index* splited_group[0].size() + j);
			}	
		}
	}
	All_Test_Times += Positive_Group.size() * splited_group[0].size();
	return All_Test_Times;
	
}

非保守群组测试

//保守测试
//输入:splited_group 、 分组混合血液 、 假阴率 fnr
//输出:检测出来的阳性样本数、总检测次数
int Conservative_Group_Detection(vector<vector<int>>& splited_group, vector<int>& result, float fnr, vector<int>& Positive_Group, vector<int>& PositiveSamples)
{
	//将Gi、Gi+1混合到一起为Mi
	//如果M检测结果是阳性,则认定是Gj,Gj+1是阳性。
	int All_Test_Times = 0;

	//【step1】将G血液结果两两混合到M中
	vector<int> Mixed_Group_Result;
	for (int i = 0; i < result.size() - 1; i++)
	{
		if (result[i] == 1 || result[i + 1] == 1)
			Mixed_Group_Result.emplace_back(1);
		else
			Mixed_Group_Result.emplace_back(0);
	}
	//第一个样本与最后一个样本混合
	if (result[0] == 1 || result[result.size() - 1] == 1) Mixed_Group_Result.emplace_back(1);
	else Mixed_Group_Result.emplace_back(0);

	//【step2】对M结果进行检测,需要考虑到假阴性影响,得到阳性G序号
	vector<int> Blured_Mixed_Group_Result;
	Test_Synthetic_Blood(Mixed_Group_Result, Blured_Mixed_Group_Result, fnr);
	All_Test_Times += Mixed_Group_Result.size();
	//寻找检测结果为阳性的M
	for (int j = 0; j < Blured_Mixed_Group_Result.size() - 1; j++)
	{
		if (Blured_Mixed_Group_Result[j] == 1)
		{
			if (!Positive_Group.empty() && Positive_Group.back() == j)
			{
				Positive_Group.emplace_back(j + 1);
			}
			else
			{
				Positive_Group.emplace_back(j);
				Positive_Group.emplace_back(j + 1);
			}
		}
	}
	//对每个可能阳性的Group进行核酸检测
	cout << "保守测试可能为阳性的Group个数"<< Positive_Group.size() <<endl;
	for (int i = 0; i < Positive_Group.size(); i++)
	{
		int pos_index = Positive_Group[i];
		//对可能阳性Group中每个样本都进行检测,要考虑到假阴性的影响,得到阳性的样本的序号。
		vector<int> Blured_Splited_GroupResult;
		Test_Synthetic_Blood(splited_group[pos_index], Blured_Splited_GroupResult, fnr);
		//对检测结果进行记录,记录下在原序列中的坐标
		for (int j = 0; j < Blured_Splited_GroupResult.size(); j++)
		{
			if (Blured_Splited_GroupResult[j] == 1)
			{
				PositiveSamples.emplace_back(pos_index * splited_group[0].size() + j);
			}
		}
	}
	All_Test_Times += Positive_Group.size() * splited_group[0].size();
	return All_Test_Times;

}

二次重复测试与自适应二次重复测试

/*
* 重复二次测量
* 输入:result实际感染情况,blured_result第一次检测情况,rep_blured_result第二次检测情况
* 输出:
*/
void Replicate_Test(vector<int>& result, vector<int>& blured_result, vector<int>& rep_blured_result, float fnr)
{
	rep_blured_result.clear();
	srand((unsigned)time(NULL));
	int fnr_100 = fnr * 100;
	for (int i = 0; i < result.size(); i++) //第二次全覆盖测试
	{
		//对每个阳性样本进行混淆处理
		if (result[i] == 1)
		{
			int num = rand() % 100;
			// r % 概率检测为阴性
				if (num <= fnr_100)
				{
					rep_blured_result.emplace_back(0);
				}
			//1-fnr%概率检测为阳性
				else
				{
					rep_blured_result.emplace_back(1);
				}
		}
		//由于covid-19不存在假阳性,这里对阴性样本不做处理
		else
		{
			rep_blured_result.emplace_back(0);
		}
	}

	for (int i = 0; i < result.size(); i++) { //将第一第二次的检测结果合并
		if (blured_result[i] == 1) {
			rep_blured_result[i] = 1;
		}
	}
	return;
}
/*
* 自适应重复二次测量,对象为第一次测量中为阴性的组
* 输入:result实际感染情况,blured_result第一次检测情况,rep_blured_result第二次检测情况
* 输出:二次测量的测量数
*/
int Adt_Replicate_Test(vector<int>& result, vector<int>& blured_result, vector<int>& rep_blured_result, float fnr) {
	rep_blured_result.clear();
	srand((unsigned)time(NULL));
	int fnr_100 = fnr * 100;
	int Test_Times = 0;
	for (int i = 0; i < blured_result.size(); i++)
	{
		//对第一次检测结果再进行挑选检测
		if (blured_result[i] == 1) //第一次检测为阳性,跳过检测
		{
			rep_blured_result.emplace_back(1);
		}
		else  //第一次检测为阴性,再次检测
		{
			Test_Times++;
			if (result[i] == 1) {
				int num = rand() % 100;
				// r % 概率检测为阴性
					if (num <= fnr_100)
					{
						rep_blured_result.emplace_back(0);
					}
				//1-fnr%概率检测为阳性
					else
					{
						rep_blured_result.emplace_back(1);
					}
			}
			else rep_blured_result.emplace_back(0);
		}
	}
	return Test_Times;
}

/*
* 重复二次测量
* 输入:splited_group分组,result实际感染情况,fnr假阴率,Positive_Group二次测量后的阳性组,PositiveSamples二次测量后的阳性样本,Mode方法模式,0为重复二次测量,1为自适应重复二次测量
* 输出:
*/
int Replicate_TestDetection(vector<vector<int>>& splited_group, vector<int>& result, float fnr, vector<int>& Positive_Group, vector<int>& PositiveSamples,int Mode)
{
	//将Gi、Gi+1混合到一起为Mi
	//如果M检测结果是阳性,则认定是Gj,Gj+1是阳性。
	int All_Test_Times = 0;

	//【step1】将G血液结果两两混合到M中
	vector<int> Mixed_Group_Result;
	for (int i = 0; i < result.size() - 1; i++)
	{
		if (result[i] == 1 || result[i + 1] == 1)
			Mixed_Group_Result.emplace_back(1);
		else
			Mixed_Group_Result.emplace_back(0);
	}
	//第一个样本与最后一个样本混合
	if (result[0] == 1 || result[result.size() - 1] == 1) Mixed_Group_Result.emplace_back(1);
	else Mixed_Group_Result.emplace_back(0);

	vector<int> Blured_Mixed_Group_Result;
	//第一次检测
	Test_Synthetic_Blood(Mixed_Group_Result, Blured_Mixed_Group_Result, fnr);
	All_Test_Times += Mixed_Group_Result.size();
	vector<int> Sec_Blured_Mixed_Group_Result;
	if (Mode == 0)
	{
		//第二次检测,采用重复二次测量
		Replicate_Test(result, Blured_Mixed_Group_Result, Sec_Blured_Mixed_Group_Result, fnr);
		All_Test_Times += Blured_Mixed_Group_Result.size();
	}
	else
	{
		//第二次检测,采用自适应重复二次测量,对象为第一次测量中为阴性的组
		int Sec_Test_Times = Adt_Replicate_Test(result, Blured_Mixed_Group_Result, Sec_Blured_Mixed_Group_Result, fnr);
		All_Test_Times += Sec_Test_Times;
	}
	//记录二次结果后的阳性组
	for (int i = 0; i < Sec_Blured_Mixed_Group_Result.size(); i++)
	{
		if (Sec_Blured_Mixed_Group_Result[i] == 1) Positive_Group.emplace_back(i);
	}
	//对每个可能阳性的Group进行核酸检测
	cout << "二次重复测试可能为阳性的Group个数" << Positive_Group.size() << endl;
	for (int i = 0; i < Positive_Group.size(); i++)
	{
		int pos_index = Positive_Group[i];
		//对可能阳性Group中每个样本都进行检测,要考虑到假阴性的影响,得到阳性的样本的序号。
		vector<int> Blured_Splited_GroupResult;
		Test_Synthetic_Blood(splited_group[pos_index], Blured_Splited_GroupResult, fnr);
		//对检测结果进行记录,记录下在原序列中的坐标
		for (int j = 0; j < Blured_Splited_GroupResult.size(); j++)
		{
			if (Blured_Splited_GroupResult[j] == 1)
			{
				PositiveSamples.emplace_back(pos_index * splited_group[0].size() + j);
			}
		}
	}
	All_Test_Times += Positive_Group.size() * splited_group[0].size();
	return All_Test_Times;
}

测试代码

	//检测总人数
	int sample_nums = 175000;
	//流行率设定
	double p = 0.04;
	//分组设定
	int sec_group_sample_nums =2;
	//假阴率设定
	float FNR = 0.2;

	int positive_sample_nums = p * sample_nums;
	if (sample_nums <= 0)
	{
		cout <<"error1" <<endl;
		return 0;
	}
		sec_group_sample_nums = pow(2, i + 1);
		vector<int> Group(sample_nums, 0);
		//混合血液后的真实结果
		vector<int> Result;
		//受到假阴性影响后的混合血液测试结果
		vector<int> Blured_Result;
		vector<vector<int>> Splited_Group;
		//【1】初始化样本序列
		Initial_Group(Group, positive_sample_nums);
		//Pirnt_Group(Group);
		cout << endl;
		//【2】Pooling操作
		Split_Group(Group, Splited_Group, sec_group_sample_nums);
		//Pirnt_Split_Group(Splited_Group);
		cout << endl;
		//【3】混合血液
		Synthetic_Blood(Splited_Group, Result);

		vector<int> Positive_Group;
		vector<int> PositiveSamples;
		int All_Test_Times = 0;
		int Methods = 3;
		//【4】使用不同的测试方法
		switch (Methods)
		{
			//【0】保守群组测试
		case 0:
		{
			All_Test_Times = Conservative_Group_Detection(Splited_Group, Result, FNR, Positive_Group, PositiveSamples);
			cout << "重复性测试总检测次数" << All_Test_Times << endl;
			if (positive_sample_nums != 0)
				cout << "检测出的阳性样本个数为" << PositiveSamples.size() << "准确率" << PositiveSamples.size() * 1.0f / positive_sample_nums << endl;
			else
				cout << "error2" << endl;
		}break;
		//【1】非保守群组测试
		case 1:
		{
			All_Test_Times = No_Conservative_Group_Detection(Splited_Group, Result, FNR, Positive_Group, PositiveSamples);
			cout << "非重复性测试总检测次数" << All_Test_Times << endl;
			if (positive_sample_nums != 0)
				cout << "检测出的阳性样本个数为" << PositiveSamples.size() << "准确率" << PositiveSamples.size() * 1.0f / positive_sample_nums << endl;
			else
				cout << "error2" << endl;
		}break;
		//【2】二次重复测试
		case 2:
		{
			All_Test_Times = Replicate_TestDetection(Splited_Group, Result, FNR, Positive_Group, PositiveSamples, 0);
			cout << "重复性测试总检测次数" << All_Test_Times << endl;
			cout << "阳性样本组数" << Positive_Group.size() << endl;
			if (positive_sample_nums != 0)
				cout << "检测出的阳性样本个数为" << PositiveSamples.size() << "准确率" << PositiveSamples.size() * 1.0f / positive_sample_nums << endl;
			else
				cout << "error2" << endl;
		}break;
		//【3】二次自适应重复测试
		case 3:
		{
			All_Test_Times = Replicate_TestDetection(Splited_Group, Result, FNR, Positive_Group, PositiveSamples, 1);
			cout << "重复性测试总检测次数" << All_Test_Times << endl;
			if (positive_sample_nums != 0)
				cout << "检测出的阳性样本个数为" << PositiveSamples.size() << "准确率" << PositiveSamples.size() * 1.0f / positive_sample_nums << endl;
			else
				cout << "error2" << endl;
		}break;
		default:
			break;
		}

		//根据仿真得出一个测试实际社会成本(即找出最后为阴性的组数/总组数*每组多少人*每个人的闲置成本)
		int Neg_Groups = Splited_Group.size() - Positive_Group.size();
		double Neg_rate = Neg_Groups * 1.0f / Splited_Group.size();
		cout << "Neg_rate " << Neg_rate << endl;
		cout << "总样本个数 " << Splited_Group.size() << endl;

参考文献

www.medrxiv.org/content/10.…