From 6628f663b636675bcaea316f2deaddf337de480e Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期五, 13 三月 2026 10:23:31 +0800
Subject: [PATCH] feat(米重分析): 新增稳态识别和预测功能页面并优化现有模型
---
app/pages/metered_weight_correlation.py | 777 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 777 insertions(+), 0 deletions(-)
diff --git a/app/pages/metered_weight_correlation.py b/app/pages/metered_weight_correlation.py
new file mode 100644
index 0000000..0afb421
--- /dev/null
+++ b/app/pages/metered_weight_correlation.py
@@ -0,0 +1,777 @@
+import streamlit as st
+import plotly.express as px
+import plotly.graph_objects as go
+import pandas as pd
+import numpy as np
+from datetime import datetime, timedelta
+from app.services.extruder_service import ExtruderService
+from app.services.main_process_service import MainProcessService
+
+def show_metered_weight_correlation():
+ # 鍒濆鍖栨湇鍔�
+ extruder_service = ExtruderService()
+ main_process_service = MainProcessService()
+
+ # 椤甸潰鏍囬
+ st.title("绫抽噸鐩稿叧鎬у垎鏋�")
+
+ # 鍒濆鍖栦細璇濈姸鎬佺敤浜庢棩鏈熷悓姝�
+ if 'mc_start_date' not in st.session_state:
+ st.session_state['mc_start_date'] = datetime.now().date() - timedelta(days=7)
+ if 'mc_end_date' not in st.session_state:
+ st.session_state['mc_end_date'] = datetime.now().date()
+ if 'mc_quick_select' not in st.session_state:
+ st.session_state['mc_quick_select'] = "鏈�杩�7澶�"
+ if 'mc_time_offset' not in st.session_state:
+ st.session_state['mc_time_offset'] = 0.0
+
+ # 瀹氫箟鍥炶皟鍑芥暟
+ def update_dates(qs):
+ st.session_state['mc_quick_select'] = qs
+ today = datetime.now().date()
+ if qs == "浠婂ぉ":
+ st.session_state['mc_start_date'] = today
+ st.session_state['mc_end_date'] = today
+ elif qs == "鏈�杩�3澶�":
+ st.session_state['mc_start_date'] = today - timedelta(days=3)
+ st.session_state['mc_end_date'] = today
+ elif qs == "鏈�杩�7澶�":
+ st.session_state['mc_start_date'] = today - timedelta(days=7)
+ st.session_state['mc_end_date'] = today
+ elif qs == "鏈�杩�30澶�":
+ st.session_state['mc_start_date'] = today - timedelta(days=30)
+ st.session_state['mc_end_date'] = today
+
+ def on_date_change():
+ st.session_state['mc_quick_select'] = "鑷畾涔�"
+
+ # 鏌ヨ鏉′欢鍖哄煙
+ with st.expander("馃攳 鏌ヨ閰嶇疆", expanded=True):
+ # 娣诲姞鑷畾涔� CSS 瀹炵幇鍝嶅簲寮忔崲琛�
+ st.markdown("""
+ <style>
+ /* 寮哄埗鍒楀鍣ㄦ崲琛� */
+ [data-testid="stExpander"] [data-testid="column"] {
+ flex: 1 1 120px !important;
+ min-width: 120px !important;
+ }
+ /* 閽堝鏃ユ湡杈撳叆妗嗗垪绋嶅井鍔犲涓�鐐� */
+ @media (min-width: 768px) {
+ [data-testid="stExpander"] [data-testid="column"]:nth-child(6),
+ [data-testid="stExpander"] [data-testid="column"]:nth-child(7) {
+ flex: 2 1 180px !important;
+ min-width: 180px !important;
+ }
+ }
+ </style>
+ """, unsafe_allow_html=True)
+
+ # 鍒涘缓甯冨眬
+ cols = st.columns([1, 1, 1, 1, 1, 1.5, 1.5, 1])
+
+ options = ["浠婂ぉ", "鏈�杩�3澶�", "鏈�杩�7澶�", "鏈�杩�30澶�", "鑷畾涔�"]
+ for i, option in enumerate(options):
+ with cols[i]:
+ # 鏍规嵁褰撳墠閫夋嫨鐘舵�佸喅瀹氭寜閽被鍨�
+ button_type = "primary" if st.session_state['mc_quick_select'] == option else "secondary"
+ if st.button(option, key=f"btn_mc_{option}", width='stretch', type=button_type):
+ update_dates(option)
+ st.rerun()
+
+ with cols[5]:
+ start_date = st.date_input(
+ "寮�濮嬫棩鏈�",
+ label_visibility="collapsed",
+ key="mc_start_date",
+ on_change=on_date_change
+ )
+
+ with cols[6]:
+ end_date = st.date_input(
+ "缁撴潫鏃ユ湡",
+ label_visibility="collapsed",
+ key="mc_end_date",
+ on_change=on_date_change
+ )
+
+ with cols[7]:
+ query_button = st.button("馃殌 寮�濮嬪垎鏋�", key="mc_query", width='stretch')
+
+ # 鏁版嵁瀵归綈璋冩暣
+ st.markdown("---")
+ offset_cols = st.columns([2, 4, 2])
+ with offset_cols[0]:
+ st.write("鈴憋笍 **鏁版嵁瀵归綈璋冩暣**")
+ with offset_cols[1]:
+ time_offset = st.slider(
+ "鏃堕棿鍋忕Щ (鍒嗛挓)",
+ min_value=0.0,
+ max_value=5.0,
+ value=st.session_state['mc_time_offset'],
+ step=0.1,
+ help="璋冩暣涓绘祦绋嬪拰娓╁害鏁版嵁鐨勬椂闂村亸绉伙紝浣垮叾涓庢尋鍑烘満绫抽噸鏁版嵁瀵归綈銆�"
+ )
+ st.session_state['mc_time_offset'] = time_offset
+ with offset_cols[2]:
+ st.write(f"褰撳墠鍋忕Щ: {time_offset} 鍒嗛挓")
+
+ # 杞崲涓篸atetime瀵硅薄
+ start_dt = datetime.combine(start_date, datetime.min.time())
+ end_dt = datetime.combine(end_date, datetime.max.time())
+
+ # 鏌ヨ澶勭悊 - 浠呰幏鍙栨暟鎹苟缂撳瓨鍒颁細璇濈姸鎬�
+ if query_button:
+ with st.spinner("姝e湪鑾峰彇鏁版嵁..."):
+ # 1. 鑾峰彇瀹屾暣鐨勬尋鍑烘満鏁版嵁锛堝寘鍚墍鏈夋椂闂寸偣鐨勮灪鏉嗚浆閫熷拰鏈哄ご鍘嬪姏锛�
+ df_extruder_full = extruder_service.get_extruder_data(start_dt, end_dt)
+
+ # 2. 鑾峰彇涓绘祦绋嬫帶鍒舵暟鎹�
+ df_main_speed = main_process_service.get_cutting_setting_data(start_dt, end_dt)
+
+ df_temp = main_process_service.get_temperature_control_data(start_dt, end_dt)
+
+ # 妫�鏌ユ槸鍚︽湁鏁版嵁
+ has_data = any([
+ df_extruder_full is not None and not df_extruder_full.empty,
+ df_main_speed is not None and not df_main_speed.empty,
+ df_temp is not None and not df_temp.empty
+ ])
+
+ if not has_data:
+ st.warning("鎵�閫夋椂闂存鍐呮湭鎵惧埌浠讳綍鏁版嵁锛岃灏濊瘯璋冩暣鏌ヨ鏉′欢銆�")
+ # 娓呴櫎缂撳瓨鏁版嵁
+ for key in ['cached_extruder_full', 'cached_main_speed', 'cached_temp', 'last_query_start', 'last_query_end']:
+ if key in st.session_state:
+ del st.session_state[key]
+ return
+
+ # 缂撳瓨鏁版嵁鍒颁細璇濈姸鎬�
+ st.session_state['cached_extruder_full'] = df_extruder_full
+ st.session_state['cached_main_speed'] = df_main_speed
+ st.session_state['cached_temp'] = df_temp
+ st.session_state['last_query_start'] = start_dt
+ st.session_state['last_query_end'] = end_dt
+
+ # 鏁版嵁澶勭悊鍜屽浘琛ㄦ覆鏌� - 姣忔搴旂敤閲嶆柊杩愯鏃舵墽琛岋紙鍖呮嫭璋冩暣鏃堕棿鍋忕Щ鏃讹級
+ if all(key in st.session_state for key in ['cached_extruder_full', 'cached_main_speed', 'cached_temp']):
+ with st.spinner("姝e湪鍒嗘瀽鏁版嵁鐩稿叧鎬�..."):
+ # 鑾峰彇缂撳瓨鏁版嵁
+ df_extruder_full = st.session_state['cached_extruder_full']
+ df_main_speed = st.session_state['cached_main_speed']
+ df_temp = st.session_state['cached_temp']
+
+ # 鑾峰彇褰撳墠鏃堕棿鍋忕Щ閲�
+ offset_delta = timedelta(minutes=st.session_state['mc_time_offset'])
+
+ # 澶勭悊鏁版嵁
+ if df_extruder_full is not None and not df_extruder_full.empty:
+ # 杩囨护鏈哄ご鍘嬪姏澶т簬2鐨勫��
+ df_extruder_filtered = df_extruder_full[df_extruder_full['head_pressure'] <= 2]
+
+ # 涓虹背閲嶆暟鎹垱寤哄亸绉诲悗鐨勬椂闂村垪锛堝彧瀵圭背閲嶆暟鎹繘琛屾椂闂村亸绉伙級
+ df_extruder_filtered['weight_time'] = df_extruder_filtered['time'] - offset_delta
+ else:
+ df_extruder_filtered = None
+
+ # 妫�鏌ユ槸鍚︽湁鏁版嵁
+ has_data = any([
+ df_extruder_filtered is not None and not df_extruder_filtered.empty,
+ df_main_speed is not None and not df_main_speed.empty,
+ df_temp is not None and not df_temp.empty
+ ])
+
+ if not has_data:
+ st.warning("鎵�閫夋椂闂存鍐呮湭鎵惧埌浠讳綍鏁版嵁锛岃灏濊瘯璋冩暣鏌ヨ鏉′欢銆�")
+ return
+
+ # 鏁版嵁鏁村悎涓庨澶勭悊
+ def integrate_data(df_extruder_filtered, df_main_speed, df_temp):
+ # 纭繚鎸ゅ嚭鏈烘暟鎹瓨鍦�
+ if df_extruder_filtered is None or df_extruder_filtered.empty:
+ return None
+
+ # 鍒涘缓鍙寘鍚背閲嶅拰鍋忕Щ鏃堕棿鐨勪富鏁版嵁闆�
+ df_weight = df_extruder_filtered[['weight_time', 'metered_weight']].copy()
+ df_weight.rename(columns={'weight_time': 'time'}, inplace=True) # 灏唚eight_time閲嶅懡鍚嶄负time浣滀负鍩哄噯鏃堕棿
+
+ # 鍒涘缓鍖呭惈铻烘潌杞�熷拰鍘熷鏃堕棿鐨勫畬鏁存暟鎹泦
+ # 娉ㄦ剰锛氳繖閲屼娇鐢ㄥ畬鏁寸殑铻烘潌杞�熸暟鎹紝鑰屼笉浠呬粎鏄笌绫抽噸瀵瑰簲鐨勬暟鎹偣
+ df_screw = df_extruder_filtered[['time', 'screw_speed_actual']].copy()
+
+ # 鍒涘缓鍖呭惈鏈哄ご鍘嬪姏鍜屽師濮嬫椂闂寸殑瀹屾暣鏁版嵁闆�
+ # 娉ㄦ剰锛氳繖閲屼娇鐢ㄥ畬鏁寸殑鏈哄ご鍘嬪姏鏁版嵁锛岃�屼笉浠呬粎鏄笌绫抽噸瀵瑰簲鐨勬暟鎹偣
+ df_pressure = df_extruder_filtered[['time', 'head_pressure']].copy()
+
+ # 浣跨敤鍋忕Щ鍚庣殑绫抽噸鏃堕棿鏁村悎铻烘潌杞�熸暟鎹�
+ # 鍏抽敭锛氫娇鐢╩erge_asof鏍规嵁鍋忕Щ鍚庣殑绫抽噸鏃堕棿鏌ユ壘鏈�鎺ヨ繎鐨勮灪鏉嗚浆閫熸暟鎹�
+ df_merged = pd.merge_asof(
+ df_weight.sort_values('time'),
+ df_screw.sort_values('time'),
+ on='time',
+ direction='nearest',
+ tolerance=pd.Timedelta('1min')
+ )
+
+ # 浣跨敤鍋忕Щ鍚庣殑绫抽噸鏃堕棿鏁村悎鏈哄ご鍘嬪姏鏁版嵁
+ # 鍏抽敭锛氫娇鐢╩erge_asof鏍规嵁鍋忕Щ鍚庣殑绫抽噸鏃堕棿鏌ユ壘鏈�鎺ヨ繎鐨勬満澶村帇鍔涙暟鎹�
+ df_merged = pd.merge_asof(
+ df_merged.sort_values('time'),
+ df_pressure.sort_values('time'),
+ on='time',
+ direction='nearest',
+ tolerance=pd.Timedelta('1min')
+ )
+
+ # 鏁村悎涓绘祦绋嬫暟鎹�
+ if df_main_speed is not None and not df_main_speed.empty:
+ df_main_speed = df_main_speed[['time', 'process_main_speed']]
+ df_merged = pd.merge_asof(
+ df_merged.sort_values('time'),
+ df_main_speed.sort_values('time'),
+ on='time',
+ direction='nearest',
+ tolerance=pd.Timedelta('1min')
+ )
+
+ # 鏁村悎娓╁害鏁版嵁
+ if df_temp is not None and not df_temp.empty:
+ temp_cols = ['time', 'nakata_extruder_screw_display_temp',
+ 'nakata_extruder_rear_barrel_display_temp',
+ 'nakata_extruder_front_barrel_display_temp',
+ 'nakata_extruder_head_display_temp']
+ df_temp_subset = df_temp[temp_cols].copy()
+ df_merged = pd.merge_asof(
+ df_merged.sort_values('time'),
+ df_temp_subset.sort_values('time'),
+ on='time',
+ direction='nearest',
+ tolerance=pd.Timedelta('1min')
+ )
+
+ # 閲嶅懡鍚嶅垪浠ユ彁楂樺彲璇绘��
+ df_merged.rename(columns={
+ 'screw_speed_actual': '铻烘潌杞��',
+ 'head_pressure': '鏈哄ご鍘嬪姏',
+ 'process_main_speed': '娴佺▼涓婚��',
+ 'nakata_extruder_screw_display_temp': '铻烘潌娓╁害',
+ 'nakata_extruder_rear_barrel_display_temp': '鍚庢満绛掓俯搴�',
+ 'nakata_extruder_front_barrel_display_temp': '鍓嶆満绛掓俯搴�',
+ 'nakata_extruder_head_display_temp': '鏈哄ご娓╁害'
+ }, inplace=True)
+
+ # 娓呯悊鏁版嵁
+ df_merged.dropna(subset=['metered_weight'], inplace=True)
+
+ return df_merged
+
+ # 鎵ц鏁版嵁鏁村悎
+ df_analysis = integrate_data(df_extruder_filtered, df_main_speed, df_temp)
+
+ if df_analysis is None or df_analysis.empty:
+ st.warning("鏁版嵁鏁村悎澶辫触锛岃妫�鏌ユ暟鎹川閲忔垨璋冩暣鏃堕棿鑼冨洿銆�")
+ return
+
+ # 閲嶅懡鍚嶇背閲嶅垪
+ df_analysis.rename(columns={'metered_weight': '绫抽噸'}, inplace=True)
+
+ # --- 鍘熷鏁版嵁瓒嬪娍鍥� ---
+ st.subheader("馃搱 鍘熷鏁版嵁瓒嬪娍鍥�")
+
+ # 鍒涘缓瓒嬪娍鍥�
+ fig_trend = go.Figure()
+
+ # 娣诲姞绫抽噸鏁版嵁锛堜娇鐢ㄥ亸绉诲悗鐨勬椂闂达級
+ if df_extruder_filtered is not None and not df_extruder_filtered.empty:
+ fig_trend.add_trace(go.Scatter(
+ x=df_extruder_filtered['weight_time'], # 浣跨敤鍋忕Щ鍚庣殑鏃堕棿
+ y=df_extruder_filtered['metered_weight'],
+ name='绫抽噸 (Kg/m) [宸插亸绉籡',
+ mode='lines',
+ line=dict(color='blue', width=2)
+ ))
+
+ # 娣诲姞铻烘潌杞�燂紙浣跨敤鍘熷鏃堕棿锛�
+ fig_trend.add_trace(go.Scatter(
+ x=df_extruder_filtered['time'], # 浣跨敤鍘熷鏃堕棿
+ y=df_extruder_filtered['screw_speed_actual'],
+ name='铻烘潌杞�� (RPM)',
+ mode='lines',
+ line=dict(color='green', width=1.5),
+ yaxis='y2'
+ ))
+
+ # 娣诲姞鏈哄ご鍘嬪姏锛堜娇鐢ㄥ師濮嬫椂闂达紝宸茶繃婊ゅぇ浜�2鐨勫�硷級
+ fig_trend.add_trace(go.Scatter(
+ x=df_extruder_filtered['time'], # 浣跨敤鍘熷鏃堕棿
+ y=df_extruder_filtered['head_pressure'],
+ name='鏈哄ご鍘嬪姏 (鈮�2)',
+ mode='lines',
+ line=dict(color='orange', width=1.5),
+ yaxis='y3'
+ ))
+
+ # 娣诲姞娴佺▼涓婚��
+ if df_main_speed is not None and not df_main_speed.empty:
+ fig_trend.add_trace(go.Scatter(
+ x=df_main_speed['time'],
+ y=df_main_speed['process_main_speed'],
+ name='娴佺▼涓婚�� (M/Min)',
+ mode='lines',
+ line=dict(color='red', width=1.5),
+ yaxis='y4'
+ ))
+
+ # 娣诲姞娓╁害鏁版嵁
+ if df_temp is not None and not df_temp.empty:
+ # 铻烘潌娓╁害
+ fig_trend.add_trace(go.Scatter(
+ x=df_temp['time'],
+ y=df_temp['nakata_extruder_screw_display_temp'],
+ name='铻烘潌娓╁害 (掳C)',
+ mode='lines',
+ line=dict(color='purple', width=1),
+ yaxis='y5'
+ ))
+ # 鍚庢満绛掓俯搴�
+ fig_trend.add_trace(go.Scatter(
+ x=df_temp['time'],
+ y=df_temp['nakata_extruder_rear_barrel_display_temp'],
+ name='鍚庢満绛掓俯搴� (掳C)',
+ mode='lines',
+ line=dict(color='pink', width=1),
+ yaxis='y5'
+ ))
+ # 鍓嶆満绛掓俯搴�
+ fig_trend.add_trace(go.Scatter(
+ x=df_temp['time'],
+ y=df_temp['nakata_extruder_front_barrel_display_temp'],
+ name='鍓嶆満绛掓俯搴� (掳C)',
+ mode='lines',
+ line=dict(color='brown', width=1),
+ yaxis='y5'
+ ))
+ # 鏈哄ご娓╁害
+ fig_trend.add_trace(go.Scatter(
+ x=df_temp['time'],
+ y=df_temp['nakata_extruder_head_display_temp'],
+ name='鏈哄ご娓╁害 (掳C)',
+ mode='lines',
+ line=dict(color='gray', width=1),
+ yaxis='y5'
+ ))
+
+ # 閰嶇疆瓒嬪娍鍥惧竷灞�
+ fig_trend.update_layout(
+ title=f'鍘熷鏁版嵁瓒嬪娍 (绫抽噸鍚戝墠鍋忕Щ {st.session_state["mc_time_offset"]} 鍒嗛挓)',
+ xaxis=dict(
+ title='鏃堕棿',
+ rangeslider=dict(visible=True),
+ type='date'
+ ),
+ yaxis=dict(
+ title='绫抽噸 (Kg/m)',
+ title_font=dict(color='blue'),
+ tickfont=dict(color='blue')
+ ),
+ yaxis2=dict(
+ title='铻烘潌杞�� (RPM)',
+ title_font=dict(color='green'),
+ tickfont=dict(color='green'),
+ overlaying='y',
+ side='right'
+ ),
+ yaxis3=dict(
+ title='鏈哄ご鍘嬪姏',
+ title_font=dict(color='orange'),
+ tickfont=dict(color='orange'),
+ overlaying='y',
+ side='right',
+ anchor='free',
+ position=0.85
+ ),
+ yaxis4=dict(
+ title='娴佺▼涓婚�� (M/Min)',
+ title_font=dict(color='red'),
+ tickfont=dict(color='red'),
+ overlaying='y',
+ side='right',
+ anchor='free',
+ position=0.75
+ ),
+ yaxis5=dict(
+ title='娓╁害 (掳C)',
+ title_font=dict(color='purple'),
+ tickfont=dict(color='purple'),
+ overlaying='y',
+ side='left',
+ anchor='free',
+ position=0.15
+ ),
+ legend=dict(
+ orientation="h",
+ yanchor="bottom",
+ y=1.02,
+ xanchor="right",
+ x=1
+ ),
+ height=600,
+ margin=dict(l=100, r=200, t=100, b=100),
+ hovermode='x unified',
+ dragmode='select',
+ )
+
+ # 鏄剧ず瓒嬪娍鍥�
+ selection = st.plotly_chart(fig_trend, width='stretch', config={'scrollZoom': True}, on_select='rerun' )
+
+ # 璋冭瘯杈撳嚭
+ # st.write("鍘熷 selection 瀵硅薄:", selection)
+
+ # 瀹氫箟鍒嗘瀽鍒�
+ analysis_cols = ['绫抽噸', '铻烘潌杞��', '鏈哄ご鍘嬪姏', '娴佺▼涓婚��', '铻烘潌娓╁害', '鍚庢満绛掓俯搴�', '鍓嶆満绛掓俯搴�', '鏈哄ご娓╁害']
+
+ # 瀹氫箟瑕佸垎鏋愮殑鍙傛暟
+ params = [
+ ('铻烘潌杞��', 'RPM'),
+ ('鏈哄ご鍘嬪姏', ''),
+ ('娴佺▼涓婚��', 'M/Min'),
+ ('铻烘潌娓╁害', '掳C'),
+ ('鍚庢満绛掓俯搴�', '掳C'),
+ ('鍓嶆満绛掓俯搴�', '掳C'),
+ ('鏈哄ご娓╁害', '掳C')
+ ]
+
+ # 姝g‘鎻愬彇
+ selected_data = None
+ if selection.selection and selection.selection.box:
+ boxs = selection.selection.box
+ # 鑾峰彇閫変腑妗嗙殑x杞磋寖鍥�
+ x_range = boxs[0]['x'][0], boxs[0]['x'][1]
+ st.write("x杞磋寖鍥�:", x_range)
+
+ # 杩囨护鍑哄湪x杞磋寖鍥村唴鐨勬暟鎹�
+ # 娉ㄦ剰锛氳繖閲岄渶瑕佷娇鐢╠f_analysis鐨則ime鍒楄繘琛岃繃婊�
+ # 棣栧厛闇�瑕佺‘淇漝f_analysis鏈塼ime鍒�
+ if 'time' in df_analysis.columns:
+ selected_data = df_analysis[
+ (df_analysis['time'] >= x_range[0]) &
+ (df_analysis['time'] <= x_range[1])
+ ].copy() # 浣跨敤copy()閬垮厤鍒囩墖璀﹀憡
+ st.write(f"閫変腑鑼冨洿鍐呯殑鏁版嵁鐐规暟閲�: {len(selected_data)}")
+ # 鏄剧ず鍙敤鐨勫垪鍚嶏紝甯姪璋冭瘯
+ st.write("鍙敤鍒楀悕:", list(selected_data.columns))
+ else:
+ st.warning("鏁版嵁涓己灏憈ime鍒楋紝鏃犳硶杩涜鑼冨洿杩囨护")
+
+ else:
+ st.info("璇蜂娇鐢ㄧ煩褰㈡閫夊伐鍏烽�夋嫨鏃堕棿鑼冨洿锛堝凡鑷姩鍚敤閫夋嫨妯″紡锛�")
+
+ # 娣诲姞缁嗚妭鍒嗘瀽鎸夐挳
+ if selected_data is not None and not selected_data.empty:
+ if st.button("馃攳 缁嗚妭鍒嗘瀽"):
+ st.subheader("馃搳 妗嗛�夎寖鍥寸粏鑺傚垎鏋�")
+
+ # 璁$畻閫変腑鑼冨洿鍐呯殑鐩稿叧绯绘暟鐭╅樀
+ selected_corr_matrix = selected_data[analysis_cols].corr()
+
+ # 鍒涘缓閫変腑鑼冨洿鐨勭儹鍔涘浘
+ selected_fig_heatmap = px.imshow(
+ selected_corr_matrix,
+ text_auto=True,
+ aspect="auto",
+ title="妗嗛�夎寖鍥村弬鏁扮浉鍏虫�х煩闃�",
+ color_continuous_scale=["#0000FF", "#FFFFFF", "#FF0000"],
+ color_continuous_midpoint=0,
+ labels=dict(color="鐩稿叧绯绘暟")
+ )
+
+ # 鑷畾涔夊竷灞�
+ selected_fig_heatmap.update_layout(
+ height=400,
+ margin=dict(l=80, r=80, t=80, b=80),
+ xaxis=dict(tickangle=-45),
+ yaxis=dict(tickangle=0)
+ )
+
+ # 鏄剧ず閫変腑鑼冨洿鐨勭儹鍔涘浘
+ st.plotly_chart(selected_fig_heatmap, width='stretch')
+
+ # 鏄剧ず閫変腑鑼冨洿鐨勫弬鏁颁笌绫抽噸鏁g偣鍥�
+ st.subheader("馃搱 妗嗛�夎寖鍥村弬鏁颁笌绫抽噸鏁g偣鍥�")
+
+ # 鍒涘缓閫変腑鑼冨洿鐨勬暎鐐瑰浘
+ for i in range(0, len(params), 2):
+ row_cols = st.columns(2)
+ for j in range(2):
+ if i + j < len(params):
+ param_name, unit = params[i + j]
+ with row_cols[j]:
+ if param_name in selected_data.columns:
+ # 璁$畻鐩稿叧绯绘暟锛堟坊鍔犻敊璇鐞嗭級
+ try:
+ # 杩囨护鎺塏aN鍊�
+ valid_data = selected_data[[param_name, '绫抽噸']].dropna()
+ if len(valid_data) >= 2: # 鑷冲皯闇�瑕�2涓暟鎹偣
+ corr_coef = np.corrcoef(valid_data['绫抽噸'], valid_data[param_name])[0, 1]
+ else:
+ corr_coef = None
+ except Exception as e:
+ corr_coef = None
+
+ # 鍒涘缓鏁g偣鍥�
+ fig_scatter = px.scatter(
+ selected_data,
+ x=param_name,
+ y='绫抽噸',
+ title=f"{param_name} vs 绫抽噸锛堟閫夎寖鍥达級",
+ labels={param_name: f"{param_name} ({unit})" if unit else param_name, '绫抽噸': '绫抽噸 (Kg/m)'}
+ )
+
+ # 娣诲姞瓒嬪娍绾匡紙娣诲姞閿欒澶勭悊锛�
+ try:
+ # 杩囨护鎺塏aN鍊�
+ valid_data = selected_data[[param_name, '绫抽噸']].dropna()
+ if len(valid_data) >= 2: # 鑷冲皯闇�瑕�2涓暟鎹偣
+ trend_line = np.poly1d(np.polyfit(valid_data[param_name], valid_data['绫抽噸'], 1))(valid_data[param_name])
+ fig_scatter.add_trace(go.Scatter(
+ x=valid_data[param_name],
+ y=trend_line,
+ mode='lines',
+ name='瓒嬪娍绾�',
+ line=dict(color='red', width=2)
+ ))
+ except Exception as e:
+ # 濡傛灉瓒嬪娍绾胯绠楀け璐ワ紝璺宠繃娣诲姞瓒嬪娍绾�
+ pass
+
+ # 娣诲姞鐩稿叧绯绘暟娉ㄩ噴锛堟坊鍔犻敊璇鐞嗭級
+ if corr_coef is not None:
+ fig_scatter.add_annotation(
+ x=0.05, y=0.95,
+ xref='paper', yref='paper',
+ text=f"鐩稿叧绯绘暟: {corr_coef:.4f}",
+ showarrow=False,
+ font=dict(size=12, color="black"),
+ bgcolor="white",
+ bordercolor="black",
+ borderwidth=1
+ )
+ else:
+ fig_scatter.add_annotation(
+ x=0.05, y=0.95,
+ xref='paper', yref='paper',
+ text="鐩稿叧绯绘暟: 鏃犳硶璁$畻",
+ showarrow=False,
+ font=dict(size=12, color="black"),
+ bgcolor="white",
+ bordercolor="black",
+ borderwidth=1
+ )
+
+ # 鏄剧ず鏁g偣鍥�
+ st.plotly_chart(fig_scatter, use_container_width=True)
+ else:
+ st.warning(f"鏁版嵁涓己灏� {param_name} 鍒�")
+
+ # 鏄剧ず閫変腑鑼冨洿鐨勬暟鎹憳瑕�
+ st.subheader("馃搳 妗嗛�夎寖鍥存暟鎹憳瑕�")
+ selected_summary_cols = st.columns(4)
+
+ with selected_summary_cols[0]:
+ if '绫抽噸' in selected_data.columns:
+ st.metric("骞冲潎绫抽噸", f"{selected_data['绫抽噸'].mean():.2f} Kg/m")
+
+ with selected_summary_cols[1]:
+ if '铻烘潌杞��' in selected_data.columns:
+ st.metric("骞冲潎铻烘潌杞��", f"{selected_data['铻烘潌杞��'].mean():.2f} RPM")
+
+ with selected_summary_cols[2]:
+ if '娴佺▼涓婚��' in selected_data.columns:
+ st.metric("骞冲潎娴佺▼涓婚��", f"{selected_data['娴佺▼涓婚��'].mean():.2f} M/Min")
+
+ with selected_summary_cols[3]:
+ if '鏈哄ご鍘嬪姏' in selected_data.columns:
+ st.metric("骞冲潎鏈哄ご鍘嬪姏", f"{selected_data['鏈哄ご鍘嬪姏'].mean():.2f}")
+
+ # 鏄剧ず閫変腑鑼冨洿鐨勬暟鎹瑙�
+ st.subheader("馃攳 妗嗛�夎寖鍥存暟鎹瑙�")
+ st.dataframe(selected_data[analysis_cols].head(10), use_container_width=True)
+
+ # --- 鐩稿叧鎬х煩闃电儹鍔涘浘 ---
+ st.subheader("馃搳 鐩稿叧鎬х煩闃电儹鍔涘浘")
+
+ # 閲嶅懡鍚嶇背閲嶅垪
+ df_analysis.rename(columns={'metered_weight': '绫抽噸'}, inplace=True)
+
+ # 璁$畻鐩稿叧绯绘暟鐭╅樀
+ corr_matrix = df_analysis[analysis_cols].corr()
+
+ # 鍒涘缓鐑姏鍥�
+ fig_heatmap = px.imshow(
+ corr_matrix,
+ text_auto=True,
+ aspect="auto",
+ title="鍙傛暟鐩稿叧鎬х煩闃�",
+ color_continuous_scale=["#0000FF", "#FFFFFF", "#FF0000"],
+ color_continuous_midpoint=0,
+ labels=dict(color="鐩稿叧绯绘暟")
+ )
+
+ # 鑷畾涔夊竷灞�
+ fig_heatmap.update_layout(
+ height=500,
+ margin=dict(l=100, r=100, t=100, b=100),
+ xaxis=dict(tickangle=-45),
+ yaxis=dict(tickangle=0)
+ )
+
+ # 鏄剧ず鐑姏鍥�
+ st.plotly_chart(fig_heatmap, width='stretch')
+
+ # --- 鍙傛暟涓庣背閲嶆暎鐐瑰浘 ---
+ st.subheader("馃搱 鍙傛暟涓庣背閲嶆暎鐐瑰浘")
+
+ # 鍒涘缓鏁g偣鍥�
+ for i in range(0, len(params), 2):
+ row_cols = st.columns(2)
+ for j in range(2):
+ if i + j < len(params):
+ param_name, unit = params[i + j]
+ with row_cols[j]:
+ if param_name in df_analysis.columns:
+ # 璁$畻鐩稿叧绯绘暟锛堟坊鍔犻敊璇鐞嗭級
+ try:
+ # 杩囨护鎺塏aN鍊�
+ valid_data = df_analysis[[param_name, '绫抽噸']].dropna()
+ if len(valid_data) >= 2: # 鑷冲皯闇�瑕�2涓暟鎹偣
+ corr_coef = np.corrcoef(valid_data['绫抽噸'], valid_data[param_name])[0, 1]
+ else:
+ corr_coef = None
+ except Exception as e:
+ corr_coef = None
+
+ # 鍒涘缓鏁g偣鍥�
+ fig_scatter = px.scatter(
+ df_analysis,
+ x=param_name,
+ y='绫抽噸',
+ title=f"{param_name} vs 绫抽噸",
+ labels={param_name: f"{param_name} ({unit})" if unit else param_name, '绫抽噸': '绫抽噸 (Kg/m)'}
+ )
+
+ # 娣诲姞瓒嬪娍绾匡紙娣诲姞閿欒澶勭悊锛�
+ try:
+ # 杩囨护鎺塏aN鍊�
+ valid_data = df_analysis[[param_name, '绫抽噸']].dropna()
+ if len(valid_data) >= 2: # 鑷冲皯闇�瑕�2涓暟鎹偣
+ trend_line = np.poly1d(np.polyfit(valid_data[param_name], valid_data['绫抽噸'], 1))(valid_data[param_name])
+ fig_scatter.add_trace(go.Scatter(
+ x=valid_data[param_name],
+ y=trend_line,
+ mode='lines',
+ name='瓒嬪娍绾�',
+ line=dict(color='red', width=2)
+ ))
+ except Exception as e:
+ # 濡傛灉瓒嬪娍绾胯绠楀け璐ワ紝璺宠繃娣诲姞瓒嬪娍绾�
+ pass
+
+ # 娣诲姞鐩稿叧绯绘暟娉ㄩ噴锛堟坊鍔犻敊璇鐞嗭級
+ if corr_coef is not None:
+ fig_scatter.add_annotation(
+ x=0.05, y=0.95,
+ xref='paper', yref='paper',
+ text=f"鐩稿叧绯绘暟: {corr_coef:.4f}",
+ showarrow=False,
+ font=dict(size=12, color="black"),
+ bgcolor="white",
+ bordercolor="black",
+ borderwidth=1
+ )
+ else:
+ fig_scatter.add_annotation(
+ x=0.05, y=0.95,
+ xref='paper', yref='paper',
+ text="鐩稿叧绯绘暟: 鏃犳硶璁$畻",
+ showarrow=False,
+ font=dict(size=12, color="black"),
+ bgcolor="white",
+ bordercolor="black",
+ borderwidth=1
+ )
+
+ # 鏄剧ず鏁g偣鍥�
+ st.plotly_chart(fig_scatter, use_container_width=True)
+ else:
+ st.warning(f"鏁版嵁涓己灏� {param_name} 鍒�")
+
+ # --- 鐩稿叧鎬х粺璁¤〃鏍� ---
+ st.subheader("馃搵 鐩稿叧鎬х粺璁�")
+
+ # 璁$畻姣忎釜鍙傛暟涓庣背閲嶇殑鐩稿叧绯绘暟锛堟坊鍔犻敊璇鐞嗭級
+ corr_stats = []
+ for param_name, _ in params:
+ if param_name in df_analysis.columns:
+ try:
+ # 杩囨护鎺塏aN鍊�
+ valid_data = df_analysis[[param_name, '绫抽噸']].dropna()
+ if len(valid_data) >= 2: # 鑷冲皯闇�瑕�2涓暟鎹偣
+ corr_coef = np.corrcoef(valid_data['绫抽噸'], valid_data[param_name])[0, 1]
+ corr_stats.append({
+ '鍙傛暟': param_name,
+ '鐩稿叧绯绘暟': corr_coef,
+ '鐩稿叧绋嬪害': '寮�' if abs(corr_coef) > 0.7 else '涓瓑' if abs(corr_coef) > 0.3 else '寮�'
+ })
+ else:
+ corr_stats.append({
+ '鍙傛暟': param_name,
+ '鐩稿叧绯绘暟': None,
+ '鐩稿叧绋嬪害': '鏃犳硶璁$畻'
+ })
+ except Exception as e:
+ corr_stats.append({
+ '鍙傛暟': param_name,
+ '鐩稿叧绯绘暟': None,
+ '鐩稿叧绋嬪害': '鏃犳硶璁$畻'
+ })
+
+ # 鍒涘缓缁熻琛ㄦ牸
+ corr_df = pd.DataFrame(corr_stats)
+ # 鎸夌浉鍏崇郴鏁扮粷瀵瑰�兼帓搴忥紙澶勭悊None鍊硷級
+ try:
+ # 璁$畻鐩稿叧绯绘暟缁濆鍊硷紝瀵逛簬None鍊间娇鐢�-1锛堣繖鏍蜂細鎺掑湪鏈�鍚庯級
+ corr_df['鐩稿叧绯绘暟缁濆鍊�'] = corr_df['鐩稿叧绯绘暟'].apply(lambda x: abs(x) if x is not None else -1)
+ corr_df.sort_values('鐩稿叧绯绘暟缁濆鍊�', ascending=False, inplace=True)
+ corr_df.drop('鐩稿叧绯绘暟缁濆鍊�', axis=1, inplace=True)
+ except Exception as e:
+ # 濡傛灉鎺掑簭澶辫触锛屼繚鎸佸師濮嬮『搴�
+ pass
+
+ # 鏄剧ず琛ㄦ牸
+ st.dataframe(corr_df, use_container_width=True)
+
+ # --- 鏁版嵁鎽樿 ---
+ # st.subheader("馃搳 鏁版嵁鎽樿")
+ # summary_cols = st.columns(4)
+
+ # with summary_cols[0]:
+ # if '绫抽噸' in df_analysis.columns:
+ # st.metric("骞冲潎绫抽噸", f"{df_analysis['绫抽噸'].mean():.2f} Kg/m")
+
+ # with summary_cols[1]:
+ # if '铻烘潌杞��' in df_analysis.columns:
+ # st.metric("骞冲潎铻烘潌杞��", f"{df_analysis['铻烘潌杞��'].mean():.2f} RPM")
+
+ # with summary_cols[2]:
+ # if '娴佺▼涓婚��' in df_analysis.columns:
+ # st.metric("骞冲潎娴佺▼涓婚��", f"{df_analysis['娴佺▼涓婚��'].mean():.2f} M/Min")
+
+ # with summary_cols[3]:
+ # if '鏈哄ご鍘嬪姏' in df_analysis.columns:
+ # st.metric("骞冲潎鏈哄ご鍘嬪姏", f"{df_analysis['鏈哄ご鍘嬪姏'].mean():.2f}")
+
+ # --- 鏁版嵁棰勮 ---
+ st.subheader("馃攳 鏁版嵁棰勮")
+ st.dataframe(df_analysis[analysis_cols].head(20), use_container_width=True)
+ else:
+ # 鎻愮ず鐢ㄦ埛鐐瑰嚮寮�濮嬪垎鏋愭寜閽�
+ st.info("璇烽�夋嫨鏃堕棿鑼冨洿骞剁偣鍑�'寮�濮嬪垎鏋�'鎸夐挳鑾峰彇鏁版嵁銆�")
--
Gitblit v1.9.3