multi_cases_evaluate.py 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ##################################################################
  4. #
  5. # Copyright (c) 2023 CICV, Inc. All Rights Reserved
  6. #
  7. ##################################################################
  8. """
  9. @Authors: yangzihao(yangzihao@china-icv.cn)
  10. @Data: 2023/11/28
  11. @Last Modified: 2023/11/28
  12. @Summary: Evaluate multiple cases.
  13. """
  14. import os
  15. import sys
  16. import pandas as pd
  17. import numpy as np
  18. import requests
  19. import json
  20. import pathlib
  21. import time
  22. import traceback
  23. import log
  24. from common import json2dict, score_grade, mileage_format, duration_format, string_concatenate, replace_key_with_value
  25. from score import cal_score_from_80
  26. def calculate_ratios(grade_count_list):
  27. """
  28. This function helps to calculate the ratios of the grade in grade_count_list.
  29. Arguments:
  30. grade_count_list: A list of the number of grades.
  31. Returns:
  32. ratios: A list of the ratios of the grade in grade_count_list.
  33. """
  34. arr = np.array(grade_count_list)
  35. total = arr.sum()
  36. ratios = np.round(arr / total * 100).astype(int).tolist()
  37. # 确保四个比例的和为100
  38. if sum(ratios) != 100:
  39. ratios[0] += 100 - sum(ratios)
  40. return ratios
  41. def grade_statistic(grade_list):
  42. """
  43. This function statistic the number of grades in grade_list.
  44. Arguments:
  45. grade_list: A list of grades.
  46. Returns:
  47. grade_distribution: A dict of the distribution of 4 grades.
  48. """
  49. a_grade_count = len([x for x in grade_list if x == "优秀"])
  50. b_grade_count = len([x for x in grade_list if x == "良好"])
  51. c_grade_count = len([x for x in grade_list if x == "一般"])
  52. d_grade_count = len([x for x in grade_list if x == "较差"])
  53. grade_count_list = [a_grade_count, b_grade_count, c_grade_count, d_grade_count]
  54. ratios = calculate_ratios(grade_count_list)
  55. grade_distribution = {
  56. "优秀": ratios[0],
  57. "良好": ratios[1],
  58. "一般": ratios[2],
  59. "较差": ratios[3]
  60. }
  61. return grade_distribution
  62. def custom_metric_param_list_parser(param_list):
  63. """
  64. param_dict = {
  65. "paramA" [
  66. {
  67. "kind": "-1",
  68. "optimal": "1",
  69. "multiple": ["0.5","5"],
  70. "spare1": null,
  71. "spare2": null
  72. }
  73. ]
  74. }
  75. """
  76. kind_list = []
  77. optimal_list = []
  78. multiple_list = []
  79. spare_list = []
  80. # spare1_list = []
  81. # spare2_list = []
  82. for i in range(len(param_list)):
  83. kind_list.append(int(param_list[i]['kind']))
  84. optimal_list.append(float(param_list[i]['optimal']))
  85. multiple_list.append([float(x) for x in param_list[i]['multiple']])
  86. spare_list.append([item["param"] for item in param_list[i]["spare"]])
  87. # spare1_list.append(param_list[i]['spare1'])
  88. # spare2_list.append(param_list[i]['spare2'])
  89. result = {
  90. "kind": kind_list,
  91. "optimal": optimal_list,
  92. "multiple": multiple_list,
  93. "spare": spare_list,
  94. # "spare1": spare1_list,
  95. # "spare2": spare2_list
  96. }
  97. return result
  98. def custom_metric_param_parser(customMetricParam):
  99. result = {}
  100. for key, param_list in customMetricParam.items():
  101. result[key] = custom_metric_param_list_parser(param_list)
  102. return result
  103. def custom_multi_case_statistic(custom_cases_dict, config, dimension):
  104. """
  105. Args:
  106. custom_cases_dict:
  107. config:
  108. dimension:
  109. Returns:
  110. """
  111. df_custom_cases = pd.DataFrame(custom_cases_dict).T.dropna(how='all')
  112. # common data
  113. bulitin_metric_list = config.builtinMetricList
  114. # config infos
  115. dimension_config = config.config[dimension]
  116. metric_list = dimension_config['metric']
  117. type_list = dimension_config['type']
  118. type_name_dict = dimension_config['typeName']
  119. name_dict = dimension_config['name']
  120. unit_dict = dimension_config['unit']
  121. metric_dict = dimension_config['typeMetricDict']
  122. dimension_name_dict = config.dimension_name
  123. # # custom metric data
  124. customMetricParam = dimension_config['customMetricParam']
  125. # custom_data = custom_data
  126. custom_param_dict = custom_metric_param_parser(customMetricParam)
  127. weight_custom = float(dimension_config['weightDimension'])
  128. # optimal_dict = dimension_config['optimal']
  129. # kind_dict = dimension_config['kind']
  130. # multiple_dict = dimension_config['multiple']
  131. report_dict = {
  132. "name": f"{dimension_name_dict[dimension]}",
  133. "weight": f"{weight_custom * 100}%",
  134. }
  135. # calculate score_custom and grade_custom
  136. score_custom_list = df_custom_cases['score'].values.tolist()
  137. score_custom = cal_score_from_80(score_custom_list)
  138. grade_custom = score_grade(score_custom)
  139. report_dict["score"] = score_custom
  140. report_dict["level"] = grade_custom
  141. report_dict["scoreList"] = score_custom_list
  142. # calculate grade_distribution
  143. grade_custom_list = df_custom_cases['level'].values.tolist()
  144. grade_custom_distribution = grade_statistic(grade_custom_list)
  145. report_dict["levelDistribution"] = grade_custom_distribution
  146. score_type_dict = {}
  147. value_list_dict = {}
  148. bad_count_dict = {}
  149. good_rate_dict = {}
  150. bad_rate_dict = {}
  151. type_details_dict = {}
  152. for type in type_list:
  153. type_dict = {
  154. "name": type_name_dict[type],
  155. }
  156. builtin_graph_dict = {}
  157. custom_graph_dict = {}
  158. df_custom_cases[type] = df_custom_cases["details"].apply(lambda x: x[type] if type in x.keys() else None)
  159. df_custom_cases1 = df_custom_cases.dropna(subset=[type])
  160. type_cases_dict = df_custom_cases1[type].to_dict()
  161. df_type_cases = pd.DataFrame(type_cases_dict).T.dropna(how='all')
  162. # calculate score_type and grade_type
  163. score_type_list = df_type_cases['score'].values.tolist()
  164. score_type = round(np.mean(score_type_list), 2)
  165. grade_type = score_grade(score_type)
  166. type_dict["score"] = score_type
  167. type_dict["level"] = grade_type
  168. score_type_dict[type] = score_type
  169. # calculate grade_distribution
  170. grade_type_list = df_type_cases['level'].values.tolist()
  171. grade_type_distribution = grade_statistic(grade_type_list)
  172. type_dict["gradeDistribution"] = grade_type_distribution
  173. dfs = df_type_cases['indexes'].apply(lambda x: pd.DataFrame(x).T)
  174. df_type_indexes = pd.concat(dfs.tolist(), ignore_index=True)
  175. # functionACC description
  176. type_metric1_list = [] # good
  177. type_metric2_list = [] # not good
  178. type_metric3_list = [] # bad
  179. # indexes
  180. type_dict_indexes = {}
  181. for metric in metric_dict[type]:
  182. df_metric_indexes = df_type_indexes[df_type_indexes['name'] == f"{name_dict[metric]}"]
  183. kind_metric = custom_param_dict[metric]['kind'][0]
  184. optimal_metric = custom_param_dict[metric]['optimal'][0]
  185. multiple_metric0 = custom_param_dict[metric]['multiple'][0][0]
  186. multiple_metric1 = custom_param_dict[metric]['multiple'][0][1]
  187. if kind_metric == 1: # 越大越好
  188. metric_value_list = df_metric_indexes['min'].astype(str).values.tolist()
  189. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  190. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  191. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  192. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  193. if not optimal_metric:
  194. metric_bad_count = 0
  195. else:
  196. metric_bad_count = len([x for x in metric_value_list if x < float(optimal_metric)])
  197. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  198. metric_good_rate = round(100 - metric_bad_rate, 2)
  199. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  200. metric) if avg_metric_value > float(optimal_metric) else type_metric3_list.append(metric)
  201. elif kind_metric == -1: # 越小越好
  202. metric_value_list = df_metric_indexes['max'].astype(str).values.tolist()
  203. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  204. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  205. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  206. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  207. if not optimal_metric:
  208. metric_bad_count = 0
  209. else:
  210. metric_bad_count = len([x for x in metric_value_list if x > float(optimal_metric)])
  211. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  212. metric_good_rate = round(100 - metric_bad_rate, 2)
  213. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  214. metric) if avg_metric_value < float(optimal_metric) else type_metric3_list.append(metric)
  215. elif kind_metric == 0:
  216. metric_value_list = df_metric_indexes['avg'].astype(str).values.tolist()
  217. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  218. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  219. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  220. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  221. if not optimal_metric:
  222. metric_bad_count = 0
  223. else:
  224. metric_bad_count = len([x for x in metric_value_list if (
  225. x > float(optimal_metric) * multiple_metric1 or x < float(
  226. optimal_metric) * multiple_metric0)])
  227. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  228. metric_good_rate = round(100 - metric_bad_rate, 2)
  229. if not optimal_metric:
  230. type_metric1_list.append(metric)
  231. else:
  232. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(metric) if (
  233. float(optimal_metric) * multiple_metric1 > avg_metric_value > float(
  234. optimal_metric) * multiple_metric0) else type_metric3_list.append(
  235. metric)
  236. type_dict_indexes[metric] = {
  237. "name": f"{name_dict[metric]}",
  238. "average": avg_metric_value,
  239. "max": max_metric_value,
  240. "min": min_metric_value,
  241. }
  242. if not optimal_metric:
  243. type_dict_indexes[metric]["range"] = f"-"
  244. else:
  245. if kind_metric == -1:
  246. type_dict_indexes[metric]["range"] = f"[0, {optimal_metric}]"
  247. elif kind_metric == 1:
  248. type_dict_indexes[metric]["range"] = f"[{optimal_metric}, inf)"
  249. elif kind_metric == 0:
  250. type_dict_indexes[metric][
  251. "range"] = f"[{float(optimal_metric) * multiple_metric0}, {float(optimal_metric) * multiple_metric1}]"
  252. value_list_dict[metric] = metric_value_list
  253. bad_count_dict[metric] = metric_bad_count
  254. good_rate_dict[metric] = metric_good_rate
  255. bad_rate_dict[metric] = metric_bad_rate
  256. type_dict["indexes"] = type_dict_indexes
  257. for metric in metric_dict[type]:
  258. metric_data = {
  259. "name": f"{name_dict[metric]}",
  260. "data": value_list_dict[metric],
  261. "markLine": [custom_param_dict[metric]['optimal'][0]]
  262. }
  263. custom_graph_dict[metric] = metric_data
  264. type_dict["builtin"] = builtin_graph_dict
  265. type_dict["custom"] = custom_graph_dict
  266. # description
  267. str_type_metric1 = ''
  268. str_type_metric2 = ''
  269. str_type_metric3 = ''
  270. if len(type_metric1_list) == len(metric_list):
  271. str_metric = string_concatenate(metric_list)
  272. type_description1 = f"{str_metric}指标均表现良好,平均值在合理范围内且不存在不合格用例。"
  273. elif len(type_metric3_list) == len(metric_list):
  274. str_metric = string_concatenate(metric_list)
  275. type_description1 = f"{str_metric}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  276. else:
  277. if type_metric1_list:
  278. str_type1 = string_concatenate(type_metric1_list)
  279. str_type_metric1 += f"{str_type1}指标表现良好,平均值在合理范围内且不存在不合格用例;"
  280. for metric in type_metric2_list:
  281. str_type_metric2 += f"{name_dict[metric]}指标表现不佳,存在{bad_count_dict[metric]}个不合格用例,需要改进算法在这些用例中的表现;"
  282. if type_metric3_list:
  283. str_type3 = string_concatenate(type_metric3_list)
  284. str_type_metric3 += f"{str_type3}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  285. type_description1 = ''
  286. type_description1 += (str_type_metric1 + '\n') if str_type_metric1 else ''
  287. type_description1 += (str_type_metric2 + '\n') if str_type_metric2 else ''
  288. type_description1 += str_type_metric3
  289. type_description1 = type_description1[:-1] + "。"
  290. # type_description2 = "经计算可知,"
  291. # for metric in metric_dict[type]:
  292. # type_description2 += f"{metric}指标位于合理区间的占比为{good_rate_dict[metric]}%,"
  293. # type_description2 = type_description2[:-1] + "。"
  294. type_dict["description1"] = replace_key_with_value(type_description1, name_dict)
  295. # type_dict["description2"] = replace_key_with_value(type_description2, name_dict)
  296. type_details_dict[type] = type_dict
  297. report_dict["details"] = type_details_dict
  298. # custom description1
  299. good_type_list = []
  300. bad_type_list = []
  301. for type in type_list:
  302. bad_type_list.append(type) if score_type_dict[type] < 80 else good_type_list.append(type)
  303. # generate custom_description1
  304. good_type_list_count = len(score_custom_list)
  305. over_80_count = len([num for num in score_custom_list if num >= 80])
  306. over_80_proportion = over_80_count / good_type_list_count
  307. below_80_count = good_type_list_count - over_80_count
  308. below_80_proportion = below_80_count / good_type_list_count
  309. below_60_count = len([num for num in score_custom_list if num < 60])
  310. below_60_proportion = below_60_count / good_type_list_count
  311. if grade_custom == '优秀':
  312. custom_description1 = f'算法在本轮测试中的表现优秀;'
  313. elif grade_custom == '良好':
  314. custom_description1 = f'算法在本轮测试中的表现满足设计指标要求。其中有{over_80_count}个用例得分超过80分,占比为{over_80_proportion * 100:.2f}%;'
  315. elif grade_custom == '一般':
  316. str_bad_type = string_concatenate(bad_type_list)
  317. custom_description1 = f'未满足设计指标要求。其中有{below_80_count}个用例得分低于80分,占比为{below_80_proportion * 100:.2f}%,需优化算法在{str_bad_type}上的表现;'
  318. elif grade_custom == '较差':
  319. str_bad_type = string_concatenate(bad_type_list)
  320. custom_description1 = f'未满足设计指标要求。其中有{below_60_count}个用例得分低于60分,占比为{below_60_proportion * 100:.2f}%,需优化算法在{str_bad_type}上的表现;'
  321. # customcient description2
  322. if not bad_type_list:
  323. custom_description2 = f"算法在{dimension_name_dict[dimension]}维度上的表现满足设计指标要求。"
  324. else:
  325. custom_description2 = f"算法在{str_bad_type}的指标需要重点优化。"
  326. report_dict["description1"] = replace_key_with_value(custom_description1, type_name_dict)
  327. report_dict["description2"] = replace_key_with_value(custom_description2, type_name_dict)
  328. return report_dict
  329. def safe_multi_case_statistic(safe_cases_dict, config):
  330. """
  331. Args:
  332. safe_cases_dict:
  333. config:
  334. Returns:
  335. """
  336. # report_dict = {
  337. # "name": "安全性",
  338. # "weight": f"{weight_safe * 100}%",
  339. # "score": score_safe,
  340. # "level": grade_safe,
  341. # "scoreList": score_safe_list,
  342. # "levelDistribution": grade_safe_distribution,
  343. #
  344. # "description1": safe_description1,
  345. # "description2": safe_description2,
  346. # "noObjectCar": False,
  347. #
  348. # "safeTime": time_dict,
  349. # "safeDistance": distance_dict,
  350. # "safeAcceleration": acceleration_dict,
  351. # "safeProbability": probability_dict
  352. # }
  353. df_safe_cases = pd.DataFrame(safe_cases_dict).T.dropna(how='all')
  354. # common data
  355. bulitin_metric_list = config.builtinMetricList
  356. # config infos
  357. dimension_config = config.config["safe"]
  358. metric_list = dimension_config['metric']
  359. type_list = dimension_config['type']
  360. type_name_dict = dimension_config['typeName']
  361. name_dict = dimension_config['name']
  362. unit_dict = dimension_config['unit']
  363. metric_dict = dimension_config['typeMetricDict']
  364. # # custom metric data
  365. customMetricParam = dimension_config['customMetricParam']
  366. # custom_data = custom_data
  367. custom_param_dict = custom_metric_param_parser(customMetricParam)
  368. weight_safe = float(dimension_config['weightDimension'])
  369. optimal_dict = dimension_config['optimal'][0]
  370. kind_dict = dimension_config['kind'][0]
  371. multiple_dict = dimension_config['multiple'][0]
  372. report_dict = {
  373. "name": "安全性",
  374. "weight": f"{weight_safe * 100}%",
  375. "noObjectCar": False,
  376. }
  377. # calculate score_safe and grade_safe
  378. score_safe_list = df_safe_cases['score'].values.tolist()
  379. score_safe = cal_score_from_80(score_safe_list)
  380. grade_safe = score_grade(score_safe)
  381. report_dict["score"] = score_safe
  382. report_dict["level"] = grade_safe
  383. report_dict["scoreList"] = score_safe_list
  384. # calculate grade_distribution
  385. grade_safe_list = df_safe_cases['level'].values.tolist()
  386. grade_safe_distribution = grade_statistic(grade_safe_list)
  387. report_dict["levelDistribution"] = grade_safe_distribution
  388. score_type_dict = {}
  389. value_list_dict = {}
  390. bad_count_dict = {}
  391. good_rate_dict = {}
  392. bad_rate_dict = {}
  393. type_details_dict = {}
  394. for type in type_list:
  395. type_dict = {
  396. "name": type_name_dict[type],
  397. }
  398. builtin_graph_dict = {}
  399. custom_graph_dict = {}
  400. df_safe_cases[type] = df_safe_cases["details"].apply(lambda x: x[type] if type in x.keys() else None)
  401. df_safe_cases1 = df_safe_cases.dropna(subset=[type])
  402. type_cases_dict = df_safe_cases1[type].to_dict()
  403. df_type_cases = pd.DataFrame(type_cases_dict).T.dropna(how='all')
  404. # calculate score_type and grade_type
  405. score_type_list = df_type_cases['score'].values.tolist()
  406. score_type = round(np.mean(score_type_list), 2)
  407. grade_type = score_grade(score_type)
  408. type_dict["score"] = score_type
  409. type_dict["level"] = grade_type
  410. score_type_dict[type] = score_type
  411. # calculate grade_distribution
  412. grade_type_list = df_type_cases['level'].values.tolist()
  413. grade_type_distribution = grade_statistic(grade_type_list)
  414. type_dict["gradeDistribution"] = grade_type_distribution
  415. # calculate type indexes
  416. # type_indexes_dict = df_type_cases['indexes'].explode().tolist()
  417. # df_type_indexes = pd.DataFrame(type_indexes_dict)
  418. dfs = df_type_cases['indexes'].apply(lambda x: pd.DataFrame(x).T)
  419. df_type_indexes = pd.concat(dfs.tolist(), ignore_index=True)
  420. # functionACC description
  421. type_metric1_list = [] # good
  422. type_metric2_list = [] # not good
  423. type_metric3_list = [] # bad
  424. # indexes
  425. type_dict_indexes = {}
  426. for metric in metric_dict[type]:
  427. df_metric_indexes = df_type_indexes[df_type_indexes['name'] == f"{name_dict[metric]}"]
  428. kind_metric = kind_dict[metric] if metric in bulitin_metric_list else custom_param_dict[metric]['kind'][0]
  429. optimal_metric = optimal_dict[metric] if metric in bulitin_metric_list else \
  430. custom_param_dict[metric]['optimal'][0]
  431. multiple_metric0 = multiple_dict[metric][0] if metric in bulitin_metric_list else \
  432. custom_param_dict[metric]['multiple'][0][0]
  433. multiple_metric1 = multiple_dict[metric][1] if metric in bulitin_metric_list else \
  434. custom_param_dict[metric]['multiple'][0][1]
  435. if kind_metric == 1: # 越大越好
  436. # metric_value_list = df_metric_indexes['min'].astype(float).values.tolist()
  437. metric_value_list = df_metric_indexes['extremum'].astype(str).values.tolist()
  438. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  439. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  440. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  441. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  442. if not optimal_metric:
  443. metric_bad_count = 0
  444. else:
  445. metric_bad_count = len([x for x in metric_value_list if x < float(optimal_metric)])
  446. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  447. metric_good_rate = round(100 - metric_bad_rate, 2)
  448. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  449. metric) if avg_metric_value > float(optimal_metric) else type_metric3_list.append(metric)
  450. elif kind_metric == -1: # 越小越好
  451. # metric_value_list = df_metric_indexes['max'].astype(float).values.tolist()
  452. metric_value_list = df_metric_indexes['extremum'].astype(str).values.tolist()
  453. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  454. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  455. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  456. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  457. if not optimal_metric:
  458. metric_bad_count = 0
  459. else:
  460. metric_bad_count = len([x for x in metric_value_list if x > float(optimal_metric)])
  461. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  462. metric_good_rate = round(100 - metric_bad_rate, 2)
  463. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  464. metric) if avg_metric_value < float(optimal_metric) else type_metric3_list.append(metric)
  465. elif kind_metric == 0:
  466. # metric_value_list = df_metric_indexes['avg'].astype(float).values.tolist()
  467. metric_value_list = df_metric_indexes['extremum'].astype(str).values.tolist()
  468. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  469. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  470. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  471. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  472. if not optimal_metric:
  473. metric_bad_count = 0
  474. else:
  475. metric_bad_count = len([x for x in metric_value_list if (
  476. x > float(optimal_metric) * multiple_metric1 or x < float(optimal_metric) *
  477. multiple_metric0)])
  478. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  479. metric_good_rate = round(100 - metric_bad_rate, 2)
  480. if not optimal_metric:
  481. type_metric1_list.append(metric)
  482. else:
  483. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  484. metric) if (
  485. float(optimal_metric) * multiple_metric1 > avg_metric_value > float(optimal_metric) *
  486. multiple_metric0) else type_metric3_list.append(metric)
  487. type_dict_indexes[metric] = {
  488. "name": f"{name_dict[metric]}",
  489. "average": avg_metric_value,
  490. "max": max_metric_value,
  491. "min": min_metric_value,
  492. "rate": f"{metric_bad_rate}%"
  493. # "range": f"[0, {optimal_dict['followSpeedDeviation']}]",
  494. }
  495. if not optimal_metric:
  496. type_dict_indexes[metric]["range"] = f"-"
  497. else:
  498. if kind_metric == -1:
  499. type_dict_indexes[metric]["range"] = f"[0, {optimal_metric}]"
  500. elif kind_metric == 1:
  501. type_dict_indexes[metric]["range"] = f"[{optimal_metric}, inf)"
  502. elif kind_metric == 0:
  503. type_dict_indexes[metric][
  504. "range"] = f"[{float(optimal_metric) * multiple_metric0}, {float(optimal_metric) * multiple_metric1}]"
  505. # else:
  506. # if custom_param_dict[metric]['kind'][0] == -1:
  507. # type_dict_indexes[metric][
  508. # "range"] = f"[0, {custom_param_dict[metric]['optimal'][0]}]"
  509. # elif custom_param_dict[metric]['kind'][0] == 1:
  510. # type_dict_indexes[metric][
  511. # "range"] = f"[{custom_param_dict[metric]['optimal'][0]}, inf)"
  512. # elif custom_param_dict[metric]['kind'][0] == 0:
  513. # type_dict_indexes[metric][
  514. # "range"] = f"[{custom_param_dict[metric]['optimal'][0] * multiple_dict[metric][0]}, {custom_param_dict[metric]['optimal'][0] * custom_param_dict[metric]['multiple'][0][1]}]"
  515. value_list_dict[metric] = metric_value_list
  516. bad_count_dict[metric] = metric_bad_count
  517. good_rate_dict[metric] = metric_good_rate
  518. bad_rate_dict[metric] = metric_bad_rate
  519. type_dict["indexes"] = type_dict_indexes
  520. for metric in metric_dict[type]:
  521. metric_data = {
  522. "name": f"{name_dict[metric]}",
  523. "data": value_list_dict[metric],
  524. # "markLine": [optimal_dict[metric]]
  525. }
  526. if metric in bulitin_metric_list:
  527. metric_data["markLine"] = [optimal_dict[metric]]
  528. builtin_graph_dict[metric] = metric_data
  529. else:
  530. metric_data["markLine"] = [custom_param_dict[metric]['optimal'][0]]
  531. custom_graph_dict[metric] = metric_data
  532. type_dict["builtin"] = builtin_graph_dict
  533. type_dict["custom"] = custom_graph_dict
  534. # description
  535. str_type_metric1 = ''
  536. str_type_metric2 = ''
  537. str_type_metric3 = ''
  538. if len(type_metric1_list) == len(metric_list):
  539. str_metric = string_concatenate(metric_list)
  540. type_description1 = f"{str_metric}指标均表现良好,平均值在合理范围内且不存在不合格用例。"
  541. elif len(type_metric3_list) == len(metric_list):
  542. str_metric = string_concatenate(metric_list)
  543. type_description1 = f"{str_metric}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  544. else:
  545. if type_metric1_list:
  546. str_type1 = string_concatenate(type_metric1_list)
  547. str_type_metric1 += f"{str_type1}指标表现良好,平均值在合理范围内且不存在不合格用例;"
  548. for metric in type_metric2_list:
  549. str_type_metric2 += f"{name_dict[metric]}指标表现不佳,存在{bad_count_dict[metric]}个不合格用例,需要改进算法在这些用例中的表现;"
  550. if type_metric3_list:
  551. str_type3 = string_concatenate(type_metric3_list)
  552. str_type_metric3 += f"{str_type3}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  553. type_description1 = ''
  554. type_description1 += (str_type_metric1 + '\n') if str_type_metric1 else ''
  555. type_description1 += (str_type_metric2 + '\n') if str_type_metric2 else ''
  556. type_description1 += str_type_metric3
  557. type_description1 = type_description1[:-1] + "。"
  558. type_description2 = "经计算可知,"
  559. for metric in metric_dict[type]:
  560. type_description2 += f"{metric}指标位于合理区间的占比为{good_rate_dict[metric]}%,"
  561. type_description2 = type_description2[:-1] + "。"
  562. type_dict["description1"] = replace_key_with_value(type_description1, name_dict)
  563. type_dict["description2"] = replace_key_with_value(type_description2, name_dict)
  564. type_details_dict[type] = type_dict
  565. report_dict["details"] = type_details_dict
  566. """
  567. # ------------------------------
  568. # safe summary dict
  569. """
  570. safe_type_list = []
  571. unsafe_type_list = []
  572. for key, value in score_type_dict.items():
  573. unsafe_type_list.append(key) if value < 80 else safe_type_list.append(key)
  574. # generate safe_description1
  575. safe_list_count = len(score_safe_list)
  576. over_80_count = len([num for num in score_safe_list if num >= 80])
  577. over_80_proportion = over_80_count / safe_list_count
  578. below_80_count = safe_list_count - over_80_count
  579. below_80_proportion = below_80_count / safe_list_count
  580. below_60_count = len([num for num in score_safe_list if num < 60])
  581. below_60_proportion = below_60_count / safe_list_count
  582. if grade_safe == '优秀':
  583. safe_description1 = '车辆在本轮测试中无碰撞风险;'
  584. elif grade_safe == '良好':
  585. safe_description1 = f'算法在本轮测试中的表现满足设计指标要求。其中有{over_80_count}个用例得分超过80分,占比为{over_80_proportion * 100:.2f}%;'
  586. elif grade_safe == '一般':
  587. str_unsafe_type = string_concatenate(unsafe_type_list)
  588. safe_description1 = f'未满足设计指标要求。其中有{below_80_count}个用例得分低于80分,占比为{below_80_proportion * 100:.2f}%,需优化算法在{str_unsafe_type}上的表现;'
  589. elif grade_safe == '较差':
  590. str_unsafe_type = string_concatenate(unsafe_type_list)
  591. safe_description1 = f'未满足设计指标要求。其中有{below_60_count}个用例得分低于60分,占比为{below_60_proportion * 100:.2f}%,需优化算法在{str_unsafe_type}上的表现;'
  592. if not unsafe_type_list:
  593. safe_description2 = '算法在安全性维度上的表现满足设计指标要求。'
  594. else:
  595. str_unsafe_type = string_concatenate(unsafe_type_list)
  596. safe_description2 = f"安全性在{str_unsafe_type}上存在严重风险,需要重点优化。"
  597. report_dict["description1"] = replace_key_with_value(safe_description1, type_name_dict)
  598. report_dict["description2"] = replace_key_with_value(safe_description2, type_name_dict)
  599. # report_dict = {
  600. # "name": "安全性",
  601. # "weight": f"{weight_safe * 100}%",
  602. # "score": score_safe,
  603. # "level": grade_safe,
  604. # "scoreList": score_safe_list,
  605. # "levelDistribution": grade_safe_distribution,
  606. #
  607. # "description1": safe_description1,
  608. # "description2": safe_description2,
  609. # "noObjectCar": False,
  610. #
  611. # "safeTime": time_dict,
  612. # "safeDistance": distance_dict,
  613. # "safeAcceleration": acceleration_dict,
  614. # "safeProbability": probability_dict
  615. # }
  616. return report_dict
  617. import pandas as pd
  618. import numpy as np
  619. def process_dataframe(df, column):
  620. # 初始化value列为NaN
  621. df['value'] = np.nan
  622. # 遍历DataFrame的每一行
  623. for index, row in df.iterrows():
  624. avg_val, max_val, min_val = row['avg'], row['max'], row['min']
  625. # 检查是否两个是'-',一个是数值
  626. count_minus = sum(val == '-' for val in [avg_val, max_val, min_val])
  627. if count_minus == 2:
  628. # 找出那个不是'-'的值,并赋值给value列
  629. numeric_val = [val for val in [avg_val, max_val, min_val] if val != '-'][0]
  630. df.at[index, 'value'] = float(numeric_val) # 转换为浮点数并赋值
  631. else:
  632. # 如果条件不满足,则将column列的值赋给value列
  633. df.at[index, 'value'] = row[column]
  634. return df
  635. def assign_value_column(df, column, avg_col='avg', max_col='max', min_col='min'):
  636. # 判断三列中有几个是'-'(作为空值或缺失值的代表)
  637. df[['num_avg', 'num_max', 'num_min']] = df[[avg_col, max_col, min_col]].applymap(
  638. lambda x: 0 if isinstance(x, (int, float)) else 1 if x == '-' else None)
  639. # 计算每行中'-'的数量
  640. df['num_hyphens'] = df[['num_avg', 'num_max', 'num_min']].sum(axis=1)
  641. # 判断是否满足条件:两个'-'和一个数值
  642. df['is_valid_condition'] = (df['num_hyphens'] == 2)
  643. # df['is_valid_condition'] = (df['num_hyphens'] == 2) & df[[avg_col, max_col, min_col]].apply(
  644. # lambda row: sum(isinstance(val, (int, float)) for val in row) == 1, axis=1)
  645. # 创建一个新的value列,根据条件填充值
  646. df['value'] = df.apply(
  647. lambda row: next((val for val in [row[avg_col], row[max_col], row[min_col]] if val != '-'), row[column]) if row['is_valid_condition'] else row[column], axis=1)
  648. # 删除辅助列
  649. df.drop(['num_avg', 'num_max', 'num_min', 'num_hyphens', 'is_valid_condition'], axis=1, inplace=True)
  650. # 返回修改后的DataFrame
  651. return df
  652. # 示例使用
  653. # 假设df是你的原始DataFrame,且包含avg, max, min和column列
  654. # df = pd.DataFrame({...}) # 这里省略了具体的DataFrame创建代码
  655. # result_df = assign_value_column(df)
  656. # print(result_df)
  657. def func_multi_case_statistic(func_cases_dict, config):
  658. """
  659. Args:
  660. func_cases_dict:
  661. config:
  662. Returns:
  663. """
  664. # report_dict = {
  665. # "name": "功能性",
  666. # "weight": f"{weight_dimension * 100}%",
  667. # "score": score_func,
  668. # "level": grade_func,
  669. # "scoreList": score_func_list,
  670. # "levelDistribution": grade_func_distribution,
  671. # "description1": func_description1,
  672. # "description2": func_description2,
  673. # "noObjectCar": False,
  674. # "functionACC": follow_dict,
  675. # "functionLKA": lane_dict
  676. # }
  677. df_func_cases = pd.DataFrame(func_cases_dict).T.dropna(how='all')
  678. # common data
  679. bulitin_metric_list = config.builtinMetricList
  680. # config infos
  681. dimension_config = config.config["function"]
  682. metric_list = dimension_config['metric']
  683. type_list = dimension_config['type']
  684. type_name_dict = dimension_config['typeName']
  685. name_dict = dimension_config['name']
  686. unit_dict = dimension_config['unit']
  687. metric_dict = dimension_config['typeMetricDict']
  688. # # custom metric data
  689. customMetricParam = dimension_config['customMetricParam']
  690. custom_metric_list = list(customMetricParam.keys())
  691. # custom_data = custom_data
  692. custom_param_dict = custom_metric_param_parser(customMetricParam)
  693. weight_dimension = float(dimension_config['weightDimension'])
  694. optimal_dict = dimension_config['optimal']
  695. kind_dict = dimension_config['kind']
  696. multiple_dict = dimension_config['multiple']
  697. report_dict = {
  698. "name": "功能性",
  699. "weight": f"{weight_dimension * 100}%",
  700. "noObjectCar": False,
  701. }
  702. # calculate score_func and grade_func
  703. score_func_list = df_func_cases['score'].values.tolist()
  704. score_func = cal_score_from_80(score_func_list)
  705. grade_func = score_grade(score_func)
  706. report_dict["score"] = score_func
  707. report_dict["level"] = grade_func
  708. report_dict["scoreList"] = score_func_list
  709. # calculate grade_distribution
  710. grade_func_list = df_func_cases['level'].values.tolist()
  711. grade_func_distribution = grade_statistic(grade_func_list)
  712. report_dict["levelDistribution"] = grade_func_distribution
  713. score_type_dict = {}
  714. value_list_dict = {}
  715. bad_count_dict = {}
  716. good_rate_dict = {}
  717. bad_rate_dict = {}
  718. type_details_dict = {}
  719. for type in type_list:
  720. type_dict = {
  721. "name": type_name_dict[type],
  722. }
  723. builtin_graph_dict = {}
  724. custom_graph_dict = {}
  725. df_func_cases[type] = df_func_cases["details"].apply(lambda x: x[type] if type in x.keys() else None)
  726. df_func_cases1 = df_func_cases.dropna(subset=[type])
  727. type_cases_dict = df_func_cases1[type].to_dict()
  728. df_type_cases = pd.DataFrame(type_cases_dict).T.dropna(how='all')
  729. # calculate score_type and grade_type
  730. score_type_list = df_type_cases['score'].values.tolist()
  731. score_type = round(np.mean(score_type_list), 2)
  732. grade_type = score_grade(score_type)
  733. type_dict["score"] = score_type
  734. type_dict["level"] = grade_type
  735. score_type_dict[type] = score_type
  736. # calculate grade_distribution
  737. grade_type_list = df_type_cases['level'].values.tolist()
  738. grade_type_distribution = grade_statistic(grade_type_list)
  739. type_dict["gradeDistribution"] = grade_type_distribution
  740. # calculate type indexes
  741. # type_indexes_dict = df_type_cases['indexes'].explode().tolist()
  742. # df_type_indexes = pd.DataFrame(type_indexes_dict)
  743. dfs = df_type_cases['indexes'].apply(lambda x: pd.DataFrame(x).T)
  744. df_type_indexes = pd.concat(dfs.tolist(), ignore_index=True)
  745. # functionACC description
  746. type_metric1_list = [] # good
  747. type_metric2_list = [] # not good
  748. type_metric3_list = [] # bad
  749. # indexes
  750. type_dict_indexes = {}
  751. for metric in metric_dict[type]:
  752. df_metric_indexes = df_type_indexes[df_type_indexes['name'] == f"{name_dict[metric]}"]
  753. kind_metric = kind_dict[metric] if metric in bulitin_metric_list else custom_param_dict[metric]['kind'][0]
  754. optimal_metric = optimal_dict[metric] if metric in bulitin_metric_list else \
  755. custom_param_dict[metric]['optimal'][0]
  756. multiple_metric0 = multiple_dict[metric][0] if metric in bulitin_metric_list else \
  757. custom_param_dict[metric]['multiple'][0][0]
  758. multiple_metric1 = multiple_dict[metric][1] if metric in bulitin_metric_list else \
  759. custom_param_dict[metric]['multiple'][0][1]
  760. if kind_metric == 1: # 越大越好
  761. # metric_value_list = df_metric_indexes['min'].astype(float).values.tolist()
  762. df_metric_indexes = assign_value_column(df_metric_indexes, "min")
  763. metric_value_list = df_metric_indexes['value'].astype(str).values.tolist()
  764. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  765. metric_value_list = [x for x in metric_value_list if not np.isnan(x)]
  766. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  767. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  768. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  769. if not optimal_metric:
  770. metric_bad_count = 0
  771. else:
  772. metric_bad_count = len([x for x in metric_value_list if x < float(optimal_metric)])
  773. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  774. metric_good_rate = round(100 - metric_bad_rate, 2)
  775. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  776. metric) if avg_metric_value > float(optimal_metric) else type_metric3_list.append(metric)
  777. elif kind_metric == -1: # 越小越好
  778. # metric_value_list = df_metric_indexes['max'].astype(float).values.tolist()
  779. # if metric in ["centerDistanceExpectation", "centerDistanceStandardDeviation", "centerDistanceFrequency",
  780. # "centerDistanceRange"]:
  781. # index_name = 'avg'
  782. # elif metric in ["centerDistanceMin"]:
  783. # index_name = 'min'
  784. # else: # centerDistanceMax, laneDistance
  785. # index_name = 'max'
  786. df_metric_indexes = assign_value_column(df_metric_indexes, "max")
  787. metric_value_list = df_metric_indexes['value'].astype(str).values.tolist()
  788. # metric_value_list = df_metric_indexes[index_name].astype(str).values.tolist()
  789. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  790. metric_value_list = [x for x in metric_value_list if not np.isnan(x)]
  791. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  792. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  793. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  794. if not optimal_metric:
  795. metric_bad_count = 0
  796. else:
  797. metric_bad_count = len([x for x in metric_value_list if x > float(optimal_metric)])
  798. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  799. metric_good_rate = round(100 - metric_bad_rate, 2)
  800. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  801. metric) if avg_metric_value < float(optimal_metric) else type_metric3_list.append(metric)
  802. elif kind_metric == 0:
  803. # metric_value_list = df_metric_indexes['avg'].astype(float).values.tolist()
  804. # metric_value_list = df_metric_indexes['avg'].astype(str).values.tolist()
  805. df_metric_indexes = assign_value_column(df_metric_indexes, "avg")
  806. metric_value_list = df_metric_indexes['value'].astype(str).values.tolist()
  807. metric_value_list = [float(x) for x in metric_value_list if '-' not in x]
  808. metric_value_list = [x for x in metric_value_list if not np.isnan(x)]
  809. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  810. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  811. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  812. if not optimal_metric:
  813. metric_bad_count = 0
  814. else:
  815. metric_bad_count = len([x for x in metric_value_list if (
  816. x > float(optimal_metric) * multiple_metric1 or x < float(optimal_metric) *
  817. multiple_metric0)])
  818. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  819. metric_good_rate = round(100 - metric_bad_rate, 2)
  820. if not optimal_metric:
  821. type_metric1_list.append(metric)
  822. else:
  823. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  824. metric) if (
  825. float(optimal_metric) * multiple_metric1 > avg_metric_value > float(optimal_metric) *
  826. multiple_metric0) else type_metric3_list.append(metric)
  827. type_dict_indexes[metric] = {
  828. "name": f"{name_dict[metric]}",
  829. "average": avg_metric_value,
  830. "max": max_metric_value,
  831. "min": min_metric_value,
  832. # "range": f"[0, {optimal_dict['followSpeedDeviation']}]",
  833. }
  834. if not optimal_metric:
  835. type_dict_indexes[metric]["range"] = f"-"
  836. else:
  837. if kind_metric == -1:
  838. type_dict_indexes[metric]["range"] = f"[0, {optimal_metric}]"
  839. elif kind_metric == 1:
  840. type_dict_indexes[metric]["range"] = f"[{optimal_metric}, inf)"
  841. elif kind_metric == 0:
  842. type_dict_indexes[metric][
  843. "range"] = f"[{float(optimal_metric) * multiple_metric0}, {float(optimal_metric) * multiple_metric1}]"
  844. # else:
  845. # if custom_param_dict[metric]['kind'][0] == -1:
  846. # type_dict_indexes[metric][
  847. # "range"] = f"[0, {custom_param_dict[metric]['optimal'][0]}]"
  848. # elif custom_param_dict[metric]['kind'][0] == 1:
  849. # type_dict_indexes[metric][
  850. # "range"] = f"[{custom_param_dict[metric]['optimal'][0]}, inf)"
  851. # elif custom_param_dict[metric]['kind'][0] == 0:
  852. # type_dict_indexes[metric][
  853. # "range"] = f"[{custom_param_dict[metric]['optimal'][0] * multiple_dict[metric][0]}, {custom_param_dict[metric]['optimal'][0] * custom_param_dict[metric]['multiple'][0][1]}]"
  854. value_list_dict[metric] = metric_value_list
  855. bad_count_dict[metric] = metric_bad_count
  856. good_rate_dict[metric] = metric_good_rate
  857. bad_rate_dict[metric] = metric_bad_rate
  858. type_dict["indexes"] = type_dict_indexes
  859. for metric in metric_dict[type]:
  860. metric_data = {
  861. "name": f"{name_dict[metric]}",
  862. "data": value_list_dict[metric],
  863. # "markLine": [optimal_dict[metric]]
  864. }
  865. if metric in bulitin_metric_list:
  866. metric_data["markLine"] = [optimal_dict[metric]]
  867. builtin_graph_dict[metric] = metric_data
  868. else:
  869. metric_data["markLine"] = [custom_param_dict[metric]['optimal'][0]]
  870. custom_graph_dict[metric] = metric_data
  871. type_dict["builtin"] = builtin_graph_dict
  872. type_dict["custom"] = custom_graph_dict
  873. # description
  874. str_type_metric1 = ''
  875. str_type_metric2 = ''
  876. str_type_metric3 = ''
  877. if len(type_metric1_list) == len(metric_list):
  878. str_metric = string_concatenate(metric_list)
  879. type_description1 = f"{str_metric}指标均表现良好,平均值在合理范围内且不存在不合格用例。"
  880. elif len(type_metric3_list) == len(metric_list):
  881. str_metric = string_concatenate(metric_list)
  882. type_description1 = f"{str_metric}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  883. else:
  884. if type_metric1_list:
  885. str_type1 = string_concatenate(type_metric1_list)
  886. str_type_metric1 += f"{str_type1}指标表现良好,平均值在合理范围内且不存在不合格用例;"
  887. for metric in type_metric2_list:
  888. str_type_metric2 += f"{name_dict[metric]}指标表现不佳,存在{bad_count_dict[metric]}个不合格用例,需要改进算法在这些用例中的表现;"
  889. if type_metric3_list:
  890. str_type3 = string_concatenate(type_metric3_list)
  891. str_type_metric3 += f"{str_type3}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  892. type_description1 = ''
  893. type_description1 += (str_type_metric1 + '\n') if str_type_metric1 else ''
  894. type_description1 += (str_type_metric2 + '\n') if str_type_metric2 else ''
  895. type_description1 += str_type_metric3
  896. type_description1 = type_description1[:-1] + "。"
  897. type_description2 = "经计算可知,"
  898. for metric in metric_dict[type]:
  899. type_description2 += f"{metric}指标位于合理区间的占比为{good_rate_dict[metric]}%,"
  900. type_description2 = type_description2[:-1] + "。"
  901. type_dict["description1"] = replace_key_with_value(type_description1, name_dict)
  902. type_dict["description2"] = replace_key_with_value(type_description2, name_dict)
  903. type_details_dict[type] = type_dict
  904. report_dict["details"] = type_details_dict
  905. """
  906. # ------------------------------
  907. # func summary dict
  908. """
  909. # generate func_description1
  910. func_list_count = len(score_func_list)
  911. over_80_count = len([num for num in score_func_list if num >= 80])
  912. over_80_proportion = over_80_count / func_list_count
  913. below_80_count = func_list_count - over_80_count
  914. below_80_proportion = below_80_count / func_list_count
  915. below_60_count = len([num for num in score_func_list if num < 60])
  916. below_60_proportion = below_60_count / func_list_count
  917. func_type_list = []
  918. unfunc_type_list = []
  919. for key, value in score_type_dict.items():
  920. unfunc_type_list.append(key) if value < 80 else func_type_list.append(key)
  921. if grade_func == '优秀':
  922. str_func_type = string_concatenate(func_type_list)
  923. func_description1 = f'算法在{str_func_type}上表现优秀;'
  924. elif grade_func == '良好':
  925. str_func_type = string_concatenate(func_type_list)
  926. func_description1 = f'算法在{str_func_type}上表现良好,满足设计指标要求。其中有{over_80_count}个用例得分超过80分,占比为{over_80_proportion * 100:.2f}%;'
  927. elif grade_func == '一般':
  928. str_unfunc_type = string_concatenate(unfunc_type_list)
  929. func_description1 = f'未满足设计指标要求。其中有{below_80_count}个用例得分低于80分,占比为{below_80_proportion * 100:.2f}%,需优化算法在{str_unfunc_type}上的表现;'
  930. elif grade_func == '较差':
  931. str_unfunc_type = string_concatenate(unfunc_type_list)
  932. func_description1 = f'未满足设计指标要求。其中有{below_60_count}个用例得分低于60分,占比为{below_60_proportion * 100:.2f}%,需优化算法在{str_unfunc_type}上的表现;'
  933. if not unfunc_type_list:
  934. str_func_type = string_concatenate(func_type_list)
  935. func_description2 = f'算法在{str_func_type}功能上的表现满足设计指标要求。'
  936. else:
  937. str_unfunc_type = string_concatenate(unfunc_type_list)
  938. func_description2 = f"算法在{str_unfunc_type}功能上需要重点优化。"
  939. report_dict["description1"] = replace_key_with_value(func_description1, type_name_dict)
  940. report_dict["description2"] = replace_key_with_value(func_description2, type_name_dict)
  941. # report_dict = {
  942. # "name": "功能性",
  943. # "weight": f"{weight_dimension * 100}%",
  944. # "score": score_func,
  945. # "level": grade_func,
  946. # "scoreList": score_func_list,
  947. # "levelDistribution": grade_func_distribution,
  948. # "description1": func_description1,
  949. # "description2": func_description2,
  950. # "noObjectCar": False,
  951. # "functionACC": follow_dict,
  952. # "functionLKA": lane_dict
  953. # }
  954. return report_dict
  955. def comp_multi_case_statistic(comp_cases_dict, config):
  956. """
  957. Args:
  958. comp_cases_dict:
  959. config:
  960. Returns:
  961. """
  962. # report_dict = {
  963. # "name": "合规性",
  964. # "weight": f"{weight_comp * 100}%",
  965. # "score": score_comp,
  966. # "level": grade_comp,
  967. # "scoreList": score_comp_list,
  968. # "levelDistribution": grade_comp_distribution,
  969. # "description1": comp_description1,
  970. # "description2": comp_description2,
  971. # "deductPoints": [deduct_1_dict, deduct_3_dict, deduct_6_dict, deduct_9_dict, deduct_12_dict]
  972. # }
  973. df_comp_cases = pd.DataFrame(comp_cases_dict).T.dropna(how='all')
  974. # common data
  975. bulitin_metric_list = config.builtinMetricList
  976. # config infos
  977. dimension_config = config.config["compliance"]
  978. metric_list = dimension_config['metric']
  979. type_list = dimension_config['type']
  980. type_name_dict = dimension_config['typeName']
  981. name_dict = dimension_config['name']
  982. unit_dict = dimension_config['unit']
  983. metric_dict = dimension_config['typeMetricDict']
  984. weight_comp = float(dimension_config['weightDimension'])
  985. report_dict = {
  986. "name": "合规性",
  987. "weight": f"{weight_comp * 100}%",
  988. }
  989. # calculate score_comp and grade_comp
  990. score_comp_list = df_comp_cases['score'].values.tolist()
  991. score_comp = cal_score_from_80(score_comp_list)
  992. grade_comp = score_grade(score_comp)
  993. report_dict["score"] = score_comp
  994. report_dict["level"] = grade_comp
  995. report_dict["scoreList"] = score_comp_list
  996. # calculate grade_distribution
  997. grade_comp_list = df_comp_cases['level'].values.tolist()
  998. grade_comp_distribution = grade_statistic(grade_comp_list)
  999. report_dict["levelDistribution"] = grade_comp_distribution
  1000. deduct_cases_dict = df_comp_cases['details'].to_dict()
  1001. df_deduct_cases = pd.DataFrame(deduct_cases_dict).T.dropna(how='all')
  1002. score_type_dict = {}
  1003. type_details_dict = {}
  1004. # report_dict["deductPoints"] = []
  1005. for type in type_list:
  1006. type_dict = {
  1007. "name": type_name_dict[type],
  1008. }
  1009. type_cases_dict = df_deduct_cases[type].to_dict()
  1010. df_type_cases = pd.DataFrame(type_cases_dict).T.dropna(how='all')
  1011. # calculate score_type and grade_type
  1012. score_type_list = df_type_cases['score'].values.tolist()
  1013. score_type = round(np.mean(score_type_list), 2)
  1014. grade_type = score_grade(score_type)
  1015. type_dict["score"] = score_type
  1016. type_dict["level"] = grade_type
  1017. score_type_dict[type] = score_type
  1018. dfs = df_type_cases['indexes'].apply(lambda x: pd.DataFrame(x).T)
  1019. df_type_indexes = pd.concat(dfs.tolist(), ignore_index=True)
  1020. type_dict_indexes = {}
  1021. for metric in metric_dict[type]:
  1022. df_metric_indexes = df_type_indexes[df_type_indexes['name'] == f"{name_dict[metric]}"]
  1023. metric_times_list = df_metric_indexes['times'].astype(int).values.tolist()
  1024. metric_times_count = sum(metric_times_list)
  1025. type_dict_indexes[metric] = {
  1026. "name": f"{name_dict[metric]}",
  1027. "times": metric_times_count
  1028. }
  1029. type_dict["indexes"] = type_dict_indexes
  1030. # report_dict[type] = type_dict
  1031. type_details_dict[type] = type_dict
  1032. report_dict["details"] = type_details_dict
  1033. # get compliance description
  1034. comp_list_count = len(score_comp_list)
  1035. below_60_count = len([num for num in score_comp_list if num < 60])
  1036. below_60_proportion = below_60_count / comp_list_count
  1037. if grade_comp == '优秀':
  1038. comp_description1 = '车辆在本轮测试中无违反交通法规行为;'
  1039. else:
  1040. comp_description1 = f'未满足设计指标要求。其中有{below_60_count}个用例得分低于60分,占比为{below_60_proportion * 100:.2f}%,需优化算法在合规性上的表现;'
  1041. comp_description2 = f'共有{comp_list_count}个用例,其中{below_60_count}个用例出现违规行为。'
  1042. report_dict["description1"] = comp_description1
  1043. report_dict["description2"] = comp_description2
  1044. # report_dict = {
  1045. # "name": "合规性",
  1046. # "weight": f"{weight_comp * 100}%",
  1047. # "score": score_comp,
  1048. # "level": grade_comp,
  1049. # "scoreList": score_comp_list,
  1050. # "levelDistribution": grade_comp_distribution,
  1051. # "description1": comp_description1,
  1052. # "description2": comp_description2,
  1053. # "deductPoints": [deduct_1_dict, deduct_3_dict, deduct_6_dict, deduct_9_dict, deduct_12_dict]
  1054. # }
  1055. return report_dict
  1056. def comf_multi_case_statistic(comf_cases_dict, config):
  1057. """
  1058. Args:
  1059. comf_cases_dict:
  1060. config:
  1061. Returns:
  1062. """
  1063. # report_dict = {
  1064. # "name": "舒适性",
  1065. # "weight": f"{weight_comfort * 100}%",
  1066. # "score": score_comf,
  1067. # "level": grade_comf,
  1068. # "scoreList": score_comf_list,
  1069. # "levelDistribution": grade_comf_distribution,
  1070. # "description1": comf_description1,
  1071. # "description2": comf_description2,
  1072. # "description3": comf_description3,
  1073. # "description4": comf_description4,
  1074. # "indexes": [],
  1075. # "zigzagData": zigzag_score_list,
  1076. # "shakeData": shake_score_list,
  1077. # "cadenceData": cadence_score_list,
  1078. # "slamBrakeData": slam_brake_score_list,
  1079. # "slamAccelData": slam_accel_score_list
  1080. # }
  1081. df_comf_cases = pd.DataFrame(comf_cases_dict).T.dropna(how='all')
  1082. # common data
  1083. bulitin_metric_list = config.builtinMetricList
  1084. # config infos
  1085. dimension_config = config.config["comfort"]
  1086. metric_list = dimension_config['metric']
  1087. type_list = dimension_config['type']
  1088. type_name_dict = dimension_config['typeName']
  1089. name_dict = dimension_config['name']
  1090. unit_dict = dimension_config['unit']
  1091. metric_dict = dimension_config['typeMetricDict']
  1092. # # custom metric data
  1093. customMetricParam = dimension_config['customMetricParam']
  1094. # custom_data = custom_data
  1095. # custom_param_dict = custom_metric_param_parser(customMetricParam)
  1096. weight_dimension = float(dimension_config['weightDimension'])
  1097. # optimal_dict = dimension_config['optimal']
  1098. # kind_dict = dimension_config['kind']
  1099. # multiple_dict = dimension_config['multiple']
  1100. report_dict = {
  1101. "name": "舒适性",
  1102. "weight": f"{weight_dimension * 100}%",
  1103. }
  1104. # calculate score_comf and grade_comf
  1105. score_comf_list = df_comf_cases['score'].values.tolist()
  1106. score_comf = cal_score_from_80(score_comf_list)
  1107. grade_comf = score_grade(score_comf)
  1108. report_dict["score"] = score_comf
  1109. report_dict["level"] = grade_comf
  1110. report_dict["scoreList"] = score_comf_list
  1111. # calculate grade_distribution
  1112. grade_comf_list = df_comf_cases['level'].values.tolist()
  1113. grade_comf_distribution = grade_statistic(grade_comf_list)
  1114. report_dict["levelDistribution"] = grade_comf_distribution
  1115. score_type_dict = {}
  1116. value_list_dict = {}
  1117. bad_count_dict = {}
  1118. good_rate_dict = {}
  1119. bad_rate_dict = {}
  1120. type_details_dict = {}
  1121. for type in type_list:
  1122. type_dict = {
  1123. "name": type_name_dict[type],
  1124. }
  1125. builtin_graph_dict = {}
  1126. custom_graph_dict = {}
  1127. df_comf_cases[type] = df_comf_cases["details"].apply(lambda x: x[type] if type in x.keys() else None)
  1128. df_comf_cases1 = df_comf_cases.dropna(subset=[type])
  1129. type_cases_dict = df_comf_cases1[type].to_dict()
  1130. df_type_cases = pd.DataFrame(type_cases_dict).T.dropna(how='all')
  1131. # calculate score_type and grade_type
  1132. score_type_list = df_type_cases['score'].values.tolist()
  1133. score_type = round(np.mean(score_type_list), 2)
  1134. grade_type = score_grade(score_type)
  1135. type_dict["score"] = score_type
  1136. type_dict["level"] = grade_type
  1137. score_type_dict[type] = score_type
  1138. # calculate grade_distribution
  1139. grade_type_list = df_type_cases['level'].values.tolist()
  1140. grade_type_distribution = grade_statistic(grade_type_list)
  1141. type_dict["gradeDistribution"] = grade_type_distribution
  1142. dfs = df_type_cases['indexes'].apply(lambda x: pd.DataFrame(x).T)
  1143. df_type_indexes = pd.concat(dfs.tolist(), ignore_index=True)
  1144. # functionACC description
  1145. type_metric1_list = [] # good
  1146. type_metric2_list = [] # not good
  1147. type_metric3_list = [] # bad
  1148. # indexes
  1149. type_dict_indexes = {}
  1150. for metric in metric_dict[type]:
  1151. df_metric_indexes = df_type_indexes[df_type_indexes['name'] == f"{name_dict[metric]}"]
  1152. metric_value_list = df_metric_indexes['score'].astype(float).values.tolist()
  1153. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  1154. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  1155. min_metric_value = min(metric_value_list) if metric_value_list else "-"
  1156. metric_bad_count = len([x for x in metric_value_list if x < 80])
  1157. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  1158. metric_good_rate = round(100 - metric_bad_rate, 2)
  1159. # metric_number_list = df_metric_indexes['numberReal'].astype(int).values.tolist()
  1160. metric_number_list = df_metric_indexes['numberReal'].astype(float).values.tolist()
  1161. metric_duration_list = df_metric_indexes['durationReal'].astype(float).values.tolist()
  1162. metric_strength_list = df_metric_indexes['strengthReal'].astype(float).values.tolist()
  1163. type_metric1_list.append(metric) if avg_metric_value > 80 else type_metric2_list.append(
  1164. metric) if min_metric_value < 80 else type_metric3_list.append(metric)
  1165. type_dict_indexes[metric] = {
  1166. "name": f"{name_dict[metric]}",
  1167. "avgScore": avg_metric_value,
  1168. "maxScore": max_metric_value,
  1169. "minScore": min_metric_value,
  1170. "avgNumber": f"{np.mean(metric_number_list):.2f}",
  1171. "avgDuration": f"{np.mean(metric_duration_list):.2f}",
  1172. "avgStrength": f"{np.mean(metric_strength_list):.2f}"
  1173. }
  1174. value_list_dict[metric] = metric_value_list
  1175. bad_count_dict[metric] = metric_bad_count
  1176. good_rate_dict[metric] = metric_good_rate
  1177. bad_rate_dict[metric] = metric_bad_rate
  1178. type_dict["indexes"] = type_dict_indexes
  1179. for metric in metric_dict[type]:
  1180. metric_data = {
  1181. "name": f"{name_dict[metric]}",
  1182. "data": value_list_dict[metric],
  1183. # "markLine": [optimal_dict[metric]]
  1184. }
  1185. if metric in bulitin_metric_list:
  1186. # metric_data["markLine"] = [optimal_dict[metric]]
  1187. builtin_graph_dict[metric] = metric_data
  1188. else:
  1189. # metric_data["markLine"] = [custom_param_dict[metric]['optimal'][0]]
  1190. custom_graph_dict[metric] = metric_data
  1191. type_dict["builtin"] = builtin_graph_dict
  1192. type_dict["custom"] = custom_graph_dict
  1193. # description
  1194. str_type_metric1 = ''
  1195. str_type_metric2 = ''
  1196. str_type_metric3 = ''
  1197. if len(type_metric1_list) == len(metric_list):
  1198. str_metric = string_concatenate(metric_list)
  1199. type_description1 = f"{str_metric}指标最低分均超过设计指标要求,算法在{len(score_comf_list)}个用例中均表现良好。"
  1200. elif len(type_metric3_list) == len(metric_list):
  1201. str_metric = string_concatenate(metric_list)
  1202. type_description1 = f"{str_metric}指标平均分低于设计指标要求,算法整体表现不佳,需要优化算法在这些指标上的表现;"
  1203. else:
  1204. if type_metric1_list:
  1205. str_type1 = string_concatenate(type_metric1_list)
  1206. str_type_metric1 += f"{str_type1}指标最低分超过设计指标要求,算法在{len(score_comf_list)}个用例中均表现良好;"
  1207. for metric in type_metric2_list:
  1208. str_type_metric2 += f"{name_dict[metric]}指标平均分超过设计指标要求,但是算法存在{bad_count_dict[metric]}个表现不佳用例,需要改进算法在这些用例中的表现;"
  1209. if type_metric3_list:
  1210. str_type3 = string_concatenate(type_metric3_list)
  1211. str_type_metric3 += f"{str_type3}指标平均分低于设计指标要求,算法整体表现不佳,需要优化算法在这些指标上的表现;"
  1212. type_description1 = ''
  1213. type_description1 += (str_type_metric1 + '\n') if str_type_metric1 else ''
  1214. type_description1 += (str_type_metric2 + '\n') if str_type_metric2 else ''
  1215. type_description1 += str_type_metric3
  1216. type_description1 = type_description1[:-1] + "。"
  1217. type_description2 = "经计算可知,算法"
  1218. for metric in metric_dict[type]:
  1219. type_description2 += f"在{metric}指标上表现良好的概率为{good_rate_dict[metric]}%,"
  1220. type_description2 = type_description2[:-1] + "。"
  1221. type_dict["description1"] = replace_key_with_value(type_description1, name_dict)
  1222. type_dict["description2"] = replace_key_with_value(type_description2, name_dict)
  1223. type_details_dict[type] = type_dict
  1224. report_dict["details"] = type_details_dict
  1225. # calculate comfort description
  1226. # str for comfort description1
  1227. comf_list = []
  1228. uncomf_list = []
  1229. for metric in metric_list:
  1230. uncomf_list.append(metric) if bad_count_dict[metric] > 0 else comf_list.append(metric)
  1231. # generate comf_description1
  1232. comf_list_count = len(score_comf_list)
  1233. over_80_count = len([num for num in score_comf_list if num >= 80])
  1234. over_80_proportion = over_80_count / comf_list_count
  1235. below_80_count = comf_list_count - over_80_count
  1236. below_80_proportion = below_80_count / comf_list_count
  1237. below_60_count = len([num for num in score_comf_list if num < 60])
  1238. below_60_proportion = below_60_count / comf_list_count
  1239. if grade_comf == '优秀':
  1240. comf_description1 = '乘客在本轮测试中体验舒适;'
  1241. elif grade_comf == '良好':
  1242. comf_description1 = f'算法在本轮测试中的表现满足设计指标要求。其中有{over_80_count}个用例得分超过80分,占比为{over_80_proportion * 100:.2f}%;'
  1243. elif grade_comf == '一般':
  1244. str_uncomf_metric = string_concatenate(uncomf_list)
  1245. comf_description1 = f'未满足设计指标要求。其中有{below_80_count}个用例得分低于80分,占比为{below_80_proportion * 100:.2f}%,需优化算法在{str_uncomf_metric}上的表现;'
  1246. elif grade_comf == '较差':
  1247. str_uncomf_metric = string_concatenate(uncomf_list)
  1248. comf_description1 = f'未满足设计指标要求。其中有{below_60_count}个用例得分低于60分,占比为{below_60_proportion * 100:.2f}%,需优化算法在{str_uncomf_metric}上的表现;'
  1249. # str for comfort description2
  1250. control_type = []
  1251. if '画龙指标' in uncomf_list or '晃动指标' in uncomf_list:
  1252. control_type.append('横向')
  1253. if '顿挫指标' in uncomf_list or '急刹指标' in uncomf_list or '急加速指标' in uncomf_list in uncomf_list:
  1254. control_type.append('纵向')
  1255. str_control_type = '和'.join(control_type)
  1256. if not control_type:
  1257. comf_description2 = f"算法在舒适性维度上的表现满足设计指标要求"
  1258. else:
  1259. comf_description2 = f"算法应该优化对车辆的{str_control_type}控制,优化乘坐体验"
  1260. report_dict["description1"] = replace_key_with_value(comf_description1, name_dict)
  1261. report_dict["description2"] = replace_key_with_value(comf_description2, type_name_dict)
  1262. # report_dict = {
  1263. # "name": "舒适性",
  1264. # "weight": f"{weight_comfort * 100}%",
  1265. # "score": score_comf,
  1266. # "level": grade_comf,
  1267. # "scoreList": score_comf_list,
  1268. # "levelDistribution": grade_comf_distribution,
  1269. # "description1": comf_description1,
  1270. # "description2": comf_description2,
  1271. # "indexes": [],
  1272. # "zigzagData": zigzag_score_list,
  1273. # "shakeData": shake_score_list,
  1274. # "cadenceData": cadence_score_list,
  1275. # "slamBrakeData": slam_brake_score_list,
  1276. # "slamAccelData": slam_accel_score_list
  1277. # }
  1278. return report_dict
  1279. def effi_deviation_extra(str):
  1280. if str[0] == '+':
  1281. return str[1:].split("%")[0]
  1282. else:
  1283. return str.split("%")[0]
  1284. def effi_multi_case_statistic(effi_cases_dict, config):
  1285. """
  1286. Args:
  1287. effi_cases_dict:
  1288. config:
  1289. Returns:
  1290. """
  1291. # report_dict = {
  1292. # "name": "高效性",
  1293. # "weight": f"{weight_efficient * 100}%",
  1294. # "score": score_effi,
  1295. # "level": grade_effi,
  1296. # "scoreList": score_effi_list,
  1297. # "levelDistribution": grade_effi_distribution,
  1298. # "description1": effi_description1,
  1299. # "description2": effi_description2,
  1300. # "description3": effi_description3,
  1301. # "indexes": []
  1302. # }
  1303. df_effi_cases = pd.DataFrame(effi_cases_dict).T.dropna(how='all')
  1304. # common data
  1305. bulitin_metric_list = config.builtinMetricList
  1306. # config infos
  1307. dimension_config = config.config["efficient"]
  1308. metric_list = dimension_config['metric']
  1309. type_list = dimension_config['type']
  1310. type_name_dict = dimension_config['typeName']
  1311. name_dict = dimension_config['name']
  1312. unit_dict = dimension_config['unit']
  1313. metric_dict = dimension_config['typeMetricDict']
  1314. # # custom metric data
  1315. customMetricParam = dimension_config['customMetricParam']
  1316. # custom_data = custom_data
  1317. custom_param_dict = custom_metric_param_parser(customMetricParam)
  1318. weight_efficient = float(dimension_config['weightDimension'])
  1319. optimal_dict = dimension_config['optimal']
  1320. kind_dict = dimension_config['kind']
  1321. multiple_dict = dimension_config['multiple']
  1322. report_dict = {
  1323. "name": "高效性",
  1324. "weight": f"{weight_efficient * 100}%",
  1325. }
  1326. # calculate score_effi and grade_effi
  1327. score_effi_list = df_effi_cases['score'].values.tolist()
  1328. score_effi = cal_score_from_80(score_effi_list)
  1329. grade_effi = score_grade(score_effi)
  1330. report_dict["score"] = score_effi
  1331. report_dict["level"] = grade_effi
  1332. report_dict["scoreList"] = score_effi_list
  1333. # calculate grade_distribution
  1334. grade_effi_list = df_effi_cases['level'].values.tolist()
  1335. grade_effi_distribution = grade_statistic(grade_effi_list)
  1336. report_dict["levelDistribution"] = grade_effi_distribution
  1337. score_type_dict = {}
  1338. value_list_dict = {}
  1339. bad_count_dict = {}
  1340. good_rate_dict = {}
  1341. bad_rate_dict = {}
  1342. type_details_dict = {}
  1343. for type in type_list:
  1344. type_dict = {
  1345. "name": type_name_dict[type],
  1346. }
  1347. builtin_graph_dict = {}
  1348. custom_graph_dict = {}
  1349. df_effi_cases[type] = df_effi_cases["details"].apply(lambda x: x[type] if type in x.keys() else None)
  1350. df_effi_cases1 = df_effi_cases.dropna(subset=[type])
  1351. type_cases_dict = df_effi_cases1[type].to_dict()
  1352. df_type_cases = pd.DataFrame(type_cases_dict).T.dropna(how='all')
  1353. # calculate score_type and grade_type
  1354. score_type_list = df_type_cases['score'].values.tolist()
  1355. score_type = round(np.mean(score_type_list), 2)
  1356. grade_type = score_grade(score_type)
  1357. type_dict["score"] = score_type
  1358. type_dict["level"] = grade_type
  1359. score_type_dict[type] = score_type
  1360. # calculate grade_distribution
  1361. grade_type_list = df_type_cases['level'].values.tolist()
  1362. grade_type_distribution = grade_statistic(grade_type_list)
  1363. type_dict["gradeDistribution"] = grade_type_distribution
  1364. # calculate type indexes
  1365. # type_indexes_dict = df_type_cases['indexes'].explode().tolist()
  1366. # df_type_indexes = pd.DataFrame(type_indexes_dict)
  1367. dfs = df_type_cases['indexes'].apply(lambda x: pd.DataFrame(x).T)
  1368. df_type_indexes = pd.concat(dfs.tolist(), ignore_index=True)
  1369. # functionACC description
  1370. type_metric1_list = [] # good
  1371. type_metric2_list = [] # not good
  1372. type_metric3_list = [] # bad
  1373. # indexes
  1374. type_dict_indexes = {}
  1375. for metric in metric_dict[type]:
  1376. df_metric_indexes = df_type_indexes[df_type_indexes['name'] == f"{name_dict[metric]}"]
  1377. kind_metric = kind_dict[metric] if metric in bulitin_metric_list else custom_param_dict[metric]['kind'][0]
  1378. optimal_metric = optimal_dict[metric] if metric in bulitin_metric_list else \
  1379. custom_param_dict[metric]['optimal'][0]
  1380. multiple_metric0 = multiple_dict[metric][0] if metric in bulitin_metric_list else \
  1381. custom_param_dict[metric]['multiple'][0][0]
  1382. multiple_metric1 = multiple_dict[metric][1] if metric in bulitin_metric_list else \
  1383. custom_param_dict[metric]['multiple'][0][1]
  1384. if kind_metric == 1: # 越大越好
  1385. metric_value_list = df_metric_indexes['value'].astype(float).values.tolist()
  1386. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  1387. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  1388. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  1389. if not optimal_metric:
  1390. metric_bad_count = 0
  1391. else:
  1392. metric_bad_count = len([x for x in metric_value_list if x < float(optimal_metric)])
  1393. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  1394. metric_good_rate = round(100 - metric_bad_rate, 2)
  1395. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  1396. metric) if avg_metric_value > float(optimal_metric) else type_metric3_list.append(metric)
  1397. elif kind_metric == -1: # 越小越好
  1398. metric_value_list = df_metric_indexes['value'].astype(float).values.tolist()
  1399. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  1400. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  1401. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  1402. if not optimal_metric:
  1403. metric_bad_count = 0
  1404. else:
  1405. metric_bad_count = len([x for x in metric_value_list if x > float(optimal_metric)])
  1406. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  1407. metric_good_rate = round(100 - metric_bad_rate, 2)
  1408. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(
  1409. metric) if avg_metric_value < float(optimal_metric) else type_metric3_list.append(metric)
  1410. elif kind_metric == 0:
  1411. metric_value_list = df_metric_indexes['value'].astype(float).values.tolist()
  1412. avg_metric_value = round(np.mean(metric_value_list), 2) if metric_value_list else "-"
  1413. max_metric_value = max(metric_value_list) if metric_value_list else "-"
  1414. min_metric_value = max(metric_value_list) if metric_value_list else "-"
  1415. if not optimal_metric:
  1416. metric_bad_count = 0
  1417. else:
  1418. metric_bad_count = len([x for x in metric_value_list if (
  1419. x > float(optimal_metric) * multiple_metric1 or x < float(
  1420. optimal_metric) * multiple_metric0)])
  1421. metric_bad_rate = round(metric_bad_count / len(metric_value_list) * 100, 2) if metric_value_list else 0
  1422. metric_good_rate = round(100 - metric_bad_rate, 2)
  1423. if not optimal_metric:
  1424. type_metric1_list.append(metric)
  1425. else:
  1426. type_metric1_list.append(metric) if metric_bad_rate == 0 else type_metric2_list.append(metric) if (
  1427. float(optimal_metric) * multiple_metric1 > avg_metric_value > float(
  1428. optimal_metric) * multiple_metric0) else type_metric3_list.append(
  1429. metric)
  1430. type_dict_indexes[metric] = {
  1431. "name": f"{name_dict[metric]}",
  1432. "average": avg_metric_value,
  1433. "max": max_metric_value,
  1434. "min": min_metric_value,
  1435. "number": metric_bad_count
  1436. }
  1437. if not optimal_metric:
  1438. type_dict_indexes[metric]["range"] = f"-"
  1439. else:
  1440. if kind_metric == -1:
  1441. type_dict_indexes[metric]["range"] = f"[0, {optimal_metric}]"
  1442. elif kind_metric == 1:
  1443. type_dict_indexes[metric]["range"] = f"[{optimal_metric}, inf)"
  1444. elif kind_metric == 0:
  1445. type_dict_indexes[metric][
  1446. "range"] = f"[{float(optimal_metric) * multiple_metric0}, {float(optimal_metric) * multiple_metric1}]"
  1447. value_list_dict[metric] = metric_value_list
  1448. bad_count_dict[metric] = metric_bad_count
  1449. good_rate_dict[metric] = metric_good_rate
  1450. bad_rate_dict[metric] = metric_bad_rate
  1451. type_dict["indexes"] = type_dict_indexes
  1452. for metric in metric_dict[type]:
  1453. metric_data = {
  1454. "name": f"{name_dict[metric]}",
  1455. "data": value_list_dict[metric],
  1456. # "markLine": [optimal_dict[metric]]
  1457. }
  1458. if metric in bulitin_metric_list:
  1459. metric_data["markLine"] = [optimal_dict[metric]]
  1460. builtin_graph_dict[metric] = metric_data
  1461. else:
  1462. metric_data["markLine"] = [custom_param_dict[metric]['optimal'][0]]
  1463. custom_graph_dict[metric] = metric_data
  1464. type_dict["builtin"] = builtin_graph_dict
  1465. type_dict["custom"] = custom_graph_dict
  1466. # description
  1467. str_type_metric1 = ''
  1468. str_type_metric2 = ''
  1469. str_type_metric3 = ''
  1470. if len(type_metric1_list) == len(metric_list):
  1471. str_metric = string_concatenate(metric_list)
  1472. type_description1 = f"{str_metric}指标均表现良好,平均值在合理范围内且不存在不合格用例。"
  1473. elif len(type_metric3_list) == len(metric_list):
  1474. str_metric = string_concatenate(metric_list)
  1475. type_description1 = f"{str_metric}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  1476. else:
  1477. if type_metric1_list:
  1478. str_type1 = string_concatenate(type_metric1_list)
  1479. str_type_metric1 += f"{str_type1}指标表现良好,平均值在合理范围内且不存在不合格用例;"
  1480. for metric in type_metric2_list:
  1481. str_type_metric2 += f"{name_dict[metric]}指标表现不佳,存在{bad_count_dict[metric]}个不合格用例,需要改进算法在这些用例中的表现;"
  1482. if type_metric3_list:
  1483. str_type3 = string_concatenate(type_metric3_list)
  1484. str_type_metric3 += f"{str_type3}指标平均值在合理范围外,算法在大部分用例下均表现不佳,需重点优化。"
  1485. type_description1 = ''
  1486. type_description1 += (str_type_metric1 + '\n') if str_type_metric1 else ''
  1487. type_description1 += (str_type_metric2 + '\n') if str_type_metric2 else ''
  1488. type_description1 += str_type_metric3
  1489. type_description1 = type_description1[:-1] + "。"
  1490. # type_description2 = "经计算可知,"
  1491. # for metric in metric_dict[type]:
  1492. # type_description2 += f"{metric}指标位于合理区间的占比为{good_rate_dict[metric]}%,"
  1493. # type_description2 = type_description2[:-1] + "。"
  1494. type_dict["description1"] = replace_key_with_value(type_description1, name_dict)
  1495. # type_dict["description2"] = replace_key_with_value(type_description2, name_dict)
  1496. type_details_dict[type] = type_dict
  1497. report_dict["details"] = type_details_dict
  1498. # efficient description1
  1499. effi_list = []
  1500. ineffi_list = []
  1501. for metric in metric_list:
  1502. ineffi_list.append(metric) if bad_count_dict[metric] > 0 else effi_list.append(metric)
  1503. # generate effi_description1
  1504. effi_list_count = len(score_effi_list)
  1505. over_80_count = len([num for num in score_effi_list if num >= 80])
  1506. over_80_proportion = over_80_count / effi_list_count
  1507. below_80_count = effi_list_count - over_80_count
  1508. below_80_proportion = below_80_count / effi_list_count
  1509. below_60_count = len([num for num in score_effi_list if num < 60])
  1510. below_60_proportion = below_60_count / effi_list_count
  1511. effi_description1 = ''
  1512. if grade_effi == '优秀':
  1513. effi_description1 = '车辆行驶效率高;'
  1514. elif grade_effi == '良好':
  1515. effi_description1 = f'算法在本轮测试中的表现满足设计指标要求。其中有{over_80_count}个用例得分超过80分,占比为{over_80_proportion * 100:.2f}%;'
  1516. elif grade_effi == '一般':
  1517. str_ineffi_type = string_concatenate(ineffi_list)
  1518. effi_description1 = f'未满足设计指标要求。其中有{below_80_count}个用例得分低于80分,占比为{below_80_proportion * 100:.2f}%,需优化算法在{str_ineffi_type}上的表现;'
  1519. elif grade_effi == '较差':
  1520. str_ineffi_type = string_concatenate(ineffi_list)
  1521. effi_description1 = f'未满足设计指标要求。其中有{below_60_count}个用例得分低于60分,占比为{below_60_proportion * 100:.2f}%,需优化算法在{str_ineffi_type}上的表现;'
  1522. # efficient description2
  1523. if not ineffi_list:
  1524. effi_description2 = "算法在高效性维度上的表现满足设计指标要求。"
  1525. else:
  1526. effi_description2 = "算法应该优化车辆的规划控制逻辑,提高算法的通行效率。"
  1527. report_dict["description1"] = replace_key_with_value(effi_description1, type_name_dict)
  1528. report_dict["description2"] = replace_key_with_value(effi_description2, type_name_dict)
  1529. # report_dict = {
  1530. # "name": "高效性",
  1531. # "weight": f"{weight_efficient * 100}%",
  1532. # "score": score_effi,
  1533. # "level": grade_effi,
  1534. # "scoreList": score_effi_list,
  1535. # "levelDistribution": grade_effi_distribution,
  1536. # "description1": effi_description1,
  1537. # "description2": effi_description2,
  1538. # "description3": effi_description3,
  1539. # "indexes": [
  1540. # {
  1541. # "name": "平均速度(km/h)",
  1542. # "average": round(np.mean(avg_v_list), 2),
  1543. # "max": max(avg_v_list),
  1544. # "min": min(avg_v_list),
  1545. # "range": "-",
  1546. # "number": avg_v_bad_count
  1547. # }, {
  1548. # "name": "停车次数(次)",
  1549. # "average": round(np.mean(stop_count_list), 2),
  1550. # "max": max(stop_count_list),
  1551. # "min": min(stop_count_list),
  1552. # "range": f"[0, {optimal_dict['stopCount']}]",
  1553. # "number": stop_count_bad_count
  1554. # }, {
  1555. # "name": "停车平均时长(s)",
  1556. # "average": round(np.mean(stop_time_list), 2),
  1557. # "max": max(stop_time_list),
  1558. # "min": min(stop_time_list),
  1559. # "range": f"[0, {optimal_dict['stopDuration']}]",
  1560. # "number": stop_time_bad_count
  1561. # }
  1562. # ]
  1563. # }
  1564. return report_dict
  1565. def inverse_mileage_format(mileage):
  1566. if "公里" in mileage:
  1567. num = float(mileage[:-2]) * 1000
  1568. elif "米" in mileage:
  1569. num = float(mileage[:-1])
  1570. else:
  1571. raise ValueError("输入的字符串格式不正确")
  1572. return num
  1573. def inverse_duration_format(duration):
  1574. time_parts = duration.split("时")
  1575. hour = int(time_parts[0]) if len(time_parts) > 1 else 0
  1576. time_parts = time_parts[-1].split("分")
  1577. minute = int(time_parts[0]) if len(time_parts) > 1 else 0
  1578. if hour or minute:
  1579. second = int(time_parts[-1][:-1]) if "秒" in time_parts[-1] else 0
  1580. else:
  1581. second = float(time_parts[-1][:-1]) if "秒" in time_parts[-1] else 0
  1582. total_seconds = hour * 3600 + minute * 60 + second
  1583. return total_seconds
  1584. def multi_case_statistic(cases_dict, config):
  1585. """
  1586. Args:
  1587. cases_dict:
  1588. config:
  1589. Returns:
  1590. """
  1591. logger = log.get_logger()
  1592. cases_df = pd.DataFrame(cases_dict).T.dropna(how='all')
  1593. case_number = cases_df.shape[0]
  1594. # done_number = 0
  1595. # fail_number = 0
  1596. # sceneNumber = f"共测试{case_number}个用例,其中成功评价{done_number}个用例,有{fail_number}个用例评价失败"
  1597. sceneNumber = f"共测试{case_number}个用例"
  1598. cases_df['testMileage'] = cases_df['testMileage'].apply(inverse_mileage_format)
  1599. cases_df['testDuration'] = cases_df['testDuration'].apply(inverse_duration_format)
  1600. mileage_sum = mileage_format(cases_df['testMileage'].sum())
  1601. duration_sum = duration_format(cases_df['testDuration'].sum())
  1602. score_all = cal_score_from_80(cases_df['algorithmComprehensiveScore'].astype(float).values.tolist())
  1603. grade_all = score_grade(score_all)
  1604. multi_dict = {
  1605. 'sceneNumber': sceneNumber,
  1606. 'testMileageSum': mileage_sum,
  1607. 'testDurationSum': duration_sum,
  1608. 'algorithmComprehensiveScore': score_all,
  1609. 'algorithmLevel': grade_all,
  1610. }
  1611. dimension_details_dict = {}
  1612. # metric check
  1613. dimension_list = config.dimension_list
  1614. dimension_name_dict = config.dimension_name
  1615. bad_dimension_list = []
  1616. score_dimension_dict = {}
  1617. for dimension in dimension_list:
  1618. cases_df[dimension] = cases_df["details"].apply(lambda x: x[dimension] if dimension in x.keys() else None)
  1619. dimension_cases_dict = cases_df[dimension].to_dict()
  1620. if dimension in config.builtinDimensionList:
  1621. dimension_dict = globals()[f"{dimension[:4]}_multi_case_statistic"](dimension_cases_dict, config)
  1622. else:
  1623. dimension_dict = custom_multi_case_statistic(dimension_cases_dict, config, dimension)
  1624. pass
  1625. score_dimension_dict[dimension] = dimension_dict['score']
  1626. if score_dimension_dict[dimension] < 80:
  1627. bad_dimension_list.append(dimension)
  1628. dimension_details_dict[dimension] = dimension_dict
  1629. multi_dict["details"] = dimension_details_dict
  1630. if not bad_dimension_list:
  1631. algorithmResultDescription = '综上所述,算法在各个维度的表现俱佳。'
  1632. else:
  1633. str_bad_dimension = string_concatenate(bad_dimension_list)
  1634. algorithmResultDescription = f'综上所述,建议算法优化在{str_bad_dimension}指标上的表现。'
  1635. multi_dict['algorithmResultDescription'] = replace_key_with_value(algorithmResultDescription, dimension_name_dict)
  1636. # multi_dict = {
  1637. # 'sceneNumber': sceneNumber,
  1638. # 'testMileageSum': mileage_sum,
  1639. # 'testDurationSum': duration_sum,
  1640. # 'algorithmComprehensiveScore': score_all,
  1641. # 'algorithmResultDescription': algorithmResultDescription,
  1642. # 'algorithmLevel': grade_all,
  1643. # 'safe': report_dict,
  1644. # 'function': func_dict,
  1645. # 'compliance': report_dict,
  1646. # 'comfort': report_dict,
  1647. # 'efficient': report_dict
  1648. # }
  1649. return multi_dict
  1650. def multi_report_post(multi_dict, task_name):
  1651. """
  1652. This function generate the multiple cases report based on multi_dict.
  1653. Arguments:
  1654. multi_dict: A dict of single case scores and descriptions.
  1655. task_files: A str of path of files, which says where the generated report is to be stored.
  1656. Returns:
  1657. None
  1658. """
  1659. url_json = 'http://36.110.106.156:18081/report/generate'
  1660. data_json = json.dumps(multi_dict)
  1661. response = requests.post(url_json, data_json, headers={'Content-Type': 'application/json; charset=utf-8'})
  1662. runtime = time.strftime('%Y%m%d%H%M%S', time.localtime())
  1663. p = pathlib.Path(rf'C:\Users\cicv\Desktop\{task_name}_{runtime}.pdf')
  1664. # p = pathlib.Path(rf'..\results\report\{algorithmName}_{runtime}.pdf')
  1665. p.write_bytes(response.content)
  1666. def multi_report_generate(task_path, cases_dict_json_path, config_path, resPath):
  1667. logger = log.get_logger()
  1668. cases_dict = json2dict(os.path.join(cases_dict_json_path, "cases_dict.json"))
  1669. config = json2dict(os.path.join(config_path, "config.json"))
  1670. if len(cases_dict) > 1:
  1671. try:
  1672. # multiple cases report statistic and generate
  1673. multi_dict = multi_case_statistic(cases_dict, config)
  1674. if resPath:
  1675. with open(f'{resPath}/report.json', 'w', encoding='utf-8') as f:
  1676. f.write(json.dumps(multi_dict, ensure_ascii=False))
  1677. with open(f'{task_path}/report.json', 'w', encoding='utf-8') as f:
  1678. f.write(json.dumps(multi_dict, ensure_ascii=False))
  1679. except:
  1680. traceback.print_exc()
  1681. print(1)
  1682. logger.error("MULTIPLE_CASES_EVAL: Evaluate multiple cases ERROR!")
  1683. sys.exit(-1)