CoolFace
Apppublic

Aurelian-Chen/NovasX2

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
visualization.py507 linesDownload Raw Back to root
1import os2os.environ["STREAMLIT_BROWSER_GATHER_USAGE_STATS"] = "false"3os.environ["STREAMLIT_SERVER_ENABLE_WEBSOCKET_COMPRESSION"] = "false"4 5import pandas as pd6import plotly.express as px7import plotly.graph_objects as go8import streamlit as st9import numpy as np10from typing import Dict, List, Optional11from pricing_module import get_categories, get_platforms, get_follower_breakpoints, get_price12 13def format_large_number(num: float, is_english: bool = False) -> str:14    """将大数格式化为易读形式,保留两位小数,支持中英文格式15    16    Args:17        num: 要格式化的数字18        is_english: 是否使用英文格式 (True 使用K/M/B, False 使用万/亿)19    """20    # 移除多余小数位,保留最多两位小数21    def clean_decimal(n: float) -> str:22        s = f"{n:.2f}"23        # 如果小数部分是.00,则只返回整数部分24        if s.endswith('.00'):25            return s[:-3]26        # 如果小数部分以0结尾,则去掉末尾的027        elif s.endswith('0'):28            return s[:-1]29        return s30    31    if is_english:32        if num >= 1_000_000_000:  # Billion33            return f"{clean_decimal(num/1_000_000_000)}B"34        elif num >= 1_000_000:  # Million35            return f"{clean_decimal(num/1_000_000)}M"36        elif num >= 1_000:  # Thousand37            return f"{clean_decimal(num/1_000)}K"38        else:39            return clean_decimal(num)40    else:41        if num >= 100_000_000:  # 亿42            return f"{clean_decimal(num/100_000_000)}亿"43        elif num >= 10_000:  # 万44            return f"{clean_decimal(num/10_000)}万"45        elif num >= 1_000:  # 千46            return f"{clean_decimal(num/1_000)}千"47        else:48            return clean_decimal(num)49 50def format_price(price: float, is_english: bool = False) -> str:51    """将价格格式化为易读形式,保留两位小数,支持中英文格式52    53    Args:54        price: 要格式化的价格55        is_english: 是否使用英文格式 (True 使用$, False 使用¥)56    """57    # 移除多余小数位,保留最多两位小数58    def clean_decimal(n: float) -> str:59        s = f"{n:.2f}"60        # 如果小数部分是.00,则只返回整数部分61        if s.endswith('.00'):62            return s[:-3]63        # 如果小数部分以0结尾,则去掉末尾的064        elif s.endswith('0'):65            return s[:-1]66        return s67    68    if is_english:69        if price >= 1_000_000:70            return f"${clean_decimal(price/1_000_000)}M"71        elif price >= 1_000:72            return f"${clean_decimal(price/1_000)}K"73        else:74            return f"${clean_decimal(price)}"75    else:76        if price >= 10_000:77            return f"¥{clean_decimal(price/10_000)}万"78        else:79            return f"¥{clean_decimal(price)}"80 81def create_platform_comparison_chart(prices: Dict[str, float], title: str = "各平台报价对比") -> go.Figure:82    """创建平台报价对比柱状图"""83    platforms = list(prices.keys())84    values = list(prices.values())85    86    # 使用更饱和的蓝色系配色方案创建柱状图,提高对比度87    colors = ['rgba(65, 105, 225, 0.95)', 'rgba(100, 149, 237, 0.95)', 88              'rgba(30, 144, 255, 0.95)', 'rgba(70, 130, 180, 0.95)']89    90    fig = go.Figure()91    for i, (platform, value) in enumerate(zip(platforms, values)):92        fig.add_trace(go.Bar(93            x=[platform], 94            y=[value],95            name=platform,96            marker_color=colors[i % len(colors)],97            text=[format_price(value)],98            textposition='outside',  # 将文本放在柱状图外部,提高可见性99            textfont=dict(100                color='white',  # 确保文本为白色101                size=14,        # 增大字体大小102                family="Arial, sans-serif",  # 使用清晰的字体103            ),104            marker=dict(105                line=dict(106                    width=1,107                    color='rgba(255, 255, 255, 0.5)'  # 添加白色边框增强对比度108                )109            )110        ))111    112    fig.update_layout(113        title={114            'text': title,115            'font': {'size': 18, 'color': 'white'},  # 增大标题字体116            'y': 0.95,  # 调整标题位置117        },118        plot_bgcolor='rgba(0,0,0,0.1)',  # 轻微的黑色背景,增强对比度119        paper_bgcolor='rgba(0,0,0,0)',120        font=dict(121            color='white',122            size=14,  # 增大整体字体大小123            family="Arial, sans-serif"  # 使用清晰的字体124        ),125        showlegend=False,126        height=400,  # 适当增加高度,使内容更清晰127        autosize=True,  # 启用自动大小调整128        yaxis=dict(129            title='报价 (元)',130            title_font={'size': 15, 'color': 'white'},  # 轴标题字体大小和颜色131            gridcolor='rgba(255,255,255,0.2)',  # 增加网格线对比度132            tickfont={'size': 13, 'color': 'white'},  # 轴刻度字体大小和颜色133            tickmode='auto',134            nticks=6,  # 控制Y轴刻度数量135            showgrid=True,136            zeroline=True,137            zerolinecolor='rgba(255,255,255,0.3)',  # 零线颜色138            zerolinewidth=1139        ),140        xaxis=dict(141            title='社交媒体平台',142            title_font={'size': 15, 'color': 'white'},  # 轴标题字体大小和颜色143            tickfont={'size': 13, 'color': 'white'},  # 轴刻度字体大小和颜色144            tickangle=0,  # 保持刻度标签水平145        ),146        margin=dict(l=20, r=20, t=80, b=20),  # 增加顶部边距,给标题更多空间147    )148    149    return fig150 151def create_follower_price_curve(category: str, max_followers: float = 2000000, 152                               steps: int = 50) -> go.Figure:153    """创建粉丝量-价格曲线图"""154    platforms = get_platforms()155    follower_range = np.linspace(0, max_followers, steps)156    157    fig = go.Figure()158    159    # 使用更明亮的颜色以提高对比度160    colors = ['#4169E1', '#9370DB', '#6A5ACD', '#20B2AA']161    162    for i, platform in enumerate(platforms):163        prices = [get_price(platform, category, followers) for followers in follower_range]164        165        fig.add_trace(go.Scatter(166            x=follower_range,167            y=prices,168            mode='lines+markers',  # 添加标记点,增强可读性169            name=platform,170            line=dict(color=colors[i % len(colors)], width=4),  # 增加线宽171            marker=dict(size=6, opacity=0.7),  # 添加小标记点172            hovertemplate="粉丝数: %{x:,.0f}<br>价格: %{y:.2f}元<extra></extra>",173        ))174    175    # 添加粉丝基准点标记176    breakpoints = get_follower_breakpoints()177    if len(breakpoints) > 0:178        for bp in breakpoints:179            if bp <= max_followers:180                fig.add_vline(181                    x=bp, 182                    line=dict(color='rgba(255,255,255,0.4)', dash='dash', width=1.5),  # 增加线的可见度183                    annotation_text=format_large_number(bp),184                    annotation_position="top right",185                    annotation_font=dict(color="white", size=12)  # 确保标注文字清晰186                )187    188    fig.update_layout(189        title={190            'text': f'{category}类别不同粉丝量的价格曲线',191            'font': {'size': 18, 'color': 'white'}  # 增大标题字体192        },193        xaxis_title={194            'text': '粉丝数量',195            'font': {'size': 14, 'color': 'white'}  # 轴标题字体196        },197        yaxis_title={198            'text': '价格 (元)',199            'font': {'size': 14, 'color': 'white'}  # 轴标题字体200        },201        plot_bgcolor='rgba(0,0,0,0)',202        paper_bgcolor='rgba(0,0,0,0)',203        font=dict(204            color='white',205            size=13,  # 增大整体字体大小206            family="Arial, sans-serif"  # 使用清晰的字体207        ),208        height=450,  # 减少高度,更适合移动端209        autosize=True,  # 启用自动大小调整210        xaxis=dict(211            gridcolor='rgba(255,255,255,0.2)',  # 增加网格线对比度212            tickvals=breakpoints,213            ticktext=[format_large_number(bp) for bp in breakpoints],214            tickfont={'size': 12, 'color': 'white'}  # 确保刻度标签清晰215        ),216        yaxis=dict(217            gridcolor='rgba(255,255,255,0.2)',  # 增加网格线对比度218            tickfont={'size': 12, 'color': 'white'}  # 确保刻度标签清晰219        ),220        legend=dict(221            orientation="h",222            yanchor="bottom",223            y=1.02,224            xanchor="right",225            x=1,226            font=dict(size=13, color="white"),  # 增加图例字体大小227            bgcolor="rgba(0,0,0,0.4)",  # 添加半透明背景提高对比度228            bordercolor="rgba(255,255,255,0.3)",229            borderwidth=1230        ),231        margin=dict(l=20, r=20, t=100, b=40),  # 显著增加顶部和底部边距,解决移动端标题重叠问题232    )233    234    return fig235 236def create_category_comparison_radar(followers: float, platform: Optional[str] = None) -> go.Figure:237    """创建不同类别的价格雷达图"""238    categories = get_categories()239    240    if platform:241        # 单一平台的所有类别242        values = [get_price(platform, category, followers) for category in categories]243        244        fig = go.Figure()245        fig.add_trace(go.Scatterpolar(246            r=values,247            theta=categories,248            fill='toself',249            name=platform,250            line=dict(251                color='rgba(65, 105, 225, 0.9)',  # 增加颜色饱和度252                width=3  # 增加线宽253            ),254            fillcolor='rgba(65, 105, 225, 0.3)',  # 增加填充透明度255            hovertemplate='%{theta}: %{r:,.0f}元<extra></extra>'  # 添加悬浮提示256        ))257        258        title = f'{platform}平台在{format_large_number(followers)}粉丝量下各类别价格'259    else:260        # 所有平台的所有类别261        platforms = get_platforms()262        263        fig = go.Figure()264        # 使用更饱和的颜色265        colors = ['rgba(65, 105, 225, 0.9)', 'rgba(138, 43, 226, 0.9)', 266                 'rgba(72, 61, 139, 0.9)', 'rgba(106, 90, 205, 0.9)']267        268        for i, platform in enumerate(platforms):269            values = [get_price(platform, category, followers) for category in categories]270            271            fig.add_trace(go.Scatterpolar(272                r=values,273                theta=categories,274                fill='toself',275                name=platform,276                line=dict(277                    color=colors[i % len(colors)],278                    width=3  # 增加线宽279                ),280                fillcolor=colors[i % len(colors)].replace('0.9', '0.3'),  # 调整填充透明度281                hovertemplate='%{theta}: %{r:,.0f}元<extra></extra>'  # 添加悬浮提示282            ))283        284        title = f'各平台在{format_large_number(followers)}粉丝量下不同类别价格对比'285    286    fig.update_layout(287        polar=dict(288            radialaxis=dict(289                visible=True,290                gridcolor='rgba(255,255,255,0.25)',  # 增加网格线对比度291                linecolor='rgba(255,255,255,0.5)',   # 增加轴线对比度292                angle=45,  # 调整径向轴标签角度293                tickfont=dict(size=12, color='white')  # 径向轴刻度标签294            ),295            angularaxis=dict(296                gridcolor='rgba(255,255,255,0.25)',  # 增加网格线对比度297                linecolor='rgba(255,255,255,0.5)',   # 增加轴线对比度298                tickfont=dict(size=11, color='white', family="Arial, sans-serif")  # 角度轴刻度标签299            ),300            bgcolor='rgba(0,0,0,0)',301        ),302        title={303            'text': title,304            'font': {'size': 18, 'color': 'white'}  # 增大标题字体305        },306        plot_bgcolor='rgba(0,0,0,0)',307        paper_bgcolor='rgba(0,0,0,0)',308        font=dict(309            color='white',310            size=13,311            family="Arial, sans-serif"312        ),313        height=500,  # 减少高度,更适合移动端314        autosize=True,  # 启用自动大小调整315        legend=dict(316            orientation="h",317            yanchor="bottom",318            y=-0.1,319            xanchor="center",320            x=0.5,321            font=dict(size=13, color="white"),  # 增加图例字体大小322            bgcolor="rgba(0,0,0,0.4)",  # 添加半透明背景提高对比度323            bordercolor="rgba(255,255,255,0.3)",324            borderwidth=1325        ),326        margin=dict(l=40, r=40, t=100, b=50),  # 显著增加顶部和底部边距,解决移动端标题重叠问题327    )328    329    return fig330 331def create_platform_coefficient_heatmap(selected_categories: Optional[List[str]] = None) -> go.Figure:332    """创建平台系数热力图"""333    from pricing_module import COEFF_MATRIX334    335    platforms = get_platforms()336    337    if not selected_categories:338        categories = get_categories()339    else:340        categories = selected_categories341    342    # 准备热力图数据343    data = []344    for category in categories:345        row = [COEFF_MATRIX[category].get(platform, 1.0) for platform in platforms]346        data.append(row)347    348    df = pd.DataFrame(data, index=categories, columns=platforms)349    350    # 创建热力图 - 使用更适合深色背景的配色方案351    fig = px.imshow(352        df, 353        labels=dict(x="平台", y="内容类别", color="系数"),354        x=platforms,355        y=categories,356        color_continuous_scale='Plasma',  # 改用Plasma配色方案,在黑色背景上更加清晰357        aspect="auto",358        zmin=0.5,  # 设置最小值使颜色对比更明显359        zmax=2.0,  # 设置最大值360    )361    362    fig.update_layout(363        title={364            'text': '内容类别在不同平台的系数热力图',365            'font': {'size': 18, 'color': 'white'}  # 增大标题字体366        },367        plot_bgcolor='rgba(0,0,0,0)',368        paper_bgcolor='rgba(0,0,0,0)',369        font=dict(370            color='white',371            size=13,372            family="Arial, sans-serif"373        ),374        height=max(350, len(categories) * 18),  # 减少高度,更适合移动端375        autosize=True,  # 启用自动大小调整376        xaxis=dict(377            title={378                'text': '平台',379                'font': {'size': 14, 'color': 'white'}380            },381            side='top',382            tickfont={'size': 12, 'color': 'white'}383        ),384        yaxis=dict(385            title={386                'text': '内容类别',387                'font': {'size': 14, 'color': 'white'}388            },389            tickfont={'size': 12, 'color': 'white'}390        ),391        margin=dict(l=20, r=40, t=100, b=40),  # 显著增加顶部和底部边距,为颜色条提供更多空间392        coloraxis=dict(393            colorbar=dict(394                title={395                    'text': '系数值',396                    'font': {'size': 12, 'color': 'white'}397                },398                tickfont={'color': 'white', 'size': 12},399                outlinecolor='rgba(255,255,255,0.3)',400                outlinewidth=1401            )402        )403    )404    405    # 添加文本标注406    for i in range(len(categories)):407        for j in range(len(platforms)):408            # 根据系数值确定文本颜色,确保在各种背景下都清晰可见409            value = df.iloc[i, j]410            if value <= 0.8:411                text_color = "white"  # 低值区域用白色文本412            elif value <= 1.2:413                text_color = "yellow"  # 中值区域用黄色文本414            else:415                text_color = "black"  # 高值区域用黑色文本416            417            fig.add_annotation(418                x=j,419                y=i,420                text=f"{value:.2f}",421                showarrow=False,422                font=dict(423                    color=text_color,424                    size=12,425                    family="Arial, sans-serif"426                )427            )428    429    return fig430 431def create_top_categories_chart(followers: float, platform: str, n: int = 10) -> go.Figure:432    """创建指定平台下报价最高的前N个类别图表"""433    categories = get_categories()434    435    prices = []436    for category in categories:437        price = get_price(platform, category, followers)438        prices.append((category, price))439    440    # 按价格降序排序441    prices.sort(key=lambda x: x[1], reverse=True)442    443    # 取前N个444    top_categories = [p[0] for p in prices[:n]]445    top_prices = [p[1] for p in prices[:n]]446    447    # 创建水平条形图 - 使用渐变色使图表更加美观448    color_scale = px.colors.sequential.Blues_r  # 使用反转的蓝色渐变色449    # 生成一个颜色梯度,让排名靠前的类别颜色更深450    colors = [color_scale[int(i * (len(color_scale)-1) / (n-1) if n > 1 else 0)] for i in range(n)]451    452    fig = go.Figure()453    fig.add_trace(go.Bar(454        y=top_categories,455        x=top_prices,456        orientation='h',457        marker=dict(458            color=colors,459            opacity=0.9,  # 增加不透明度460            line=dict(width=1, color='rgba(255,255,255,0.3)')  # 添加细微边框461        ),462        text=[format_price(p) for p in top_prices],463        textposition='auto',464        textfont=dict(465            color='white',  # 确保文本为白色466            size=13,        # 增大字体大小467            family="Arial, sans-serif"  # 使用清晰的字体468        ),469        hovertemplate='%{y}: %{x:,.0f}元<extra></extra>'470    ))471    472    fig.update_layout(473        title={474            'text': f'{platform}平台{format_large_number(followers)}粉丝量下报价最高的{n}个类别',475            'font': {'size': 18, 'color': 'white'}  # 增大标题字体476        },477        plot_bgcolor='rgba(0,0,0,0)',478        paper_bgcolor='rgba(0,0,0,0)',479        font=dict(480            color='white',481            size=13,482            family="Arial, sans-serif"483        ),484        height=max(400, n * 28),  # 减少高度,更适合移动端,但仍保持动态调整485        autosize=True,  # 启用自动大小调整486        xaxis=dict(487            title={488                'text': '报价 (元)',489                'font': {'size': 14, 'color': 'white'}490            },491            gridcolor='rgba(255,255,255,0.2)',  # 增加网格线对比度492            tickfont={'size': 12, 'color': 'white'},  # 确保刻度标签清晰493            showgrid=True494        ),495        yaxis=dict(496            title={497                'text': '内容类别',498                'font': {'size': 14, 'color': 'white'}499            },500            categoryorder='total ascending',501            tickfont={'size': 12, 'color': 'white'}  # 确保刻度标签清晰502        ),503        margin=dict(l=20, r=20, t=100, b=40),  # 显著增加顶部和底部边距,解决移动端标题重叠问题504    )505    506    return fig507