import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import numpy as np
import pandas as pd
from sklearn.manifold import TSNE
from sklearn.cluster import KMeans
def generate_lipstick_dataset(n=100):
np.random.seed(42)
n = n // 3 * 3
reds = np.random.randint(100, 256, (n//3, 1))
pinks = np.random.randint(50, 200, (n//3, 1))
nudes = np.random.randint(150, 230, (n//3, 1))
red_shades = np.concatenate((reds, np.random.randint(0, 100, (n//3, 2))), axis=1)
pink_shades = np.concatenate((np.random.randint(150, 256, (n//3, 1)), pinks, np.random.randint(150, 256, (n//3, 1))), axis=1)
nude_shades = np.concatenate((nudes, nudes, nudes), axis=1)
shades = np.vstack((red_shades, pink_shades, nude_shades))
df = pd.DataFrame(shades, columns=['R', 'G', 'B'])
df['Shade'] = ['Shade {}'.format(i) for i in range(n)]
return df
def perform_tsne(df, perplexity=30):
tsne = TSNE(n_components=2, perplexity=min(perplexity, len(df) - 1), random_state=42)
tsne_result = tsne.fit_transform(df[['R', 'G', 'B']])
df['TSNE-2D-One'] = tsne_result[:, 0]
df['TSNE-2D-Two'] = tsne_result[:, 1]
return df
def perform_kmeans(df, n_clusters=5):
kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
df['Cluster'] = kmeans.fit_predict(df[['TSNE-2D-One', 'TSNE-2D-Two']])
return df
def rgb_to_hex(r, g, b):
return '#{:02x}{:02x}{:02x}'.format(r, g, b)
df = generate_lipstick_dataset()
df = perform_tsne(df)
df = perform_kmeans(df)
app = dash.Dash(__name__)
# Include custom CSS in the app layout
app.index_string = '''
<!DOCTYPE html>
<html>
<head>
{%metas%}
<title>{%title%}</title>
{%favicon%}
{%css%}
<style>
body {
background-color: #F0F0F0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
}
h1 {
color: #1C1C1C;
text-align: center;
margin-top: 20px;
font-size: 2.5em;
font-weight: bold;
}
label {
color: #1C1C1C;
font-size: 1.2em;
margin-left: 20px;
}
.dccSlider {
width: 80%;
margin-left: auto;
margin-right: auto;
margin-top: 20px;
}
.dccGraph {
margin-left: auto;
margin-right: auto;
width: 90%;
}
.container {
max-width: 1200px;
margin: auto;
padding: 20px;
background-color: #FFFFFF;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.dash-slider .rc-slider-track {
background-color: #FF0000;
}
.dash-slider .rc-slider-handle {
border-color: #FF0000;
}
.dash-slider .rc-slider-mark-text {
color: #1C1C1C;
}
</style>
</head>
<body>
{%app_entry%}
<footer>
{%config%}
{%scripts%}
{%renderer%}
</footer>
</body>
</html>
'''
app.layout = html.Div(className='container', children=[
html.H1('Lipstick Shades 2D t-SNE Clustering'),
html.Label('Number of shades:', className='dccLabel'),
dcc.Slider(
id='n-shades-slider',
min=10,
max=200,
step=10,
value=100,
marks={i: str(i) for i in range(10, 210, 20)},
className='dash-slider'
),
html.Label('Number of clusters:', className='dccLabel'),
dcc.Slider(
id='n-clusters-slider',
min=2,
max=10,
step=1,
value=5,
marks={i: str(i) for i in range(2, 11)},
className='dash-slider'
),
dcc.Graph(id='tsne-plot', className='dccGraph')
])
@app.callback(
Output('tsne-plot', 'figure'),
[Input('n-shades-slider', 'value'), Input('n-clusters-slider', 'value')]
)
def update_tsne_plot(n_shades, n_clusters):
df_sample = generate_lipstick_dataset(n=n_shades)
df_sample = perform_tsne(df_sample, perplexity=30)
df_sample = perform_kmeans(df_sample, n_clusters=n_clusters)
df_sample['hex'] = df_sample.apply(lambda row: rgb_to_hex(row['R'], row['G'], row['B']), axis=1)
df_sample['hover_text'] = df_sample.apply(
lambda row: f"Shade: {row['Shade']}<br>RGB: ({row['R']}, {row['G']}, {row['B']})<br>Hex: {row['hex']}", axis=1
)
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df_sample['TSNE-2D-One'],
y=df_sample['TSNE-2D-Two'],
mode='markers',
marker=dict(
size=10,
color=['rgb({}, {}, {})'.format(r, g, b) for r, g, b in zip(df_sample['R'], df_sample['G'], df_sample['B'])]
),
text=df_sample['hover_text'],
hoverinfo='text'
))
fig.update_layout(
title=f'2D t-SNE Clustering of {n_shades} Lipstick Shades',
showlegend=False,
margin=dict(l=0, r=0, t=30, b=0),
xaxis_title='TSNE-2D-One',
yaxis_title='TSNE-2D-Two'
)
return fig
if __name__ == '__main__':
app.run_server(debug=True)