Soil Acidification Iterative Evolution.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import * as echarts from '../../components/ec-canvas/echarts';
  2. Page({
  3. data: {
  4. initScatterData : [],
  5. ecLine: {
  6. onInit: function (canvas, width, height, dpr) {
  7. const lineChart = echarts.init(canvas, null, {
  8. width: width,
  9. height: height,
  10. devicePixelRatio: dpr // new
  11. });
  12. canvas.setChart(lineChart);
  13. lineChart.setOption(getLineOption());
  14. return lineChart;
  15. }
  16. },
  17. ecInitScatter: {
  18. onInit: function (canvas, width, height, dpr) {
  19. const initScatter = echarts.init(canvas, null, {
  20. width: width,
  21. height: height,
  22. devicePixelRatio: dpr
  23. });
  24. canvas.setChart(initScatter);
  25. getInitScatterOption().then(option => {
  26. initScatter.setOption(option)
  27. });
  28. return initScatter;
  29. }
  30. },
  31. ecMidScatter: {
  32. onInit: function (canvas, width, height, dpr) {
  33. const midScatter = echarts.init(canvas, null, {
  34. width: width,
  35. height: height,
  36. devicePixelRatio: dpr // new
  37. });
  38. canvas.setChart(midScatter);
  39. midScatter.setOption(getMidScatterOption());
  40. return midScatter;
  41. }
  42. },
  43. ecFinalScatter: {
  44. onInit: function (canvas, width, height, dpr) {
  45. const finalScatter = echarts.init(canvas, null, {
  46. width: width,
  47. height: height,
  48. devicePixelRatio: dpr // new
  49. });
  50. canvas.setChart(finalScatter);
  51. finalScatter.setOption(getFinalScatterOption());
  52. return finalScatter;
  53. }
  54. },
  55. },
  56. onReady() {
  57. // You can add any additional logic here if needed
  58. }
  59. });
  60. function getInitScatterOption() {
  61. const fetchScatterData = () => {
  62. return new Promise((resolve, reject) => {
  63. wx.request({
  64. url: 'https://soilgd.com:5000/model-scatter-data/24',
  65. method: 'GET',
  66. timeout: 5000,
  67. success: (res) => {
  68. console.log('接口响应:', res);
  69. // 校验状态码
  70. if (res.statusCode !== 200) {
  71. reject(new Error(`请求失败,状态码: ${res.statusCode}`));
  72. return;
  73. }
  74. // 校验数据结构
  75. if (!res.data?.scatter_data || !Array.isArray(res.data.scatter_data)) {
  76. reject(new Error('接口数据格式错误'));
  77. return;
  78. }
  79. resolve(res.data.scatter_data);
  80. },
  81. fail: (err) => {
  82. reject(new Error(`网络错误: ${err.errMsg}`));
  83. }
  84. });
  85. });
  86. };
  87. // 核心计算逻辑
  88. const calculateChartOptions = (scatterData) => {
  89. const calculateDataRange = (data) => {
  90. const xValues = data.map(item => item[0]);
  91. const yValues = data.map(item => item[1]);
  92. return {
  93. xMin: Math.min(...xValues),
  94. xMax: Math.max(...xValues),
  95. yMin: Math.min(...yValues),
  96. yMax: Math.max(...yValues)
  97. };
  98. };
  99. const range = calculateDataRange(scatterData);
  100. console.log(scatterData)
  101. const padding = 0.1;
  102. const xMin = range.xMin - Math.abs(range.xMin * padding);
  103. const xMax = range.xMax + Math.abs(range.xMax * padding);
  104. const yMin = range.yMin - Math.abs(range.yMin * padding);
  105. const yMax = range.yMax + Math.abs(range.yMax * padding);
  106. const axisMin = Math.min(xMin, yMin);
  107. const axisMax = Math.max(xMax, yMax);
  108. return {
  109. tooltip: {
  110. trigger: 'axis',
  111. axisPointer: { type: 'cross' }
  112. },
  113. grid: {
  114. left: '3%',
  115. right: '22%',
  116. bottom: '3%',
  117. containLabel: true
  118. },
  119. xAxis: {
  120. name: 'True Values',
  121. type: 'value',
  122. min: axisMin,
  123. max: axisMax,
  124. axisLabel: {
  125. formatter: value => parseFloat(value).toFixed(2)
  126. }
  127. },
  128. yAxis: {
  129. name: 'Predicted Values',
  130. type: 'value',
  131. min: parseFloat(axisMin).toFixed(2),
  132. max: parseFloat(axisMax).toFixed(2)
  133. },
  134. series: [
  135. {
  136. name: 'True vs Predicted',
  137. type: 'scatter',
  138. data: scatterData,
  139. symbolSize: 10,
  140. itemStyle: {
  141. color: '#1f77b4',
  142. opacity: 0.7,
  143. borderColor: '#fff',
  144. borderWidth: 0.5
  145. },
  146. },
  147. {
  148. name: 'Trendline',
  149. type: 'line',
  150. data: [[axisMin, axisMin], [axisMax, axisMax]],
  151. lineStyle: {
  152. type: 'dashed',
  153. color: '#ff7f0e',
  154. width: 2
  155. }
  156. }
  157. ]
  158. };
  159. };
  160. return new Promise(async (resolve) => {
  161. let retryCount = 0;
  162. const maxRetries = 2;
  163. while (retryCount <= maxRetries) {
  164. try {
  165. const scatterData = await fetchScatterData();
  166. resolve(calculateChartOptions(scatterData));
  167. return;
  168. } catch (error) {
  169. console.error(`第 ${retryCount + 1} 次尝试失败:`, error);
  170. if (retryCount === maxRetries) {
  171. resolve({
  172. xAxis: { show: false },
  173. yAxis: { show: false },
  174. series: [{
  175. type: 'scatter',
  176. data: [],
  177. silent: true
  178. }],
  179. graphic: {
  180. type: 'text',
  181. text: '数据加载失败',
  182. }
  183. })
  184. }
  185. retryCount++;
  186. await new Promise(r => setTimeout(r, 1000)); // 1秒后重试
  187. }
  188. }
  189. });
  190. }
  191. // 反酸模型 中间代 散点图
  192. function getMidScatterOption() {
  193. /**
  194. * 计算数据的最大最小值
  195. * @param {Array} data - 散点数据数组
  196. * @returns {Object} 包含 xMin, xMax, yMin, yMax 的对象
  197. */
  198. const calculateDataRange = (data) => {
  199. let xValues = data.map(item => item[0]);
  200. let yValues = data.map(item => item[1]);
  201. return {
  202. xMin: Math.min(...xValues),
  203. xMax: Math.max(...xValues),
  204. yMin: Math.min(...yValues),
  205. yMax: Math.max(...yValues)
  206. };
  207. };
  208. const scatterData = [[-0.003333333333333854, -0.5483999999999993], [-0.1733333333333329, -0.2093333333333331], [-0.6233333333333331, -0.5090000000000002], [-0.7088888888888892, -0.4281333333333331], [-0.3366666666666669, -0.4691333333333336], [-0.8888888888888887, -0.49643333333333267], [-0.5633333333333326, -0.7191999999999996], [-0.7333333333333325, -0.5024666666666666], [-0.3366666666666663, -0.37796666666666623], [-1.176666666666666, -0.6415666666666656], [-0.7122222222222225, -0.43299999999999966], [-0.7699999999999996, -0.499966666666667]];
  209. const range = calculateDataRange(scatterData);
  210. const padding = 0.1;
  211. const xMin = range.xMin - Math.abs(range.xMin * padding);
  212. const xMax = range.xMax + Math.abs(range.xMax * padding);
  213. const yMin = range.yMin - Math.abs(range.yMin * padding);
  214. const yMax = range.yMax + Math.abs(range.yMax * padding);
  215. const min = Math.min(xMin, yMin)
  216. const max = Math.max(xMax, yMax)
  217. return {
  218. tooltip: {
  219. trigger: 'axis',
  220. axisPointer: {
  221. type: 'cross'
  222. }
  223. },
  224. legend: {
  225. data: ['True vs Predicted']
  226. },
  227. grid: {
  228. left: '3%',
  229. right: '22%',
  230. bottom: '3%',
  231. containLabel: true
  232. },
  233. xAxis: {
  234. name: 'True Values',
  235. type: 'value',
  236. min: min,
  237. max: max
  238. },
  239. yAxis: {
  240. name: 'Predicted Values',
  241. type: 'value',
  242. min: parseFloat(min).toFixed(2),
  243. max: parseFloat(max).toFixed(2)
  244. },
  245. series: [
  246. {
  247. name: 'True vs Predicted',
  248. type: 'scatter',
  249. data: scatterData,
  250. symbolSize: 10,
  251. itemStyle: {
  252. color: '#1f77b4',
  253. opacity: 0.7
  254. }
  255. },
  256. {
  257. name: 'Trendline',
  258. type: 'line',
  259. data: [
  260. [min, min],
  261. [max, max]
  262. ],
  263. lineStyle: {
  264. type: 'dashed',
  265. color: '#ff7f0e',
  266. width: 2
  267. }
  268. }
  269. ]
  270. };
  271. }
  272. // 反酸模型 最终代 散点图
  273. function getFinalScatterOption() {
  274. /**
  275. * 计算数据的最大最小值
  276. * @param {Array} data - 散点数据数组
  277. * @returns {Object} 包含 xMin, xMax, yMin, yMax 的对象
  278. */
  279. const calculateDataRange = (data) => {
  280. let xValues = data.map(item => item[0]);
  281. let yValues = data.map(item => item[1]);
  282. return {
  283. xMin: Math.min(...xValues),
  284. xMax: Math.max(...xValues),
  285. yMin: Math.min(...yValues),
  286. yMax: Math.max(...yValues)
  287. };
  288. };
  289. const scatterData = [[-0.003333333333333854, -0.45726666666666654], [-0.1733333333333329, -0.1726333333333331],
  290. [-0.6233333333333331, -0.5226666666666667], [-0.7088888888888892, -0.4791888888888889],
  291. [-0.3366666666666669, -0.3630666666666673], [-0.8888888888888887, -0.48272222222222183],
  292. [-0.5633333333333326, -0.7492444444444444], [-0.7333333333333325, -0.5572666666666672],
  293. [-0.3366666666666663, -0.29379999999999984], [-1.176666666666666, -0.8544111111111106],
  294. [-0.7122222222222225, -0.4959777777777775], [-0.7699999999999996, -0.6149666666666669]];
  295. const range = calculateDataRange(scatterData);
  296. const padding = 0.1;
  297. const xMin = range.xMin - Math.abs(range.xMin * padding);
  298. const xMax = range.xMax + Math.abs(range.xMax * padding);
  299. const yMin = range.yMin - Math.abs(range.yMin * padding);
  300. const yMax = range.yMax + Math.abs(range.yMax * padding);
  301. const min = Math.min(xMin, yMin)
  302. const max = Math.max(xMax, yMax)
  303. return {
  304. tooltip: {
  305. trigger: 'axis',
  306. axisPointer: {
  307. type: 'cross'
  308. }
  309. },
  310. legend: {
  311. data: ['True vs Predicted']
  312. },
  313. grid: {
  314. left: '3%',
  315. right: '22%',
  316. bottom: '3%',
  317. containLabel: true
  318. },
  319. xAxis: {
  320. name: 'True Values',
  321. type: 'value',
  322. min: min,
  323. max: max
  324. },
  325. yAxis: {
  326. name: 'Predicted Values',
  327. type: 'value',
  328. min: parseFloat(min).toFixed(2),
  329. max: parseFloat(max).toFixed(2)
  330. },
  331. series: [
  332. {
  333. name: 'True vs Predicted',
  334. type: 'scatter',
  335. data: scatterData,
  336. symbolSize: 10,
  337. itemStyle: {
  338. color: '#1f77b4',
  339. opacity: 0.7
  340. }
  341. },
  342. {
  343. name: 'Trendline',
  344. type: 'line',
  345. data: [
  346. [min, min],
  347. [max, max]
  348. ],
  349. lineStyle: {
  350. type: 'dashed',
  351. color: '#ff7f0e',
  352. width: 2
  353. }
  354. }
  355. ]
  356. };
  357. }
  358. function getLineOption() {
  359. return {
  360. tooltip: {
  361. trigger: 'axis'
  362. },
  363. legend: {
  364. data: ['Random Forest', 'XGBoost', 'Gradient Boosting'] // 模型名称
  365. },
  366. grid: {
  367. left: '3%',
  368. right: '17%',
  369. bottom: '3%',
  370. containLabel: true
  371. },
  372. xAxis: {
  373. name: '模型迭代',
  374. type: 'category',
  375. boundaryGap: false,
  376. data: ['1代','2代','3代','4代','5代','6代','7代','8代','9代'] // train_sizes按10%递增
  377. },
  378. yAxis: {
  379. name: 'Score (R^2)',
  380. type: 'value'
  381. },
  382. series: [
  383. {
  384. name: 'Random Forest',
  385. type: 'line',
  386. data: [-0.17101591951095463, -0.556719360354051, -0.04083550751401055, -0.20858221504075436, 0.07297292282221035, 0.19857845644421734, 0.28407131176770184, 0.27979356883596496, 0.36904808817286416, 0.4183018571701477] // 使用您的实际R2分数数据
  387. },
  388. {
  389. name: 'XGBoost',
  390. type: 'line',
  391. data: [-1.1811781145886937, -1.5645641005612534, -0.12619079632263497, 0.03324096120721032, 0.06969290639267578, 0.12375262461601955, 0.5331670468884062, 0.49454793164801647, 0.31904329339597803, 0.2712670704381914]
  392. },
  393. {
  394. name: 'Gradient Boosting',
  395. type: 'line',
  396. data: [-0.8583039298789095, -1.073316171952042, 0.09659300885027666, 0.06097833957434784, 0.191975498544109, 0.3718334600546489, 0.3948098332187753, 0.4398778520728397, 0.452609022210963, 0.41484634172723023]
  397. }
  398. ]
  399. };
  400. }