Programming
functions_inner
import pandas as pd
import numpy as np
import numpy as np
import plotly.graph_objects as go
def format_number(value):
try:
value = float(value)
"""Format large numbers to B (billions) or T (trillions)."""
if abs(value) >= 1_000_000_000_000:
return f"{value / 1_000_000_000_000:.1f} Трлн."
elif abs(value) >= 1_000_000_000:
return f"{value / 1_000_000_000:.1f} Млрд."
elif abs(value) >= 1_000_000:
return f"{value / 1_000_000:.1f} Млн."
return f"{value:,.0f} Сум"
except (ValueError, TypeError):
return value
def prepare_df(df, column, dept=None):
try:
df.loc[df['department'] == 'sk', 'deprtament'] = 'rk'
df = df[df['brutto_95_amount']>0]
if dept:
df = df[df['department']==dept]
df['is_bad_npl'] = df['npl_category'] > 2
df['brutto_bad'] = df['brutto_amount'].where(df['is_bad_npl'], 0)
df['brutto_good'] = df['brutto_amount'].where(~df['is_bad_npl'], 0)
print("1. Grouping")
# 3. Group by whatever dimension you need dynamically (e.g. department)
grouped = df.groupby(column).agg(
brutto_bad=('brutto_bad', 'sum'),
brutto_good=('brutto_good', 'sum')
).reset_index()
print("2. Calculations")
# 4. Calculate calculated totals & relative risk shares
grouped['brutto_total'] = grouped['brutto_bad'] + grouped['brutto_good']
grouped['bad_share'] = (grouped['brutto_bad'] / grouped['brutto_total'] * 100).fillna(0)
grouped['good_share'] = (grouped['brutto_good'] / grouped['brutto_total'] * 100).fillna(0)
grouped = grouped.sort_values('brutto_total', ascending = True)
# 5. Format using your local layout number strings
grouped['brutto_bad_formatted'] = grouped['brutto_bad'].apply(format_number)
grouped['brutto_good_formatted'] = grouped['brutto_good'].apply(format_number)
grouped['brutto_total_formatted'] = grouped['brutto_total'].apply(format_number)
return grouped
except Exception as e:
print(f'{e} Error happened')
def generate_risk_chart(df, group_col, target_font="Arial", custom_color=None):
COLOR_MAP = {
'kk': '#5E4D21',
'rk': '#1e3655',
'mk': '#174d39',
}
active_dept = df['department'].iloc[0] if 'department' in df else 'All'
good_color = COLOR_MAP.get(active_dept, custom_color or '#22e3a3')
# --- SCROLL LOGIC SYSTEM ---
ROW_LIMIT = 8 # Max bars to show without scrolling
ROW_HEIGHT = 45 # Pixels allocated per bar
BASE_PADDING = 100 # Pixels needed for margins and legend
num_rows = len(df)
#should set like > but I am finding this better
if num_rows < ROW_LIMIT:
# Calculate extended height if over the limit
calculated_height = (num_rows * ROW_HEIGHT) + BASE_PADDING
else:
# Use your default static height if under the limit
calculated_height = 450
hover_matrix = np.column_stack((
df['brutto_good_formatted'], df['good_share'].round(1).astype(str) + '%',
df['brutto_bad_formatted'], df['bad_share'].round(1).astype(str) + '%',
df['brutto_total_formatted']
))
hover_template = (
'<b>%{y}</b><br>'
'Стандарт: %{customdata[0]} (%{customdata[1]})<br>'
'NPL (90+): %{customdata[2]} (%{customdata[3]})<br>'
'Всего Портфел: %{customdata[4]}<extra></extra>'
)
hover_style = dict(bgcolor="white", font=dict(size=12), namelength=-1)
traces_config = [
{'x': df['brutto_bad'], 'name': 'NPL 90+', 'color': '#ef4444', 'text': None},
{'x': df['brutto_good'], 'name': 'Стандарт', 'color': good_color, 'text': df['brutto_total_formatted']}
]
fig = go.Figure()
for t in traces_config:
fig.add_trace(go.Bar(
y=df[group_col],
x=t['x'],
orientation='h',
name=t['name'],
marker=dict(color=t['color'], cornerradius=3),
opacity=0.9,
text=t['text'],
textposition='auto' if t['text'] is not None else 'none',
hovertemplate=hover_template,
hoverlabel=hover_style,
customdata=hover_matrix,
textfont=dict(size=16, color='#fff')
))
fig.update_layout(
barmode='stack',
showlegend=True,
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
margin=dict(l=5, r=5, t=5, b=5),
height=calculated_height, # Dynamic height injected here
font=dict(size=16, family=target_font),
legend=dict(
orientation='h', yanchor='bottom', y=1.02,
xanchor='right', x=1, font=dict(size=14)
)
)
fig.update_yaxes(tickfont=dict(size=12))
buttons_to_kill = [
'hoverClosestCartesian', 'pan2d', 'select2d', 'zoom2d',
'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'
]
chart_html = fig.to_html(
full_html=False,
include_plotlyjs=False,
config={"modeBarButtonsToRemove": buttons_to_kill, "displaylogo": False}
)
# --- HTML WRAPPER SYSTEM ---
# Wraps the raw Plotly div in a scrollable CSS container
max_container_height = 450
wrapper_html = f"""
<div style="max-height: {max_container_height}px; overflow-y: auto; overflow-x: hidden;">
{chart_html}
</div>
"""
return wrapper_html
PO
powerty
Author
· Staff
Aug. 14, 2026
Aug. 18, 2026
11
Views
0
Likes
2m
Read