import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from vizro import Vizro
from vizro.models import (
Button,
Card,
Dropdown,
Graph,
Layout,
Page,
RadioItems,
Slider,
Tabs,
Text,
)
font_family = "Arial"
# Load your data
df = pd.read_csv('historico_qualidade_fazendas_pre_clean1.csv', sep=";")
# Datas
datas = {
"mes": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
"data": ["2023-09-01", "2023-10-01", "2023-11-01", "2023-12-01", "2024-01-01", "2024-02-01", "2024-03-01", "2024-04-01", "2024-05-01", "2024-06-01", "2024-07-01", "2024-08-01", "2024-09-01"],
"ano": ["2023", "2023", "2023", "2023", "2024", "2024", "2024", "2024", "2024", "2024", "2024", "2024", "2024"],
"mes_num": ["9", "10", "11", "12", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
"mes_name": ["setembro", "outubro", "novembro", "dezembro", "janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro"],
"mes_abr": ["set", "out", "nov", "dez", "jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set"]
}
df_datas = pd.DataFrame(datas)
# Use pd.melt for more efficient reshaping
id_vars = ['consultor', 'produtor', 'fazenda', 'centro', 'regional', 'veterinario', 'empresa', 'matricula']
value_vars = [col for col in df.columns if 'ccs.' in col or 'cpp.' in col]
melted_df = pd.melt(df, id_vars=id_vars, value_vars=value_vars, var_name='variable', value_name='value')
# Extract 'indicador' (ccs or cpp) and month number ('mes')
melted_df[['indicador', 'mes']] = melted_df['variable'].str.extract(r'(ccs|cpp)\.(\d+)', expand=True)
# Ensure 'mes' is treated as a string from the beginning:
melted_df['mes'] = melted_df['mes'].astype(str)
# Merge with df_datas, also ensuring 'mes' is string in df_datas
melted_df = pd.merge(melted_df, df_datas[['mes', 'data', 'mes_name', 'mes_abr']].astype({'mes': str}), on='mes', how='left')
# Rename 'data' column to 'date' for consistency
melted_df = melted_df.rename(columns={'data': 'date'})
# Convert 'date' to datetime
melted_df['date'] = pd.to_datetime(melted_df['date'])
# Clean fazenda names (same as before)
melted_df['fazenda'] = melted_df['fazenda'].str.replace('Propriedade |Propriedade de ', '', regex=True)
melted_df['fazenda'] = melted_df['fazenda'].fillna(melted_df['produtor'])
# Create a new column with full month name and year
melted_df['month_year'] = melted_df['mes_abr'] + '-' + melted_df['date'].dt.year.astype(str)
# Get month abbreviations from df_datas
meses = melted_df['month_year'].unique().tolist()
# Pivot the data for plotting, including 'regional' and 'month_year'
pivot_df = melted_df.pivot_table(index=['fazenda', 'date', 'mes', 'regional', 'month_year'], # Include month_year
columns=['indicador'],
values='value').reset_index()
# Get unique regional values and sort alphabetically starting with 'R1a'
regional_order = sorted(pivot_df['regional'].unique(), key=lambda x: (x != 'R1a', x))
# Plotagem CCS x CPP (CCS(cpp))
fig = px.scatter(pivot_df,
x="cpp",
y="ccs",
animation_frame="month_year", # Use the new column
animation_group="fazenda",
hover_name="fazenda",
# color_discrete_sequence=px.colors.qualitative.Bold, # Use 'Bold' color palette
range_x=[-20, 1000],
range_y=[-20, 2000],
title="<b>Evolução dos Indicadores de Qualidade do Leite CCS e CPP - SET. 2023 a SET. 2024</b>",
labels={"month_year": "Mês", # Update label
"cpp": "<b>CPP (x1000 UFC/ml)</b>",
"ccs": "<b>CCS (x1000 Cél./ml)</b>"},
color="regional",
category_orders={"regional": regional_order}, # Apply sorting to legend
custom_data=['mes'])
# Customize layout
fig.update_layout(
plot_bgcolor="#f0f0f0",
paper_bgcolor="#f8f9fa",
font=dict(family=font_family, size=14, color="#131314"), # Use font instead of font_dict
title_font=dict(family=font_family, size=20, color="#131314"),
title_x=0.5,
xaxis_title_font=dict(family=font_family, size=16, color="#131314"),
yaxis_title_font=dict(family=font_family, size=16, color="#131314"),
xaxis=dict(showline=True, linewidth=2, linecolor='darkgray', gridcolor='#d3d3d3'),
yaxis=dict(showline=True, linewidth=2, linecolor='darkgray', gridcolor='#d3d3d3'),
legend=dict(title="", x=1, xanchor="right"),
height=1080,
updatemenus=[dict(
type="buttons",
showactive=False,
y=-0.4, # Place buttons at the top right
x=0.5,
xanchor='left',
yanchor='bottom',
buttons=[dict(label="▶",
method="animate",
args=[None, {"frame": {"duration": 1000, "redraw": True},
"fromcurrent": True,
"transition": {"duration": 500,
"easing": "quadratic-in-out"}}]),
dict(label="⏸",
method="animate",
args=[[None], {"frame": {"duration": 0, "redraw": False},
"mode": "immediate",
"transition": {"duration": 0}}])])],
sliders=[dict(
active=0,
currentvalue={"prefix": "", 'visible': False,
"font": {"size": 20, "color": "#131314"}}, # Increased font size and set color to #131314
pad={"t": 50},
font={"size": 16, "color": "#131314"},
len=0.9,
x=0.1,
y=-0.1,
tickcolor='#131314', # Set tick color to blue
ticklen=10, # Set tick length
tickwidth=1, # Set tick width
bordercolor='#131314', # Set border color to blue
borderwidth=1, # Set border width
)],
)
# Add lines and annotations
fig.add_hline(y=200, line_dash="dash", line_color="red", annotation_text="CCS (Meta Lactaleite)",
annotation_position="bottom right", line_width=3) # Increased line width to 3
fig.add_hline(y=500, line_dash="dash", line_color="green", annotation_text="CCS (Referência MAPA/IN 31)",
annotation_position="bottom right", line_width=3) # Increased line width to 3
fig.add_vline(x=40, line_dash="dash", line_color="red", annotation_text="CPP (Meta Lactaleite)",
annotation_position="top right", line_width=3) # Increased line width to 3
fig.add_vline(x=300, line_dash="dash", line_color="green", annotation_text="CPP (Referência MAPA/IN 31)",
annotation_position="top right", line_width=3) # Increased line width to 3
fig.update_traces(marker=dict(size=10, color='rgb(77, 118, 214)'))
# len(fig.frames) and len(meses) might not be the same, use min to avoid IndexError
num_frames = min(len(fig.frames), len(meses))
for i, frame in enumerate(fig.frames):
if i < num_frames:
frame.layout.annotations = [go.layout.Annotation(
x=0.5, y=1.03, xref="paper", yref="paper",
text=f"<b><span style='font-family:Arial; color:#131314; border: 1px solid; border-radius: 5px;padding: 5px;'>{meses[i].upper()}</span></b>", # Modified line
showarrow=False, font=dict(size=20, family=font_family))]
else:
# Handle frames beyond the range of meses if necessary
# For example, you could keep the last month label or remove the annotation
frame.layout.annotations = [] # Remove annotation for extra frames
# Use enumerate to get both index and month abbreviation:
for i, mes in enumerate(meses):
fig.layout.sliders[0].steps[i].label = mes.upper()
# Define dashboard layout
dashboard = Vizro(
pages=[
Page(
title="Milk Quality Evolution",
layout=Layout(
grid=[[0, 1]],
col_widths=[1, 12],
row_heights=[2,10],
),
components=[
Card(
text="", # Add a text argument (can be empty if you only need the dropdown)
layout=Layout(
grid=[[0]], # Inner grid needed for card structure
col_widths=[1],
row_heights=[2],
components=[
Dropdown(
id="regional_dropdown",
options=[
{"label": regional, "value": regional}
for regional in regional_order
],
value=regional_order[0],
title="Select a Region:", # Added the title here
)
]
),
col=0,
),
Graph(
id="scatter_plot",
figure=fig,
col=1,
),
]
)
],
)
dashboard.run()