diff --git a/app/eva_common.py b/app/eva_common.py index 635bf70..23b97e8 100644 --- a/app/eva_common.py +++ b/app/eva_common.py @@ -785,9 +785,9 @@ def gen_flow_turn_rate_index(avg_cross_delay_info, roads_dir_dict): split_turns_set.add(1) car_num = road_delay_dict[roadid].delay_info.turn_ratio_1 + road_delay_dict[roadid].delay_info.turn_ratio_0 + road_delay_dict[roadid].delay_info.turn_ratio_2 in_flow_rate = int(car_num / cross_sum_car_num * 100) if cross_out_sum_car_num != 0 else 0 - l_rate = int(road_delay_dict[roadid].delay_info.turn_ratio_1 / car_num * 100) if car_num != 0 else 0 - s_rate = int(road_delay_dict[roadid].delay_info.turn_ratio_0 / car_num * 100) if car_num != 0 else 0 - r_rate = int(road_delay_dict[roadid].delay_info.turn_ratio_2 / car_num * 100) if car_num != 0 else 0 + l_rate = max(1, round(road_delay_dict[roadid].delay_info.turn_ratio_1 / car_num * 100)) if car_num != 0 else 0 + s_rate = max(1, round(road_delay_dict[roadid].delay_info.turn_ratio_0 / car_num * 100)) if car_num != 0 else 0 + r_rate = max(1, round(road_delay_dict[roadid].delay_info.turn_ratio_2 / car_num * 100)) if car_num != 0 else 0 if 0 not in split_turns_set: s_rate = '-' if 1 not in split_turns_set: @@ -818,9 +818,9 @@ def gen_flow_turn_rate_index(avg_cross_delay_info, roads_dir_dict): merge_turns_set.add(1) out_car_num = outroad_info_dict[out_road_id].turn_info.car_num out_flow_rate = int(out_car_num / cross_out_sum_car_num * 100) if cross_out_sum_car_num != 0 else 0 - out_l_rate = int(outroad_info_dict[out_road_id].turn_info.turn_ratio_1 / out_car_num * 100) if out_car_num != 0 else 0 - out_s_rate = int(outroad_info_dict[out_road_id].turn_info.turn_ratio_0 / out_car_num * 100) if out_car_num != 0 else 0 - out_r_rate = int(outroad_info_dict[out_road_id].turn_info.turn_ratio_2 / out_car_num * 100) if out_car_num != 0 else 0 + out_l_rate = max(1, round(outroad_info_dict[out_road_id].turn_info.turn_ratio_1 / out_car_num * 100)) if out_car_num != 0 else 0 + out_s_rate = max(1, round(outroad_info_dict[out_road_id].turn_info.turn_ratio_0 / out_car_num * 100)) if out_car_num != 0 else 0 + out_r_rate = max(1, round(outroad_info_dict[out_road_id].turn_info.turn_ratio_2 / out_car_num * 100)) if out_car_num != 0 else 0 if 0 not in merge_turns_set: out_s_rate = '-' if 1 not in merge_turns_set: @@ -2254,22 +2254,93 @@ def get_prev_cross(nodeid, area_id, roads_dir_dict): return src_cross -def _to_int_list(nums, mask): - """把非'-'位转成int,再微调±1使总和=100,只动最大位。""" - # 先全部转int - nums = [int(v) if not mask[i] else v for i, v in enumerate(nums)] - # 有效位下标 - valid = [i for i, m in enumerate(mask) if not m] - if len(valid) < 2: # 单有效或全'-'无需调 - return nums - s = sum(nums[i] for i in valid) - if s == 100: - return nums - # 差±1 - delta = s - 100 - idx_big = max(valid, key=nums.__getitem__) # 仍只动最大位 - nums[idx_big] -= delta - return nums +import random + +def _to_int_list(float_nums, mask): + """ + 将非'-'的浮点数转为整数,满足: + - 总和 = 100 + - 原始值 > 0 的位置结果至少为 1 + - 尽量保持比例 + """ + n = len(float_nums) + result = [0] * n + valid_indices = [i for i in range(n) if not mask[i]] + + if len(valid_indices) == 0: + return ['-' if m else 0 for m in mask] + if len(valid_indices) == 1: + result[valid_indices[0]] = 100 + for i in range(n): + if mask[i]: + result[i] = '-' + return result + + # Step 1: 初始 round + rounded = [] + remainders = [] + total = 0 + for i in valid_indices: + val = float_nums[i] + r = int(round(val)) + # 保证正数至少为1 + if r == 0 and val > 0: + r = 1 + rounded.append(r) + remainders.append(val - int(val)) # 小数部分,用于后续调整 + total += r + + # 构建初始结果 + for idx, i in enumerate(valid_indices): + result[i] = rounded[idx] + + delta = total - 100 + if delta == 0: + # 补充 '-' + for i in range(n): + if mask[i]: + result[i] = '-' + return result + + # Step 2: 调整 delta + # 如果 delta > 0:需要减掉 delta(从“最不重要”的位置减) + # 如果 delta < 0:需要加上 |delta| + # 策略:按小数部分排序(或按值大小),优先调整“对比例影响最小”的项 + + # 创建可调整项列表:(index_in_valid, original_value, remainder) + adjust_list = [] + for idx, i in enumerate(valid_indices): + val = float_nums[i] + # 只允许调整到 >=1 + can_decrease = result[i] > 1 + can_increase = True # 总是可以加 + adjust_list.append((idx, val, remainders[idx], can_decrease)) + + if delta > 0: + # 需要减少 delta + # 按小数部分升序(小数越小,越该减),或按值升序 + adjust_list.sort(key=lambda x: x[2]) # 按 remainder 升序 + for _ in range(delta): + for item in adjust_list: + idx_in_valid = item[0] + if result[valid_indices[idx_in_valid]] > 1: + result[valid_indices[idx_in_valid]] -= 1 + break + else: + # delta < 0,需要增加 |delta| + # 按小数部分降序(小数越大,越该加) + adjust_list.sort(key=lambda x: x[2], reverse=True) + for _ in range(-delta): + for item in adjust_list: + idx_in_valid = item[0] + result[valid_indices[idx_in_valid]] += 1 + break + + # 补充 '-' + for i in range(n): + if mask[i]: + result[i] = '-' + return result def fix_to_100(a, b, c): @@ -2277,63 +2348,57 @@ def fix_to_100(a, b, c): mask = [v == '-' for v in nums] valid_idx = [i for i, m in enumerate(mask) if not m] - # ----- 全 '-' ----- if not valid_idx: - return nums[:] + return ['-', '-', '-'] - # ----- 仅 1 个有效 ----- if len(valid_idx) == 1: - nums[valid_idx[0]] = 100 - return _to_int_list(nums, mask) + res = ['-', '-', '-'] + res[valid_idx[0]] = 100 + return res - # ----- 2 个有效 ----- if len(valid_idx) == 2: i0, i1 = valid_idx v0, v1 = nums[i0], nums[i1] - # 只有当两个值恰好是 {0, 100} 时才用特殊处理 if {v0, v1} == {0, 100}: r1 = random.randint(1, 3) r2 = 100 - r1 + res = ['-', '-', '-'] if v0 == 100: - nums[i0] = max(r1, r2) - nums[i1] = min(r1, r2) + res[i0] = max(r1, r2) + res[i1] = min(r1, r2) else: - nums[i1] = max(r1, r2) - nums[i0] = min(r1, r2) - return _to_int_list(nums, mask) + res[i1] = max(r1, r2) + res[i0] = min(r1, r2) + return res else: - # 其他情况:正常归一化(包括 100+50, 0+50 等) s = v0 + v1 if s == 0: - nums[i0] = 50 - nums[i1] = 50 + scaled = [50.0, 50.0] else: - nums[i0] = v0 * 100 / s - nums[i1] = v1 * 100 / s - return _to_int_list(nums, mask) + scaled = [v0 * 100 / s, v1 * 100 / s] + float_result = ['-', '-', '-'] + float_result[i0] = scaled[0] + float_result[i1] = scaled[1] + return _to_int_list(float_result, mask) # ----- 3 个有效 ----- - # 特殊处理:仅当存在一个 100 且其余两个为 0 时 if 100 in nums: zeros = [i for i in range(3) if nums[i] == 0] if len(zeros) == 2: - # 确认为 [100, 0, 0] 的某种排列 r1 = random.randint(1, 3) r2 = 100 - r1 - nums[zeros[0]] = r1 - nums[zeros[1]] = r2 - # 100 位置保持不变 - return _to_int_list(nums, mask) + res = list(nums) + res[zeros[0]] = r1 + res[zeros[1]] = r2 + return _to_int_list(res, mask) - # 所有其他情况(包括含 0 但无 100,如 [0, 98, 0])→ 正常归一化 + # 正常缩放(包括 [1,99,1]) s = sum(nums) if s == 0: - # 全为 0,平均分配(或按需调整) - nums = [34, 33, 33] + float_result = [33.33, 33.33, 33.33] else: - for i in range(3): - nums[i] = nums[i] * 100 / s - return _to_int_list(nums, mask) + float_result = [x * 100 / s for x in nums] + return _to_int_list(float_result, mask) def query_cross_delay_info_controller_export_excel(road_flow_delay_infos, road_flow_turn_rate):