tabitaCatalan / CovidMTK

Covid multiclass model with virtual dispersal using Modelling Toolkit and my own Kalman Filter Julia Package
0 stars 0 forks source link

Mejorar las opciones para graficar los distintos estados #5

Closed tabitaCatalan closed 2 years ago

tabitaCatalan commented 2 years ago

Problema

Los gráficos no son satisfactorios, los labels se podrían mejorar, la notación científica no se ve bien, etc.

Grafico defectuoso

Cosas que mejorar

tabitaCatalan commented 2 years ago

Problema al LaTeXificar la variable α

Mi idea para "latexificar" las variables simbólicas tiene un problema al tratar la variable α. Mi idea era usar split_var_and_real_index que permite obtener, por ejemplo, la tupla ("R", "2") a partir de la variable simbólica R[2] creada con MTK (@variables t R[1,2](t)). Para hacer esto estaba buscando los índices de los caracteres [ y ], lo que funciona sin problema con las variables S, E, I, etc., pero que ya no funciona con la variable α. Esto es debido a que la indexación de los string en Julia es rara (por motivos de eficiencia, ver documentación). Esa indexación provoca cosas como esta:

julia> length("α[1](t)")
7

julia> lastindex("α[1](t)")
8

julia> "α[1](t)"[1]
'α': Unicode U+03B1 (category Ll: Letter, lowercase)

julia> "α[1](t)"[2]
ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'α', [3]=>'['
Stacktrace:
 [1] string_index_err(s::String, i::Int64)
   @ Base .\strings\string.jl:12
 [2] getindex_continued(s::String, i::Int64, u::UInt32)
   @ Base .\strings\string.jl:233
 [3] getindex(s::String, i::Int64)
   @ Base .\strings\string.jl:226
 [4] top-level scope
   @ REPL[219]:1

julia> "α[1](t)"[3]
'[': ASCII/Unicode U+005B (category Ps: Punctuation, open)

Solución

Siguiendo esta sugerencia decidí aplicar un collect al string "α[1](t)", lo que lo transforma en un arreglo de caracteres. Hay que reemplazar también el caracter α por la variable en LaTeX \alpha.

tabitaCatalan commented 2 years ago

Notación científica

Tenía un código parecido en plotting.jl, la función plot_scnotation!. Para que funcione se necesita:

tabitaCatalan commented 2 years ago

Gráfico que destaca una clase

Esto está casi listo, aunque tengo un problema: el grupo destacado debería ser el último en graficarse porque queda tapado por los grupos siguientes. Clase destacada parcialmente obstruida

En Matplotlib existe el argumento zorder para hacer esto, pero según el issue JuliaPlots/Plots.jl/issues/3834 eso no está implementado en Plots.jl.

Decidí reemplazar el ciclo for en las clases, reordenando las clases con la función put_at_the_end, de manera que la destacada quedara al final. class_to_highlight toma el valor de n (total de clases) por defecto, así que si hightlight es false debería verse normal.

# antes 
for class = 1:n
    ...
end

# ahora  
for class = put_at_the_end(1:n, class_to_highlight) 
    ...
end
tabitaCatalan commented 2 years ago

Latexificar Dates

Decidí usar multiple dispatch para definir una función to_latex_string para distintos tipos, que se encargue de "latexificar" correctamente las distintas cosas. En primera instancia estaba utilizado la 1ra componente de las xticks, y mientras solo usaba números no había problema, pero al tratar de usar fechas me topé con un problema con la lógica interna de Plots.jl al usar Dates. Me aparecía esto

julia> Plots.xticks(b_plot[5])
([737516.0, 737638.0, 737760.0, 737881.0], ["2020-04-01", "2020-08-01", "2020-12-01", "2021-04-01"])

Mirando el código de Plots.jl se ve que transforman las fechas a enteros/floats (no entiendo cómo ni para qué sentido). Eso funciona bien para posicionar los xtixcs, pero tiene como consecuencia que las xtixcs que efectivamente se muestran no tengan nada que ver.

Esto me obliga a usar los strings de 2da componente de los xticks, que para el caso en que uso fechas tiene la forma "yyyy-mm-dd", y cuando uso números es algo como "123.456". El motivo por el que no quería usar los strings es que cada caso debe ser tratado de forma independiente, y no tengo forma de diferenciarlos. Finalmente me incliné por una solución con dispatch.

Solución

Definí una función transform que recibe el string y algún dato de ejemplo que diga el tipo al que corresponde ese string.

transform(strdate, example_data::Date) = Date(strdate, "y-m-d") # retorna un Date
transform(strfloat, example_data::Float64) = parse(Float64, strfloat) # retorna un Float64 

La idea es que si uso transform en un string de la forma "2020-03-05" y tomando t como un Date cualquiera, podré parsear la fecha correctamente (y lo mismo para un Float).

Esto obliga, desde luego, a cambiar la interfaz de latexify_ticks!, puesto que ahora debe recibir el argumento example_data para transferirlo a transform.

latexify_ticks!(a_plot::Plots.Subplot, axis::Symbol, example_data)

A la hora de "latexificar" ambos ejes, necesitamos un tipo de dato de ejemplo para cada uno.

latexify_ticks!(a_plot::Plots.Plot, t0, y0)

La función transform se encarga de entregar el tipo correcto a to_latex_string, que deberá estar definida para los distintos tipos (Num, Float64, Date).

Problemas inesperados

1. Este comportamiento era inesperado (pensé que sería un Num), y no me permitía diferenciar adecuadamente a las variables simbólicas del modelo de los otros tipos de datos:

julia> eltype(states(simple_episys_uknown))
Any

Afortunadamente, esto se resolvió de manera sencilla transformando a Num las variables simbólicas dentro de plot_all_states_grid (Num(symstates[index])).

2. to_latex_string para Dates dio algunos problemas porque no lograba encontrar la sintaxis para mezclar texto y matemáticas dentro de una latex string. La forma que me dio resultado fue esta:

function to_latex_string(t::Date)
    (y, m, d) = Dates.yearmonthday(t)
    strm = Dates.monthabbr(m; locale = "spanish")
    L"%$d \textrm{/%$(strm)/} %$y" # esta parte fue la difícil
end 

Llegué a ella por medio del issue JuliaPlots/Plots.jl/issues/3203.

Extras

tabitaCatalan commented 2 years ago

Gráficos finales

El gráfico estándar ahora se ve así, y puede obtenerse con el código:

plot_all_states_grid(tsdate, xs, Ps, states(simple_episys_uknown))

Gráfico de todas las

(tsdate es un rango de Dates)

Ahora además existe la posibilidad de destacar una clase:

plot_all_states_grid(tsdate, xs, Ps, states(simple_episys_uknown), highlight = true, class_to_highlight = 4)

Gráfico destacando una clase