Skip to content

Instantly share code, notes, and snippets.

@robintux
Created December 4, 2025 14:47
Show Gist options
  • Select an option

  • Save robintux/36b4a462bb1da56790f7920adb711c0d to your computer and use it in GitHub Desktop.

Select an option

Save robintux/36b4a462bb1da56790f7920adb711c0d to your computer and use it in GitHub Desktop.
import plotly.express as px
import pandas as pd
import numpy as np
from typing import Optional, List, Union
def visualize_palette_colors_enhanced(
palette_name: str = 'YlGnBu',
palette_category: str = 'sequential', # 'sequential', 'qualitative', 'diverging', 'cyclical'
show_values: bool = True,
show_legend: bool = False,
orientation: str = 'horizontal', # 'horizontal' o 'vertical'
custom_title: Optional[str] = None
):
"""
Visualiza paletas de cualquier categoría de Plotly
Parameters:
-----------
palette_name : str
Nombre de la paleta (ej: 'YlGnBu', 'Set1', 'RdYlBu')
palette_category : str
Categoría de la paleta: 'sequential', 'qualitative', 'diverging', 'cyclical'
show_values : bool
Si True, muestra los valores hexadecimales
show_legend : bool
Si True, muestra la leyenda de colores (útil para paletas cualitativas)
orientation : str
Orientación de las barras: 'horizontal' o 'vertical'
custom_title : str, optional
Título personalizado (si None, se genera automáticamente)
Returns:
--------
plotly.graph_objects.Figure or None
"""
# Mapeo de categorías válidas
VALID_CATEGORIES = {
'sequential': px.colors.sequential,
'qualitative': px.colors.qualitative,
'diverging': px.colors.diverging,
'cyclical': px.colors.cyclical,
'carto': px.colors.carto,
'colorbrewer': px.colors.colorbrewer,
# 'scientific': px.colors.scientific
}
# Validar categoría
if palette_category not in VALID_CATEGORIES:
print(f"⚠️ Categoría '{palette_category}' no válida.")
print(f"Categorías válidas: {list(VALID_CATEGORIES.keys())}")
return None
category_module = VALID_CATEGORIES[palette_category]
# Acceder a la paleta con manejo de errores mejorado
try:
palette = getattr(category_module, palette_name)
except AttributeError:
print(f"⚠️ Paleta '{palette_name}' no encontrada en {palette_category}.")
# Mostrar paletas disponibles en esa categoría
available = [p for p in dir(category_module)
if not p.startswith('_') and not p.endswith('_r')]
# Agrupar por tipo si hay muchas
if len(available) > 20:
print(f"Paletas disponibles en {palette_category} (primeras 20):")
for i in range(0, min(20, len(available)), 5):
print(f" {', '.join(available[i:i+5])}")
else:
print(f"Paletas disponibles en {palette_category}:")
print(f" {', '.join(available)}")
# Sugerir paletas similares
if '_' in palette_name or palette_name.lower() != palette_name:
# Intentar con formato diferente
suggestions = [p for p in available
if palette_name.lower() in p.lower()]
if suggestions:
print(f"\n💡 Sugerencias: {', '.join(suggestions[:3])}")
return None
# Obtener información de la paleta
n_colors = len(palette)
# Para paletas cualitativas, verificar si es lista de listas (Set1, Set2, etc.)
if (palette_category == 'qualitative' and
isinstance(palette, list) and
len(palette) > 0 and
isinstance(palette[0], list)):
# Las paletas cualitativas como Set1 tienen estructura anidada
# Aplanar la lista si es necesario
flat_palette = []
for item in palette:
if isinstance(item, list):
flat_palette.extend(item)
else:
flat_palette.append(item)
palette = flat_palette
n_colors = len(palette)
# Crear DataFrame para visualización
df = pd.DataFrame({
'Color_Index': range(n_colors),
'Hex_Color': palette,
'Position': np.linspace(0, 1, n_colors),
'Category': [f'Color {i}' for i in range(n_colors)]
})
# Determinar si usar escala de color continua o discreta
use_continuous_scale = palette_category in ['sequential', 'diverging', 'cyclical']
# Crear título
if custom_title is None:
title = (f'<b>Paleta: {palette_name}</b> '
f'<span style="font-size:12px;color:#666">({palette_category})</span><br>'
f'<span style="font-size:10px">Número de colores: {n_colors}</span>')
else:
title = custom_title
# Crear visualización según orientación
if orientation == 'horizontal':
fig = px.bar(
df,
y=[1] * n_colors, # Altura constante
x=df['Color_Index'],
color=df['Category'] if not use_continuous_scale else df['Color_Index'],
color_discrete_sequence=palette if not use_continuous_scale else None,
color_continuous_scale=palette if use_continuous_scale else None,
title=title,
labels={'x': 'Índice del Color', 'y': ''},
text=df['Hex_Color'] if show_values else None
)
else: # vertical
fig = px.bar(
df,
x=[1] * n_colors, # Ancho constante
y=df['Color_Index'],
color=df['Category'] if not use_continuous_scale else df['Color_Index'],
color_discrete_sequence=palette if not use_continuous_scale else None,
color_continuous_scale=palette if use_continuous_scale else None,
title=title,
labels={'y': 'Índice del Color', 'x': ''},
text=df['Hex_Color'] if show_values else None,
orientation='h'
)
# Personalizar según tipo de paleta
if use_continuous_scale:
# Para paletas secuenciales/divergentes: usar escala continua
fig.update_traces(
textposition='outside',
textfont=dict(size=10, color='black'),
hovertemplate=(
f'<b>Paleta: {palette_name} ({palette_category})</b><br>'
'Índice: %{x if orientation=="horizontal" else y}<br>'
'Color: %{text}<br>'
'Posición: %{customdata[0]:.3f}<extra></extra>'
),
customdata=df[['Position']],
width=1.0 if orientation == 'horizontal' else None,
text=1.0 if orientation == 'vertical' else None
)
fig.update_layout(coloraxis_showscale=False)
else:
# Para paletas cualitativas: usar colores discretos
fig.update_traces(
textposition='outside',
textfont=dict(size=10, color='black'),
hovertemplate=(
f'<b>Paleta: {palette_name} ({palette_category})</b><br>'
'Índice: %{x if orientation=="horizontal" else y}<br>'
'Color: %{text}<extra></extra>'
),
width=1.0 if orientation == 'horizontal' else None,
text=1.0 if orientation == 'vertical' else None
)
# Configurar layout común
if orientation == 'horizontal':
fig.update_layout(
xaxis=dict(
title='Índice del Color',
tickmode='array',
tickvals=list(range(n_colors)),
ticktext=[f'{i}' for i in range(n_colors)],
showgrid=False
),
yaxis=dict(
showticklabels=False,
showgrid=False,
zeroline=False
)
)
else: # vertical
fig.update_layout(
yaxis=dict(
title='Índice del Color',
tickmode='array',
tickvals=list(range(n_colors)),
ticktext=[f'{i}' for i in range(n_colors)],
showgrid=False,
autorange='reversed' # Para que el índice 0 esté arriba
),
xaxis=dict(
showticklabels=False,
showgrid=False,
zeroline=False
)
)
# Configuraciones adicionales
fig.update_layout(
showlegend=show_legend,
plot_bgcolor='white',
height=300 if orientation == 'horizontal' else 400,
width=max(800, n_colors * 30) if orientation == 'horizontal' else 500,
margin=dict(t=80, b=40, l=40, r=40)
)
# Añadir información adicional
annotations = []
# Información básica de la paleta
annotations.append(
dict(
x=0.02, y=1.08,
xref='paper', yref='paper',
text=f'<b>Primer color:</b> {palette[0]}<br><b>Último color:</b> {palette[-1]}',
showarrow=False,
align='left',
bgcolor='rgba(255, 255, 255, 0.8)',
bordercolor='lightgray',
borderwidth=1,
font=dict(size=10)
)
)
# Para paletas cualitativas, añadir información de uso recomendado
if palette_category == 'qualitative':
recommendations = {
'Set1': 'Ideal para ≤8 categorías, colores distintivos',
'Set2': 'Para 8+ categorías, tonos pastel',
'Set3': 'Para muchas categorías, amplia variedad',
'Pastel1': 'Estilo suave, presentaciones',
'Dark2': 'Alto contraste, accesibilidad'
}
if palette_name in recommendations:
annotations.append(
dict(
x=0.98, y=1.08,
xref='paper', yref='paper',
text=f'<b>Uso:</b> {recommendations[palette_name]}',
showarrow=False,
align='right',
bgcolor='rgba(173, 216, 230, 0.8)', # Light blue
bordercolor='steelblue',
borderwidth=1,
font=dict(size=9)
)
)
fig.update_layout(annotations=annotations)
return fig
# Función de conveniencia para visualizar múltiples paletas
def compare_palettes(
palette_list: List[dict],
n_cols: int = 2,
show_values: bool = False,
orientation: str = 'horizontal'
):
"""
Compara múltiples paletas en una cuadrícula
Parameters:
-----------
palette_list : list of dict
Lista de diccionarios con especificaciones de paletas
Ej: [{'name': 'Viridis', 'category': 'sequential'},
{'name': 'Set1', 'category': 'qualitative'}]
n_cols : int
Número de columnas en la cuadrícula
show_values : bool
Si True, muestra valores hexadecimales
orientation : str
Orientación de las barras
"""
from plotly.subplots import make_subplots
import plotly.graph_objects as go
n_palettes = len(palette_list)
n_rows = int(np.ceil(n_palettes / n_cols))
fig = make_subplots(
rows=n_rows, cols=n_cols,
subplot_titles=[f"{p['name']} ({p['category']})" for p in palette_list],
vertical_spacing=0.15,
horizontal_spacing=0.05
)
for idx, palette_spec in enumerate(palette_list):
row = idx // n_cols + 1
col = idx % n_cols + 1
# Crear gráfico individual
individual_fig = visualize_palette_colors_enhanced(
palette_name=palette_spec['name'],
palette_category=palette_spec['category'],
show_values=show_values,
show_legend=False,
orientation=orientation,
custom_title=None # Usar título del subplot
)
if individual_fig is None:
continue
# Añadir cada traza al subplot correspondiente
for trace in individual_fig.data:
fig.add_trace(trace, row=row, col=col)
# Copiar ejes y layout
if orientation == 'horizontal':
fig.update_xaxes(
title_text='Índice',
row=row, col=col,
showgrid=False
)
fig.update_yaxes(
showticklabels=False,
row=row, col=col,
showgrid=False
)
else:
fig.update_yaxes(
title_text='Índice',
row=row, col=col,
showgrid=False,
autorange='reversed'
)
fig.update_xaxes(
showticklabels=False,
row=row, col=col,
showgrid=False
)
# Ajustar layout general
fig.update_layout(
title_text='<b>Comparación de Paletas de Colores</b>',
title_x=0.5,
showlegend=False,
plot_bgcolor='white',
height=300 * n_rows,
width=800 if orientation == 'horizontal' else 500 * n_cols
)
return fig
# Ejemplos de uso
if __name__ == "__main__":
# Visualizar YlGnBu
import plotly.io as pio
pio.renderers.default = 'browser'
# Ejemplo 1: Visualizar paleta secuencial
fig1 = visualize_palette_colors_enhanced(
palette_name='Viridis',
palette_category='sequential',
show_values=True,
orientation='horizontal'
)
fig1.show()
# Ejemplo 2: Visualizar paleta cualitativa
fig2 = visualize_palette_colors_enhanced(
palette_name='Set1',
palette_category='qualitative',
show_values=True,
show_legend=True,
orientation='vertical'
)
fig2.show()
# Ejemplo 3: Comparar múltiples paletas
palettes_to_compare = [
{'name': 'Viridis', 'category': 'sequential'},
{'name': 'Plasma', 'category': 'sequential'},
{'name': 'Set1', 'category': 'qualitative'},
{'name': 'Set2', 'category': 'qualitative'},
{'name': 'RdYlBu', 'category': 'diverging'},
{'name': 'Twilight', 'category': 'cyclical'}
]
fig3 = compare_palettes(palettes_to_compare, n_cols=3, show_values=False)
fig3.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment