ObjectiveC中的self.变量和_变量的区别

本文通过实例解析了Objective-C中@property属性的重写方法,并解释了self与下划线变量的区别,以及如何避免在重写setter方法时出现的常见错误。

今天因需要重写@property属性的set方法,没想到程序编译通过了,却运行不了,set方法一直报错,错误如下:
这里写图片描述

一开始真是搞不明白啊,都能改用别的方法,不重载这个东东了,烦死了!不过烦归烦,问题终归还是要解决的。我看着代码,突然觉得这个self.变量是不是不对呢?于是我把set函数里self改成了下划线,成功运行了!!太棒了!

这里就解释一下原因,我们都知道,在苹果的官方源码中,大量使用了下划线,那为什么苹果会使用下划线而不是self呢?这里有篇博客大家可以参考一下,说得比较清楚,不过可能在ARC环境下不再适用了吧。http://blog.sina.com.cn/s/blog_7b9d64af0101923n.html

还有一点需要了解的就是@property定义的属性编译器会自动编写getter和setter方法,当然,getter和setter方法也可以根据需要重写。
先给出例子:
Test.h
Test.h

Test.m
Test.m

测试代码
测试代码

运行结果
运行结果

从这个简单的例子中,很清楚可以知道,利用self改变变量值,其实是调用了setter方法,但是下划线这种方式并没有调用setter方法。所以也可以说明了我之前在写setter方法时,在setter方法里使用self肯定会有问题。

在这个简单的测试代码里,我写了四个NSString,主要是为了说明一下,公共变量和私有变量,对初学者来说,一开始很容易混淆的。这四个变量中,只有string2能被外部类访问,其他三个都不能,另外,string2和string4有默认的getter和setter方法,可以使用self和下划线获取变量。string1和string3直接使用string1=XXX或XXX=string1就可以。

这是很简单的一个说明,太本质的东西我暂时也没有弄清楚,记录一下,希望看到这个的人不要再像我一样犯错误!如果有什么不对的对方,欢迎大神们指教!!

#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import rospy import numpy as np from geometry_msgs.msg import WrenchStamped from nav_msgs.msg import Odometry class TutorialNMPCController: def __init__(self): super(TutorialNMPCController, self).__init__() # NMPC控制器参数初始化 self._prediction_horizon = 0 self._control_horizon = 0 self._dt = 0.0 # 权重矩阵 self._Q = np.zeros(shape=(6, 6)) # 状态误差权重 self._R = np.zeros(shape=(6, 6)) # 控制输入权重 self._P = np.zeros(shape=(6, 6)) # 终端状态权重 # 约束参数 self._u_min = np.zeros(shape=(6,)) # 控制输入下限 self._u_max = np.zeros(shape=(6,)) # 控制输入上限 self._x_min = np.zeros(shape=(6,)) # 状态下限 self._x_max = np.zeros(shape=(6,)) # 状态上限 # 其他参数 self._solver_options = {} # 求解器选项 self._warm_start = False # 是否使用热启动 # 控制器状态 self._prev_control = np.zeros(shape=(6,)) self._predicted_states = None self._predicted_controls = None self._solver_status = None self._solve_time = 0.0 self._warm_start_solution = None # 系统状态 self._is_init = False self.odom_is_init = False self.current_odom = None # ROS相关初始化 self._control_pub = rospy.Publisher('thruster_output', WrenchStamped, queue_size=10) self._odom_sub = rospy.Subscriber('odom', Odometry, self._odom_callback) # 从ROS参数服务器加载参数 self._load_parameters() # 验证关键参数 if self._control_horizon <= 0: rospy.logwarn(f"Invalid control_horizon: {self._control_horizon}, setting to default (5)") self._control_horizon = 5 self._is_init = True rospy.loginfo(f"NMPC Controller initialized with control horizon: {self._control_horizon}") rospy.loginfo("NMPC Controller initialized successfully") def _odom_callback(self, msg): """里程计消息回调函数""" self.current_odom = msg self.odom_is_init = True # 提取位置姿态信息 self.current_position = np.array([ msg.pose.pose.position.x, msg.pose.pose.position.y, msg.pose.pose.position.z ]) # 四元数转欧拉角 (简化处理,实际应用需使用完整转换) self.current_orientation = np.array([0, 0, 0]) # 示例,需完善 rospy.logdebug("Received odometry message") def _load_parameters(self): """从ROS参数服务器加载NMPC控制器参数""" if rospy.has_param('~prediction_horizon'): self._prediction_horizon = rospy.get_param('~prediction_horizon') rospy.loginfo(f'Prediction Horizon = {self._prediction_horizon}') else: rospy.logwarn('Parameter prediction_horizon not found, using default') self._prediction_horizon = 10 if rospy.has_param('~control_horizon'): self._control_horizon = rospy.get_param('~control_horizon') rospy.loginfo(f'Control Horizon = {self._control_horizon}') else: rospy.logwarn('Parameter control_horizon not found, using default') self._control_horizon = 5 if rospy.has_param('~dt'): self._dt = rospy.get_param('~dt') rospy.loginfo(f'Sampling Time = {self._dt}') else: rospy.logwarn('Parameter dt not found, using default') self._dt = 0.1 # 加载权重矩阵 # ... 其他参数加载代码保持不变 ... def _reset_controller(self): """重置NMPC控制器状态""" # 重置逻辑保持不变... def update_controller(self): """更新NMPC控制器并发布控制指令""" if not self._is_init: rospy.logwarn("Controller not initialized, skipping update") return False if not self.odom_is_init: rospy.logwarn("Odometry not received yet, skipping update") return False # 获取当前状态参考轨迹 current_state = self._get_current_state() reference_trajectory = self._get_reference_trajectory() # 构建NMPC优化问题 problem = self._construct_optimization_problem(current_state, reference_trajectory) # 求解优化问题 solution, status, solve_time = self._solve_optimization_problem(problem) # 检查求解状态 if status != 'optimal': rospy.logwarn(f"NMPC solver did not converge: {status}") if self._prev_control is not None: # 使用上一次控制输入作为备份 control_input = self._prev_control else: # 如果没有上一次控制输入,则使用零控制 control_input = np.zeros(shape=(6,)) else: # 提取最优控制输入 control_input = self._extract_control_input(solution) self._prev_control = control_input # 保存当前控制输入供下次使用 # 打印控制输入用于调试 rospy.loginfo(f"Control input: {control_input}") # 发布控制指令 self.publish_control_wrench(control_input) # 保存求解信息用于调试 self._solver_status = status self._solve_time = solve_time return True def _get_current_state(self): """获取当前系统状态""" if self.current_odom is None: rospy.logerr("No odometry data available") return np.zeros(6) # 从里程计消息中提取状态 [x, y, z, vx, vy, vz] state = np.array([ self.current_odom.pose.pose.position.x, self.current_odom.pose.pose.position.y, self.current_odom.pose.pose.position.z, self.current_odom.twist.twist.linear.x, self.current_odom.twist.twist.linear.y, self.current_odom.twist.twist.linear.z ]) return state def _get_reference_trajectory(self): """获取参考轨迹""" # 示例:生成一个简单的参考轨迹 (保持当前位置) current_state = self._get_current_state() ref_traj = np.tile(current_state, (self._prediction_horizon + 1, 1)) # 添加一个小的目标偏移 (测试用) ref_traj[:, 0] += 1.0 # x方向偏移1米 return ref_traj def _construct_optimization_problem(self, current_state, reference_trajectory): """构建NMPC优化问题 (需根据具体求解器实现)""" # 实际应用中需替换为真实优化问题构建逻辑 # 这里使用简化示例,返回一个模拟问题 return {"current_state": current_state, "reference": reference_trajectory} def _solve_optimization_problem(self, problem): """求解NMPC优化问题""" # 添加调试日志 rospy.loginfo(f"Solving optimization problem with control horizon: {self._control_horizon}") # 确保 control_horizon 至少为1 control_horizon = max(1, self._control_horizon) # 返回固定控制输入 (x方向100推力) solution = np.tile(np.array([100, 0, 0, 0, 0, 0]), (control_horizon, 1)) rospy.loginfo(f"Solution shape: {solution.shape}") status = 'optimal' solve_time = 0.01 return solution, status, solve_time def _extract_control_input(self, solution): """从优化解中提取控制输入""" if solution is None or solution.size == 0: rospy.logerr("Received empty solution, using default control") return np.array([100, 0, 0, 0, 0, 0]) # 返回默认控制 # 提取第一个控制输入 return solution[0] def publish_control_wrench(self, control_input): """发布控制指令""" msg = WrenchStamped() msg.header.stamp = rospy.Time.now() msg.header.frame_id = 'base_link' msg.wrench.force.x = control_input[0] msg.wrench.force.y = control_input[1] msg.wrench.force.z = control_input[2] msg.wrench.torque.x = control_input[3] msg.wrench.torque.y = control_input[4] msg.wrench.torque.z = control_input[5] self._control_pub.publish(msg) rospy.logdebug(f"Published control wrench: {control_input}") if __name__ == '__main__': print('Tutorial - NMPC Controller') rospy.init_node('tutorial_nmpc_controller') try: node = TutorialNMPCController() # 设置更新频率 rate = rospy.Rate(10000) # 10Hz while not rospy.is_shutdown(): node.update_controller() rate.sleep() except rospy.ROSInterruptException: print('caught exception') print('exiting') 更改这段代码,只保留def __init__(self):、def _reset_controller(self):、def update_controller(self): 、if __name__ == '__main__':、nmpc定义的模型问题求解部分
05-14
#!/usr/bin/env python import rospy import numpy as np from uuv_control_interfaces import DPControllerBase class TutorialDPController(DPControllerBase): def __init__(self): super(TutorialDPController, self).__init__(self) self._Kp = np.zeros(shape=(6, 6)) self._Kd = np.zeros(shape=(6, 6)) self._Ki = np.zeros(shape=(6, 6)) self._int = np.zeros(shape=(6,)) self._error_pose = np.zeros(shape=(6,)) # Do the same for the other two matrices if rospy.get_param('~Kp'): diag = rospy.get_param('~Kp') if len(diag) == 6: self._Kp = np.diag(diag) print 'Kp=\n', self._Kp else: # If the vector provided has the wrong dimension, raise an exception raise rospy.ROSException('For the Kp diagonal matrix, 6 coefficients are needed') if rospy.get_param('~Kd'): diag = rospy.get_param('~Kd') if len(diag) == 6: self._Kd = np.diag(diag) print 'Kd=\n', self._Kd else: # If the vector provided has the wrong dimension, raise an exception raise rospy.ROSException('For the Kd diagonal matrix, 6 coefficients are needed') if rospy.get_param('~Ki'): diag = rospy.get_param('~Ki') if len(diag) == 6: self._Ki = np.diag(diag) print 'Ki=\n', self._Ki else: # If the vector provided has the wrong dimension, raise an exception raise rospy.ROSException('For the Ki diagonal matrix, 6 coefficients are needed') self._is_init = True def _reset_controller(self): super(TutorialDPController, self)._reset_controller() self._error_pose = np.zeros(shape=(6,)) self._int = np.zeros(shape=(6,)) def update_controller(self): if not self._is_init: return False if not self.odom_is_init: return self._int = self._int + 0.5 * (self.error_pose_euler + self._error_pose) * self._dt self._error_pose = self.error_pose_euler tau = np.dot(self._Kp, self.error_pose_euler) + np.dot(self._Kd, self._errors['vel']) + np.dot(self._Ki, self._int) self.publish_control_wrench(tau) if __name__ == '__main__': print('Tutorial - DP Controller') rospy.init_node('tutorial_dp_controller') try: node = TutorialDPController() rospy.spin() except rospy.ROSInterruptException: print('caught exception') print('exiting')学习这个代码,设计一个python3环境下的nmpc控制器,若用到相同的变量名还保持pid中的变量名,kp、ki、kd不用保留,用nmpc中的q矩阵就行
05-14
import numpy as np class ComputationGraphFunction: def __init__(self, inputs, outcomes, parameters, prediction, objective): """ Parameters: inputs: list of ValueNode objects containing inputs (in the ML sense) outcomes: list of ValueNode objects containing outcomes (in the ML sense) parameters: list of ValueNode objects containing values we will optimize over prediction: node whose 'out' variable contains our prediction objective: node containing the objective for which we compute the gradient """ self.inputs = inputs self.outcomes = outcomes self.parameters = parameters self.prediction = prediction self.objective = objective # Create name to node lookup, so users can just supply node_name to set parameters self.name_to_node = {} self.name_to_node[self.prediction.node_name] = self.prediction self.name_to_node[self.objective.node_name] = self.objective for node in self.inputs + self.outcomes + self.parameters: self.name_to_node[node.node_name] = node # Precompute the topological and reverse topological sort of the nodes self.objective_node_list_forward = sort_topological(self.objective) self.objective_node_list_backward = sort_topological(self.objective) self.objective_node_list_backward.reverse() self.prediction_node_list_forward = sort_topological(self.prediction) def __set_values__(self, node_values): for node_name in node_values: node = self.name_to_node[node_name] node.out = node_values[node_name] def set_parameters(self, parameter_values): self.__set_values__(parameter_values) def increment_parameters(self, parameter_steps): for node_name in parameter_steps: node = self.name_to_node[node_name] node.out += parameter_steps[node_name] def get_objective(self, input_values, outcome_values): self.__set_values__(input_values) self.__set_values__(outcome_values) obj = forward_graph(self.objective, node_list=self.objective_node_list_forward) return obj def get_gradients(self, input_values, outcome_values): obj = self.get_objective(input_values, outcome_values) #need forward pass anyway #print("backward node list: ",self.objective_node_list_backward) backward_graph(self.objective, node_list=self.objective_node_list_backward) parameter_gradients = {} for node in self.parameters: parameter_gradients[node.node_name] = node.d_out return obj, parameter_gradients def get_prediction(self, input_values): self.__set_values__(input_values) pred = forward_graph(self.prediction, node_list=self.prediction_node_list_forward) return pred ###### Computation graph utilities def sort_topological(sink): """Returns a list of the sink node and all its ancestors in topologically sorted order. Subgraph of these nodes must form a DAG.""" L = [] # Empty list that will contain the sorted nodes T = set() # Set of temporarily marked nodes P = set() # Set of permanently marked nodes def visit(node): if node in P: return if node in T: raise 'Your graph is not a DAG!' T.add(node) # mark node temporarily for predecessor in node.get_predecessors(): visit(predecessor) P.add(node) # mark node permanently L.append(node) visit(sink) return L def forward_graph(graph_output_node, node_list=None): # If node_list is not None, it should be sort_topological(graph_output_node) if node_list is None: node_list = sort_topological(graph_output_node) for node in node_list: out = node.forward() return out def backward_graph(graph_output_node, node_list=None): """ If node_list is not None, it should be the reverse of sort_topological(graph_output_node). Assumes that forward_graph has already been called on graph_output_node. Sets d_out of each node to the appropriate derivative. """ if node_list is None: node_list = sort_topological(graph_output_node) node_list.reverse() graph_output_node.d_out = np.array(1) # Derivative of graph output w.r.t. itself is 1 for node in node_list: node.backward() import numpy as np class ValueNode(object): """计算图中没有输入的节点,仅持有一个值""" def __init__(self, node_name): self.node_name = node_name self.out = None self.d_out = None def forward(self): self.d_out = np.zeros(self.out.shape) return self.out def backward(self): pass def get_predecessors(self): return [] class VectorScalarAffineNode(object): """计算一个将向量映射到标量的仿射函数的节点""" def __init__(self, x, w, b, node_name): """ 参数: x: 节点,其 `x.out` 是一个一维的 numpy 数组 w: 节点,其 `w.out` 是一个与 `x.out` 相同大小的一维 numpy 数组 b: 节点,其 `b.out` 是一个 numpy 标量(即 0 维数组) node_name: 节点的名称(字符串) """ self.node_name = node_name self.out = None self.d_out = None self.x = x self.w = w self.b = b def forward(self): self.out = np.dot(self.x.out, self.w.out) + self.b.out self.d_out = np.zeros(self.out.shape) return self.out def backward(self): d_x = self.d_out * self.w.out d_w = self.d_out * self.x.out d_b = self.d_out self.x.d_out += d_x self.w.d_out += d_w self.b.d_out += d_b def get_predecessors(self): return [self.x, self.w, self.b] class SquaredL2DistanceNode(object): """ 计算两个数组之间的 L2 距离(平方差之)的节点""" def __init__(self, a, b, node_name): """ 参数: a: 节点,其 `a.out` 是一个 numpy 数组 b: 节点,其 `b.out` 是一个与 `a.out` 形状相同的 numpy 数组 node_name: 节点的名称(字符串) """ self.node_name = node_name self.out = None self.d_out = None self.a = a self.b = b self.a_minus_b = None def forward(self): self.a_minus_b = self.a.out - self.b.out self.out = np.sum(self.a_minus_b ** 2) self.d_out = np.zeros(self.out.shape) #此处为初始化,下同 return self.out def backward(self): d_a = self.d_out * 2 * self.a_minus_b d_b = -self.d_out * 2 * self.a_minus_b self.a.d_out += d_a self.b.d_out += d_b return self.d_out def get_predecessors(self): return [self.a, self.b] class L2NormPenaltyNode(object): """ 计算 l2_reg * ||w||^2 节点,其中 l2_reg 为标量, w为向量""" def __init__(self, l2_reg, w, node_name): """ 参数: l2_reg: 一个大于等于0的标量值(不是节点) w: 节点,其 w.out 是一个 numpy 向量 node_name: 节点的名称(字符串) """ self.node_name = node_name self.out = None self.d_out = None self.l2_reg = np.array(l2_reg) self.w = w def forward(self): self.out = self.l2_reg * np.sum(self.w.out ** 2) self.d_out = np.zeros(self.out.shape) return self.out def backward(self): d_w = self.d_out * 2 * self.l2_reg * self.w.out self.w.d_out += d_w return self.d_out def get_predecessors(self): return [self.w] ## TODO ## Hint:实现对应的forward,backword,get_predecessors接口即可 class SumNode(object): """ 计算 a + b 的节点,其中 a b 是 numpy 数组。""" def __init__(self, a, b, node_name): """ 参数: a: 节点,其 a.out 是一个 numpy 数组 b: 节点,其 b.out 是一个与 a 的形状相同的 numpy 数组 node_name: 节点的名称(一个字符串) """ self.node_name = node_name self.out = None self.d_out = None self.a = a self.b = b def forward(self): self.out = self.a.out + self.b.out self.d_out = np.zeros(self.out.shape) return self.out def backward(self): d_a = self.d_out * np.ones_like(self.a.out) d_b = self.d_out * np.ones_like(self.b.out) self.a.d_out += d_a self.b.d_out += d_b return self.d_out def get_predecessors(self): return [self.a, self.b] ## TODO ## Hint:实现对应的forward,backword,get_predecessors接口即可 class AffineNode(object): """实现仿射变换 (W,x,b)-->Wx+b 的节点,其中 W 是一个矩阵,x b 是向量 参数: W: 节点,其 W.out 是形状为 (m,d) 的 numpy 数组 x: 节点,其 x.out 是形状为 (d) 的 numpy 数组 b: 节点,其 b.out 是形状为 (m) 的 numpy 数组(即长度为 m 的向量) """ def __init__(self, W, x, b, node_name): self.node_name = node_name self.out = None self.d_out = None self.W = W self.x = x self.b = b def forward(self): self.out = np.dot(self.W.out, self.x.out) + self.b.out self.d_out = np.zeros(self.out.shape) return self.out def backward(self): d_W = np.outer(self.d_out, self.x.out) d_x = np.dot(self.W.out.T, self.d_out) d_b = self.d_out.copy() self.W.d_out += d_W self.x.d_out += d_x self.b.d_out += d_b return self.d_out def get_predecessors(self): return [self.W, self.x, self.b] ## Hint:实现对应的forward,backword,get_predecessors接口即可 class TanhNode(object): """节点 tanh(a),其中 tanh 是对数组 a 逐元素应用的 参数: a: 节点,其 a.out 是一个 numpy 数组 """ def __init__(self, a, node_name): self.node_name = node_name self.out = None self.d_out = None self.a = a def forward(self): self.out = np.tanh(self.a.out) self.d_out = np.zeros(self.out.shape) return self.out def backward(self): d_a = self.d_out * (1 - self.out ** 2) self.a.d_out += d_a return self.d_out def get_predecessors(self): return [self.a] import matplotlib.pyplot as plt import setup_problem from sklearn.base import BaseEstimator, RegressorMixin import numpy as np import nodes import graph import plot_utils #import pdb #pdb.set_trace() #useful for debugging! class MLPRegression(BaseEstimator, RegressorMixin): """ 基于计算图的MLP实现 """ def __init__(self, num_hidden_units=10, step_size=.005, init_param_scale=0.01, max_num_epochs = 5000): self.num_hidden_units = num_hidden_units self.init_param_scale = 0.01 self.max_num_epochs = max_num_epochs self.step_size = step_size # 开始构建计算图 self.x = nodes.ValueNode(node_name="x") # to hold a vector input self.y = nodes.ValueNode(node_name="y") # to hold a scalar response ## TODO ## Hint: 根据PPT中给定的图,来构建MLP def fit(self, X, y): num_instances, num_ftrs = X.shape y = y.reshape(-1) ## TODO: 初始化参数(小的随机数——不是全部为0,以打破对称性) s = self.init_param_scale init_values = None ## TODO,在这里进行初始化,hint:调用np.random.standard_normal方法 self.graph.set_parameters(init_values) for epoch in range(self.max_num_epochs): shuffle = np.random.permutation(num_instances) epoch_obj_tot = 0.0 for j in shuffle: obj, grads = self.graph.get_gradients(input_values = {"x": X[j]}, outcome_values = {"y": y[j]}) #print(obj) epoch_obj_tot += obj # Take step in negative gradient direction steps = {} for param_name in grads: steps[param_name] = -self.step_size * grads[param_name] self.graph.increment_parameters(steps) if epoch % 50 == 0: train_loss = sum((y - self.predict(X,y)) **2)/num_instances print("Epoch ",epoch,": Ave objective=",epoch_obj_tot/num_instances," Ave training loss: ",train_loss) def predict(self, X, y=None): try: getattr(self, "graph") except AttributeError: raise RuntimeError("You must train classifer before predicting data!") num_instances = X.shape[0] preds = np.zeros(num_instances) for j in range(num_instances): preds[j] = self.graph.get_prediction(input_values={"x":X[j]}) return preds def main(): #lasso_data_fname = "lasso_data.pickle" lasso_data_fname = r"C:\Users\XM_Ta\OneDrive\Desktop\1120223544-汤阳光-实验四\Question\lasso_data.pickle" x_train, y_train, x_val, y_val, target_fn, coefs_true, featurize = setup_problem.load_problem(lasso_data_fname) # Generate features X_train = featurize(x_train) X_val = featurize(x_val) # Let's plot prediction functions and compare coefficients for several fits # and the target function. pred_fns = [] x = np.sort(np.concatenate([np.arange(0,1,.001), x_train])) pred_fns.append({"name": "Target Parameter Values (i.e. Bayes Optimal)", "coefs": coefs_true, "preds": target_fn(x)}) estimator = MLPRegression(num_hidden_units=10, step_size=0.001, init_param_scale=.0005, max_num_epochs=5000) x_train_as_column_vector = x_train.reshape(x_train.shape[0],1) # fit expects a 2-dim array x_as_column_vector = x.reshape(x.shape[0],1) # fit expects a 2-dim array estimator.fit(x_train_as_column_vector, y_train) name = "MLP regression - no features" pred_fns.append({"name":name, "preds": estimator.predict(x_as_column_vector) }) X = featurize(x) estimator = MLPRegression(num_hidden_units=10, step_size=0.0005, init_param_scale=.01, max_num_epochs=500) estimator.fit(X_train, y_train) name = "MLP regression - with features" pred_fns.append({"name":name, "preds": estimator.predict(X) }) plot_utils.plot_prediction_functions(x, pred_fns, x_train, y_train, legend_loc="best") if __name__ == '__main__': main() 请帮我补充完整以上代码
05-29
import numpy as np import pandas as pd from scipy.optimize import minimize, Bounds import matplotlib.pyplot as plt class NIPTOptimizer: def __init__(self, df, num_groups=6): """ 初始化NIPT优化器 参数: df: 包含所有数据的DataFrame num_groups: BMI分组数量 """ self.df = df.copy() self.num_groups = num_groups self.num_bmi_splits = num_groups - 1 # 获取决策变量的上下界 self.BMI_min = df.iloc[:, 10].min() # 假设第11列是BMI self.BMI_max = df.iloc[:, 10].max() self.TIME_min = df.iloc[:, 9].min() # 假设第10列是孕周 self.TIME_max = df.iloc[:, 9].max() # 设置变量上下界 self.bounds = self._setup_bounds() # 设置约束条件 self.constraints = self._setup_constraints() def _setup_bounds(self): """设置决策变量的上下界""" bounds = [] # BMI分割点的上下界 for _ in range(self.num_bmi_splits): bounds.append((self.BMI_min, self.BMI_max)) # 时间区间的上下界 for _ in range(self.num_groups): bounds.append((self.TIME_min, self.TIME_max)) # 时间下界 bounds.append((self.TIME_min, self.TIME_max)) # 时间上界 return bounds def _setup_constraints(self): """设置优化约束条件""" constraints = [] # 约束1: BMI分割点必须有序 (b1 ≤ b2 ≤ ... ≤ b_{k-1}) for i in range(self.num_bmi_splits - 1): def constraint_func(x, idx=i): return x[idx+1] - x[idx] constraints.append({'type': 'ineq', 'fun': constraint_func}) # 约束2: 时间下界必须小于时间上界 (l_i ≤ u_i - ε) ε = 1e-3 for i in range(self.num_groups): def constraint_func(x, idx=i): l_idx = self.num_bmi_splits + 2*idx u_idx = self.num_bmi_splits + 2*idx + 1 return x[u_idx] - x[l_idx] - ε constraints.append({'type': 'ineq', 'fun': constraint_func}) return constraints def _assign_groups(self, x): """根据决策变量x分配BMI分组""" # 提取BMI分割点 bmi_splits = x[:self.num_bmi_splits] # 创建BMI分组 bmi_bins = np.concatenate([[-np.inf], np.sort(bmi_splits), [np.inf]]) self.df['bmi_group'] = pd.cut(self.df.iloc[:, 10], bins=bmi_bins) # 提取时间区间 time_intervals = [] for i in range(self.num_groups): l_idx = self.num_bmi_splits + 2*i u_idx = self.num_bmi_splits + 2*i + 1 time_intervals.append((x[l_idx], x[u_idx])) return time_intervals def _calculate_risk(self, t): """根据检测时间计算风险值""" if t <= 12: return 1 # 早期风险低 elif t <= 27: return 2 # 中期风险高 else: return 3 # 晚期风险极高 def _calculate_accuracy(self, group_df, t_mid): """计算给定分组时点的准确性""" # 这里简化处理:使用该组在当前时点附近的平均准确率 # 实际应用中需要更复杂的模型来预测不同时点的准确率 # 筛选在时点t_mid附近的数据点 time_window = 2 # 周 time_filtered = group_df[ (group_df.iloc[:, 9] >= t_mid - time_window) & (group_df.iloc[:, 9] <= t_mid + time_window) ] if len(time_filtered) == 0: # 如果没有附近的数据点,使用整个组的平均准确率 accuracy = group_df['Correct'].mean() if 'Correct' in group_df.columns else 0.95 else: # 使用附近数据点的平均准确率 accuracy = time_filtered['Correct'].mean() if 'Correct' in time_filtered.columns else 0.95 return accuracy def objective_function(self, x): """目标函数:最小化风险,最大化准确性""" # 分配BMI分组时间区间 time_intervals = self._assign_groups(x) obj1 = 0 # 风险总 obj2 = 0 # 准确性总 total_samples = 0 # 计算每个分组的风险准确性 for i in range(self.num_groups): group_df = self.df[self.df['bmi_group'].cat.codes == i] if len(group_df) == 0: continue # 获取该组的时间区间 t_low, t_high = time_intervals[i] t_mid = (t_low + t_high) / 2 # 使用时间区间中点作为代表时点 # 计算该组的风险 group_risk = self._calculate_risk(t_mid) obj1 += group_risk * len(group_df) # 计算该组的准确性 accuracy = self._calculate_accuracy(group_df, t_mid) obj2 += accuracy * len(group_df) total_samples += len(group_df) # 计算平均值 if total_samples > 0: obj1 /= total_samples # 平均风险 obj2 /= total_samples # 平均准确率 # 组合目标:最小化风险,最大化准确性 # 使用加权法,权重可以根据需要调整 weight_risk = 0.7 weight_accuracy = 0.3 return weight_risk * obj1 - weight_accuracy * obj2 def optimize(self, method='SLSQP', max_iter=1000): """执行优化""" # 初始猜测:均匀分布的BMI分割点时间区间 x0 = np.zeros(self.num_bmi_splits + 2*self.num_groups) # BMI分割点初始值 bmi_range = np.linspace(self.BMI_min, self.BMI_max, self.num_groups+1) bmi_splits_uniform = bmi_range[1:-1] # 添加随机扰动(如±10%范围内的随机变化) perturbation = np.random.uniform(low=-0.1, high=0.1, size=len(bmi_splits_uniform)) bmi_splits_nonuniform = bmi_splits_uniform * (1 + perturbation) # 将非均匀分割点赋值给x0 x0[:self.num_bmi_splits] = bmi_splits_nonuniform # 时间区间初始值 time_mid = (self.TIME_min + self.TIME_max) / 2 time_half_range = (self.TIME_max - self.TIME_min) / (2 * self.num_groups) for i in range(self.num_groups): l_idx = self.num_bmi_splits + 2*i u_idx = self.num_bmi_splits + 2*i + 1 # 设置初始时间区间 x0[l_idx] = time_mid - time_half_range x0[u_idx] = time_mid + time_half_range # 执行优化 result = minimize( self.objective_function, x0, method=method, bounds=self.bounds, constraints=self.constraints, options={'maxiter': max_iter} ) return result def interpret_result(self, x): """解释优化结果""" # 提取BMI分割点 bmi_splits = x[:self.num_bmi_splits] bmi_bins = np.concatenate([[-np.inf], np.sort(bmi_splits), [np.inf]]) # 提取时间区间 time_intervals = [] for i in range(self.num_groups): l_idx = self.num_bmi_splits + 2*i u_idx = self.num_bmi_splits + 2*i + 1 time_intervals.append((x[l_idx], x[u_idx])) # 计算每个分组的统计信息 self._assign_groups(x) results = [] for i in range(self.num_groups): group_df = self.df[self.df['bmi_group'].cat.codes == i] if len(group_df) == 0: continue # 获取该组的时间区间 t_low, t_high = time_intervals[i] t_mid = (t_low + t_high) / 2 # 计算该组的风险 risk = self._calculate_risk(t_mid) # 计算该组的准确性 accuracy = self._calculate_accuracy(group_df, t_mid) # 获取BMI区间 bmi_low = bmi_bins[i] bmi_high = bmi_bins[i+1] results.append({ 'group': i+1, 'bmi_range': f"[{bmi_low:.1f}, {bmi_high:.1f})", 'time_interval': f"[{t_low:.1f}, {t_high:.1f}]", 'recommended_time': f"{t_mid:.1f}", 'risk_level': risk, 'estimated_accuracy': f"{accuracy:.3f}", 'sample_size': len(group_df) }) return pd.DataFrame(results) # 使用示例 if __name__ == "__main__": # 假设df是已经加载的数据框 # 需要包含BMI列(索引10)、孕周列(索引9)Correct列(准确率) # 创建优化器实例 optimizer = NIPTOptimizer(df, num_groups=5) # 执行优化 result = optimizer.optimize(method='SLSQP', max_iter=200) if result.success: print("优化成功!") print(f"目标函数值: {result.fun:.4f}") # 解释结果 results_df = optimizer.interpret_result(result.x) print("\n优化结果:") print(results_df.to_string(index=False)) # 可视化结果 plt.figure(figsize=(10, 6)) plt.bar(range(len(results_df)), results_df['sample_size']) plt.xlabel('BMI分组') plt.ylabel('样本数量') plt.title('各BMI分组的样本分布') plt.xticks(range(len(results_df)), results_df['bmi_range'], rotation=45) plt.tight_layout() plt.show() else: print("优化失败!") print(result.message) 这是遗传算法非线性规划的结合吗
最新发布
09-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值