| import faicons as fa |
| import plotly.express as px |
| import pandas as pd |
|
|
| |
| from shared import app_dir, tips |
| from shinywidgets import render_plotly |
| from shiny import reactive, render |
| from shiny.express import input, ui |
|
|
| |
| tips["tip"] = tips["احساس"].map({"مثبت": 1, "خنثی": 0, "منفی": -1}) |
|
|
| |
| bill_rng = (min(tips.سن), max(tips.سن)) |
|
|
| |
| ui.page_opts(title="تحلیل احساسات کاربران", fillable=True) |
|
|
| with ui.sidebar(open="desktop"): |
| ui.input_slider( |
| "سن", |
| "رنج سنی", |
| min=bill_rng[0], |
| max=bill_rng[1], |
| value=bill_rng, |
| pre="سال" |
| ) |
| ui.input_checkbox_group( |
| "تاریخ", |
| "شبکه اجتماعی", |
| tips["شبکه اجتماعی"].unique().tolist(), |
| selected=tips["شبکه اجتماعی"].unique().tolist(), |
| inline=True, |
| ) |
| ui.input_action_button("reset", "بازنشانی فیلتر") |
|
|
| |
| ICONS = { |
| "user": fa.icon_svg("user", "regular"), |
| "wallet": fa.icon_svg("wallet"), |
| "currency-dollar": fa.icon_svg("dollar-sign"), |
| "ellipsis": fa.icon_svg("ellipsis"), |
| } |
|
|
| |
| with ui.layout_columns(fill=False): |
| with ui.value_box(showcase=ICONS["user"]): |
| "تعداد کاربران" |
|
|
| @render.express |
| def total_tippers(): |
| data = tips_data() |
| ui.h3(f"تعداد کاربران: {data.shape[0]}") |
|
|
| with ui.value_box(showcase=ICONS["wallet"]): |
| "میانگین احساس" |
|
|
| @render.express |
| def average_tip(): |
| data = tips_data() |
| if data.shape[0] > 0: |
| ui.h3(f"میانگین احساس مثبت یا منفی: {data['tip'].mean():.2f}") |
| else: |
| ui.h3("دادهای برای محاسبه میانگین وجود ندارد.") |
|
|
| with ui.value_box(showcase=ICONS["currency-dollar"]): |
| "میانگین سن" |
|
|
| @render.express |
| def average_bill(): |
| data = tips_data() |
| if data.shape[0] > 0: |
| ui.h3(f"میانگین سن: {data['سن'].mean():.1f} سال") |
| else: |
| ui.h3("دادهای برای محاسبه میانگین سن وجود ندارد.") |
|
|
| |
| with ui.layout_columns(col_widths=[6, 6, 12]): |
| with ui.card(full_screen=True): |
| ui.card_header("جدول دادهها") |
|
|
| |
| @render.data_frame |
| def table(): |
| return tips_data() |
|
|
| |
| @render_plotly |
| def scatterplot(): |
| data = tips_data() |
| if data.shape[0] == 0: |
| return {} |
| return px.scatter( |
| data, |
| x="سن", |
| y="tip", |
| color="جنسیت", |
| trendline="lowess", |
| labels={"tip": "امتیاز احساس", "سن": "سن"}, |
| title="رابطه سن با احساس" |
| ) |
|
|
| with ui.card(full_screen=True): |
| |
| with ui.card_header(class_="d-flex justify-content-between align-items-center"): |
| "تحلیل پراکندگی احساس" |
| |
| with ui.popover(title="گروهبندی بر اساس متغیر"): |
| ICONS["ellipsis"] |
| ui.input_radio_buttons( |
| "tip_perc_y", |
| "گروهبندی بر اساس:", |
| ["جنسیت", "تأثیر", "سطح تأثیر", "موضوع"], |
| selected="جنسیت", |
| inline=True, |
| ) |
|
|
| |
| @render_plotly |
| def tip_perc(): |
| from ridgeplot import ridgeplot |
|
|
| dat = tips_data() |
| if dat.shape[0] == 0: |
| return {} |
|
|
| dat["percent"] = dat["tip"] |
| yvar = input.tip_perc_y() |
| uvals = dat[yvar].unique() |
|
|
| |
| samples = [[dat.percent[dat[yvar] == val]] for val in uvals] |
|
|
| |
| plt = ridgeplot( |
| samples=samples, |
| labels=uvals, |
| bandwidth=0.01, |
| colorscale="viridis", |
| colormode="row-index", |
| ) |
|
|
| |
| plt.update_layout( |
| legend=dict( |
| orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5 |
| ) |
| ) |
|
|
| return plt |
|
|
|
|
| |
| ui.include_css(app_dir / "styles.css") |
|
|
| |
| |
| |
|
|
| @reactive.calc |
| def tips_data(): |
| سنی = input.سن() |
| تاریخ_انتخابی = input.تاریخ() |
|
|
| idx1 = tips["سن"].between(سنی[0], سنی[1]) |
| idx2 = tips["شبکه اجتماعی"].isin(تاریخ_انتخابی) |
| |
| return tips[idx1 & idx2] |
|
|
| @reactive.effect |
| @reactive.event(input.reset) |
| def _(): |
| ui.update_slider("سن", value=bill_rng) |
| ui.update_checkbox_group("تاریخ", selected=tips["شبکه اجتماعی"].unique().tolist()) |