From c203913bd40420fca5e932a6b21df8367ebb8dea Mon Sep 17 00:00:00 2001 From: chaopower Date: Tue, 30 Jan 2024 14:31:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rule/columns.xlsx | Bin 0 -> 9588 bytes tools/t7.py | 213 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 rule/columns.xlsx diff --git a/rule/columns.xlsx b/rule/columns.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..acc4da2889201877d4910722b90d446591d01d3c GIT binary patch literal 9588 zcmeHtg;yNe_I2YHpmEm*f?IG6?h*(dG&Jt+9$bREJHg$9Ly+JhKp??ggX`CsncvC` z^S-~}y}MRbty$P3kpKU0{1?wadBTWI4=YgOGVumIwn@!uJFko$ zIzlLkNo5D2`Q#|@y z6&qq6=S{({+kV_Z-FC70NA1b7HnrQV@|pT~G^{1S2yAG3S7D0N*4q!Ep#s!=|5_BJ{eoMj5S zY@b%V%-mKG0g0ZWai7BiBB<**@jDN{p{cHapFJi503IJ<0LuShYJ(ap=nUe1@(?1T zK$xoU0Jd^uVg7agA4C6(Y5BKTFOO4D>ScWvd?bArGITk+^d2ZG=PDxIM)BImPv#qX zLsUK$(bC7~#K6~tfiP0O9X@x%i%SAgyF(Ngs~qJKnArTFM%RkqD9ScSyFxPqzv+5-w&%SKm~ zP_v?{2Nl89P24%V@#E>fb4f)zsKTMV^7~V%I75y`rt_6vLsk^$*Mw@SX1wOrMp+Jg zlCH#*ML1PXhRY-;OCQ8l(W3CSg5h#Q`)eHQß?(nx%ZePYz{% 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 literal 0 HcmV?d00001 diff --git a/tools/t7.py b/tools/t7.py index 0b0dd6b..bc199e4 100644 --- a/tools/t7.py +++ b/tools/t7.py @@ -5,16 +5,163 @@ from collections import defaultdict from datetime import datetime import pandas as pd +from deap import base, creator, tools, algorithms from tools.common import basedir, log # 定义一个格式化函数 def format_date(date): - if isinstance(date, pd.Timestamp): - return date.strftime('%Y-%m-%d') - else: - return str(date) + return date.strftime('%Y-%m-%d') + + +def count_barcode_radio(data): + df = pd.DataFrame(data) + ratio_sites = dict() + is_not_balance_list = [] + if df.empty: + return ratio_sites, is_not_balance_list + + df['barcode'] = df['barcode'].str.slice(0, 16) + barcode_df = pd.DataFrame(df['barcode'].str.split('', expand=True).iloc[:, 1:-1].values, + columns=['T' + str(x) for x in range(16)]).join(df['data_needed']) + total = barcode_df['data_needed'].sum() + + for i in range(16): + column = 'T' + str(i) + col_df = barcode_df.groupby(column).agg({'data_needed': 'sum'}) + # 去掉N计数 + if 'N' in col_df.index: + base_n_size = col_df.loc['N', 'data_needed'] + col_df = col_df.drop('N') + else: + base_n_size = 0 + col_df['ratio'] = (col_df['data_needed']) / (total - base_n_size) + ratio = col_df['ratio'].to_dict() + ratio_sites[i] = ratio + A, B, C, D, E, F = list(), list(), list(), list(), list(), list() + for decbase in ['A', 'T', 'C', 'G']: + if decbase not in ratio: + ratio[decbase] = 0 + if ratio[decbase] >= 0.6: + A.append(decbase) + if 0.2 <= ratio[decbase] < 0.6: + B.append(decbase) + if 0.15 <= ratio[decbase] < 0.2: + C.append(decbase) + if 0.1 <= ratio[decbase] < 0.15: + D.append(decbase) + if 0.08 <= ratio[decbase] < 0.1: + E.append(decbase) + if ratio[decbase] < 0.08: + F.append(decbase) + + A_num, B_num, C_num, D_num, E_num, F_num = len(A), len(B), len(C), len(D), len(E), len(F) + if not ((B_num + C_num + D_num == 4) or (F_num == 1 and (A_num + B_num) == 3) or ( + E_num == 1 and D_num == 1 and (A_num + B_num + C_num) == 2) or ( + E_num == 1 and (A_num + B_num + C_num) == 3)): + is_not_balance_list.append( + '第%s位置,算出结果为 %s' % (i, ratio) + ) + return ratio_sites, is_not_balance_list + + +# 定义遗传算法 +class Ga: + """ + # 定义遗传算法 + """ + + def __init__(self, sheets): + self.sheets = sheets + + # 定义个体的生成方式 + def generate_individual(self): + individual = copy.deepcopy(self.sheets) # 初始解作为个体 + return [individual] + + # 定义评估函数 + @staticmethod + def evaluate(individual): + total_data_needed_sum = 0 + xchip = 0 + try: + for sheetname, data in individual[0][0].items(): + library_data = pd.DataFrame(data) + + size = library_data['data_needed'].sum() + + # 芯片大小不能超过设定限制 + if size > 1700: + return (0, 100000, 100000) + + # barcode有重复 + if len(library_data['barcode'].values) < len(set(library_data['barcode'].values)): + return (0, 100000, 100000) + + # 不平衡文库大于250G 不能添加 + if library_data[library_data['is_balance_lib'] == '否']['data_needed'].sum() > 250: + return (0, 100000, 100000) + + # 碱基不平衡不过不添加,保证前面的数据, 在数据达到1200G的时候开始 + ratio_sites, is_not_balance_list = count_barcode_radio(library_data) + if is_not_balance_list: + return (0, 100000, 100000) + + if library_data[library_data['classification'].str.lower() == 'nextera']['data_needed'].sum() <= 50: + return (0, 100000, 100000) + + # 计算每个sheet的data_needed之和 + total_data_needed_sum += library_data['data_needed'].sum() + + # 记录包含字母"A"的sheet数量 + if any('极致' in value for value in library_data['split']): + xchip += 1 + except Exception: + return (0, 100000, 100000) + + # 返回一个适应度值,目标是最大化总的data_needed之和,最小化sheet的数量, 最少的极致芯片 + total_data_needed_sum, num_sheets, num_xchip = total_data_needed_sum, len(individual[0]), xchip + return total_data_needed_sum, num_sheets, num_xchip + + def run(self): + # 定义遗传算法的参数 + pop_size = 50 + cxpb = 0.7 # 交叉概率 + mutpb = 0.2 # 变异概率 + ngen = 100 # 迭代次数 + + # 初始化遗传算法工具箱 + creator.create("FitnessMax", base.Fitness, weights=(1.0, -1.0, -1.0,)) # 三个目标,一个最大化两个最小化 + creator.create("Individual", list, fitness=creator.FitnessMax) + + toolbox = base.Toolbox() + + # 结构初始化器 + toolbox.register("individual", tools.initRepeat, creator.Individual, self.generate_individual, n=3) + toolbox.register("population", tools.initRepeat, list, toolbox.individual) + toolbox.register("evaluate", self.evaluate) + + # 注册遗传算法所需的操作 + toolbox.register("mate", tools.cxTwoPoint) + toolbox.register("mutate", tools.mutUniformInt, low=1, up=100, indpb=0.2) + toolbox.register("select", tools.selTournament, tournsize=3) + # 初始化种群 + population = toolbox.population(n=pop_size) + + # 运行遗传算法 + algorithms.eaMuPlusLambda(population, toolbox, mu=pop_size, lambda_=pop_size * 2, cxpb=cxpb, mutpb=mutpb, + ngen=ngen, stats=None, halloffame=None) + + # 输出结果 + best_individual = tools.selBest(population, k=1) + print(best_individual) + optimized_sheets = best_individual[0] # 获取最优解 + + # 将优化后的结果输出 + # for i, sheet in enumerate(optimized_sheets): + # sheet.to_excel(f'optimized_sheet_{i + 1}.xlsx', index=False) + return optimized_sheets class AutoLayout: @@ -53,6 +200,7 @@ class AutoLayout: self.logger = log(os.path.basename(f'{path}.txt')) self.return_log = list() self.no_assign_data = list() + self.need_cols = self.read_cols() def read_excel(self): """ @@ -99,7 +247,8 @@ class AutoLayout: if 'nextera' in library_data['classification'].lower(): self.chip_speciallib_nextera_size[chipname] += library_data['size'] - def count_barcode_radio(self, data): + @staticmethod + def count_barcode_radio(data): df = pd.DataFrame(data) ratio_sites = dict() is_not_balance_list = [] @@ -116,11 +265,11 @@ class AutoLayout: col_df = barcode_df.groupby(column).agg({'data_needed': 'sum'}) # 去掉N计数 if 'N' in col_df.index: - base_N_size = col_df.loc['N', 'data_needed'] + base_n_size = col_df.loc['N', 'data_needed'] col_df = col_df.drop('N') else: - base_N_size = 0 - col_df['ratio'] = (col_df['data_needed']) / (total - base_N_size) + base_n_size = 0 + col_df['ratio'] = (col_df['data_needed']) / (total - base_n_size) ratio = col_df['ratio'].to_dict() ratio_sites[i] = ratio A, B, C, D, E, F = list(), list(), list(), list(), list(), list() @@ -195,6 +344,12 @@ class AutoLayout: res = pd.concat([df, newdf]) return res.reset_index() + @staticmethod + def read_cols(): + df = pd.read_excel(os.path.join(basedir, 'rule', 'columns.xlsx')) + cols = list(df['cols'].values) + return cols + def use_rule(self, chipname, classfication): may_classfic = set(self.rule[self.rule['c1'] == classfication]['c2']) if self.chip_customer[chipname].intersection(may_classfic): @@ -204,7 +359,7 @@ class AutoLayout: def judge_data(self, chipname, library_data): size = library_data['size'] # customer = library_data['customer'] - library = library_data['library'] + # library = library_data['library'] classification = library_data['classification'] is_balance_lib = library_data['is_balance_lib'] @@ -269,12 +424,13 @@ class AutoLayout: raise UserWarning('提供excel没有 未测 sheet ,请核查!') ori_library_df = pd.DataFrame(self.ori_data['未测']) - need_col = ['status', '#library', 'sublibrary', 'i5', 'i7', 'data_needed', 'real_data', 'customer', - 'classification', 'priority', 'time', '拆分方式', 'barcode', 'is_balance_lib', '备注', - 'TIPS1', 'TIPS2', 'TIPS3' - ] + # need_col = ['status', '#library', 'sublibrary', 'i5', 'i7', 'data_needed', 'real_data', 'customer', + # 'classification', 'priority', 'time', '拆分方式', 'barcode', 'is_balance_lib', '备注', + # 'TIPS1', 'TIPS2', 'TIPS3' + # ] + self.need_cols = self.read_cols() get_col = set(ori_library_df.columns) - unhave_col = set(need_col) - get_col + unhave_col = set(self.need_cols) - get_col if unhave_col: unhave_fom = '; '.join(unhave_col) @@ -291,10 +447,10 @@ class AutoLayout: ori_library_df.loc[~time_mask, 'note'] = 'time 列非日期' ori_library_df.loc[status_mask, 'note'] = '暂不排样' - need_col.append('note') + # need_col.append('note') no_ori_data = ori_library_df[~(numeric_mask & time_mask) | status_mask] - no_ori_data.loc[:, 'time'] = no_ori_data['time'].apply(format_date) + self.no_assign_data.extend(no_ori_data.to_dict('records')) # 使用布尔索引筛选出不是数字和非日期的行 @@ -332,7 +488,7 @@ class AutoLayout: level=library_df['level'].values[0], customer=library_df['customer'].values[0], classification=library_df['classification'].values[0], - data=library_df[need_col].to_dict('records') + data=library_df[self.need_cols].to_dict('records') )) if flag: @@ -346,9 +502,9 @@ class AutoLayout: level=library_df['level'].values[0], customer=library_df['customer'].values[0], classification=library_df['classification'].values[0], - data=library_df[need_col].to_dict('records') + data=library_df[self.need_cols].to_dict('records') )) - ori_sort_data = sorted(ori_library_data, key=lambda x: (x['level'], x['time'])) + ori_sort_data = sorted(ori_library_data, key=lambda x: (x['level'], x['time'], -x['size'])) i = 0 while ori_sort_data: @@ -397,6 +553,9 @@ class AutoLayout: outputpath = os.path.join(self.output, 'result', outputname) writer = pd.ExcelWriter(outputpath) + # ga = Ga(sheets=self.index_assignments) + # self.index_assignments = ga.run() + chip_loc = 1 librarynum = 0 for chip_idx, chip_assignments in self.index_assignments.items(): @@ -415,9 +574,23 @@ class AutoLayout: else: addname = '' self.dec_barcode_radio(chip_idx) - df.to_excel(writer, sheet_name=addname + chip_idx, index=False) + chipname = addname + chip_idx + + sum_list = list() + for library, library_df in df.groupby('#library'): + sum_list.append(dict( + 二次拆分=library, + 客户=library_df['customer'].values[0], + 类型=library_df['classification'].values[0], + 打折前=library_df['data_needed'].sum() + )) + df_sum = pd.DataFrame(sum_list) + res_df = pd.concat([df, df_sum], axis=1) + res_df.to_excel(writer, sheet_name=chipname, index=False) chip_loc += 1 no_assign_df = pd.DataFrame(self.no_assign_data) + no_assign_df = no_assign_df.applymap(lambda x: format_date(x) if isinstance(x, pd.Timestamp) else x) + no_assign_df = no_assign_df[self.need_cols] no_assign_df.to_excel(writer, sheet_name='未测', index=False) if self.return_log: pd.DataFrame(self.return_log).to_excel(writer, sheet_name='log', index=False)