Py.Cafe

Milk Quality Evolution Dashboard with Vizro

DocsPricing
  • app.py
  • historico_qualidade_fazendas_pre_clean1.csv
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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()