Created
December 4, 2025 14:47
-
-
Save robintux/36b4a462bb1da56790f7920adb711c0d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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