Преглед изворни кода

feat(标题相关性分析): 增强筛选和显示功能

- 新增下级数量筛选(默认≥2)
- 新增下级点击UV筛选(默认≥10)
- 新增下级排序选项(默认点击UV,可切换相似度)
- 回流率改为4位小数显示
- 页面宽度改为100%自适应

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
yangxiaohui пре 1 месец
родитељ
комит
789a8974d8
1 измењених фајлова са 44 додато и 9 уклоњено
  1. 44 9
      tasks/头部/进入前的I与头部I的相关性分析_v2/visualize.py

+ 44 - 9
tasks/头部/进入前的I与头部I的相关性分析_v2/visualize.py

@@ -296,7 +296,7 @@ html_content = f"""<!DOCTYPE html>
         * {{ margin: 0; padding: 0; box-sizing: border-box; }}
         * {{ margin: 0; padding: 0; box-sizing: border-box; }}
         body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
         body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                background: #f5f5f5; padding: 20px; }}
                background: #f5f5f5; padding: 20px; }}
-        .container {{ max-width: 1200px; margin: 0 auto; }}
+        .container {{ width: 100%; max-width: none; }}
         h1 {{ font-size: 24px; margin-bottom: 10px; color: #333; }}
         h1 {{ font-size: 24px; margin-bottom: 10px; color: #333; }}
         .subtitle {{ color: #666; margin-bottom: 20px; font-size: 14px; }}
         .subtitle {{ color: #666; margin-bottom: 20px; font-size: 14px; }}
 
 
@@ -409,6 +409,21 @@ html_content = f"""<!DOCTYPE html>
                 <label>最小点击:</label>
                 <label>最小点击:</label>
                 <input type="number" id="min-click-input" value="100" min="0" step="100" style="width:70px" onchange="onFilterChange()">
                 <input type="number" id="min-click-input" value="100" min="0" step="100" style="width:70px" onchange="onFilterChange()">
             </div>
             </div>
+            <div class="control-group">
+                <label>下级≥:</label>
+                <input type="number" id="min-children-input" value="2" min="1" max="20" style="width:50px" onchange="onFilterChange()">
+            </div>
+            <div class="control-group">
+                <label>下级点击≥:</label>
+                <input type="number" id="min-child-click-input" value="10" min="0" step="10" style="width:60px" onchange="onFilterChange()">
+            </div>
+            <div class="control-group">
+                <label>下级排序:</label>
+                <select id="child-sort-select" onchange="onFilterChange()">
+                    <option value="click" selected>点击UV</option>
+                    <option value="sim">相似度</option>
+                </select>
+            </div>
             <div class="control-group">
             <div class="control-group">
                 <label>Top:</label>
                 <label>Top:</label>
                 <input type="number" id="top-input" value="50" min="1" max="500" style="width:60px" onchange="onFilterChange()">
                 <input type="number" id="top-input" value="50" min="1" max="500" style="width:60px" onchange="onFilterChange()">
@@ -473,6 +488,9 @@ html_content = f"""<!DOCTYPE html>
             channel: document.getElementById('channel-select').value,
             channel: document.getElementById('channel-select').value,
             category: document.getElementById('category-select').value,
             category: document.getElementById('category-select').value,
             minClick: parseInt(document.getElementById('min-click-input').value) || 0,
             minClick: parseInt(document.getElementById('min-click-input').value) || 0,
+            minChildren: parseInt(document.getElementById('min-children-input').value) || 2,
+            minChildClick: parseInt(document.getElementById('min-child-click-input').value) || 10,
+            childSort: document.getElementById('child-sort-select').value,
             top: parseInt(document.getElementById('top-input').value) || 50
             top: parseInt(document.getElementById('top-input').value) || 50
         }};
         }};
     }}
     }}
@@ -593,18 +611,35 @@ html_content = f"""<!DOCTYPE html>
         shareData = categoryFiltered.share;
         shareData = categoryFiltered.share;
         videoData = categoryFiltered.video;
         videoData = categoryFiltered.video;
 
 
-        // 按最小点击UV过滤(分享标题层 + 视频标题层)
+        // 下级排序函数
+        const childSortFn = filters.childSort === 'click'
+            ? (a, b) => b.total_click - a.total_click
+            : (a, b) => b.sim - a.sim;
+
+        // 按最小点击UV过滤 + 下级点击过滤 + 下级排序
         if (shareData.share_titles) {{
         if (shareData.share_titles) {{
             shareData.share_titles = shareData.share_titles
             shareData.share_titles = shareData.share_titles
                 .filter(st => st.total_click >= filters.minClick)
                 .filter(st => st.total_click >= filters.minClick)
                 .map(st => {{
                 .map(st => {{
-                    st.video_titles = st.video_titles.filter(vt => vt.total_click >= filters.minClick);
+                    // 下级点击过滤 + 排序
+                    st.video_titles = st.video_titles
+                        .filter(vt => vt.total_click >= filters.minChildClick)
+                        .sort(childSortFn);
                     return st;
                     return st;
                 }})
                 }})
-                .filter(st => st.video_titles.length > 0);  // 移除没有视频标题的
+                .filter(st => st.video_titles.length >= filters.minChildren);  // 下级数量筛选
         }}
         }}
         if (videoData.videos) {{
         if (videoData.videos) {{
-            videoData.videos = videoData.videos.filter(v => v.total_click >= filters.minClick);
+            videoData.videos = videoData.videos
+                .filter(v => v.total_click >= filters.minClick)
+                .map(v => {{
+                    // 下级点击过滤 + 排序
+                    v.share_titles = v.share_titles
+                        .filter(st => st.total_click >= filters.minChildClick)
+                        .sort(childSortFn);
+                    return v;
+                }})
+                .filter(v => v.share_titles.length >= filters.minChildren);  // 下级数量筛选
         }}
         }}
 
 
         // 记录真实数量(过滤后、截取前)
         // 记录真实数量(过滤后、截取前)
@@ -728,12 +763,12 @@ html_content = f"""<!DOCTYPE html>
         return rate >= q66 ? 'good' : (rate <= q33 ? 'bad' : '');
         return rate >= q66 ? 'good' : (rate <= q33 ? 'bad' : '');
     }}
     }}
 
 
-    // 渲染三种回流率
+    // 渲染三种回流率(4位小数)
     function renderRates(item) {{
     function renderRates(item) {{
         return `
         return `
-            <span class="rate" style="${{rateGradient(item.overall_rate)}}">整体 ${{(item.overall_rate * 100).toFixed(1)}}%</span>
-            <span class="rate" style="${{rateGradient(item.head_rate)}}">头部 ${{(item.head_rate * 100).toFixed(1)}}%</span>
-            <span class="rate" style="${{rateGradient(item.rec_rate)}}">推荐 ${{(item.rec_rate * 100).toFixed(1)}}%</span>
+            <span class="rate" style="${{rateGradient(item.overall_rate)}}">整体 ${{item.overall_rate.toFixed(4)}}</span>
+            <span class="rate" style="${{rateGradient(item.head_rate)}}">头部 ${{item.head_rate.toFixed(4)}}</span>
+            <span class="rate" style="${{rateGradient(item.rec_rate)}}">推荐 ${{item.rec_rate.toFixed(4)}}</span>
         `;
         `;
     }}
     }}