I want a dendrogram to be animated and i want the branches to appear when they are clustered together. So lines between the the smallest distance would appear first and so on… I’ve tried so many things but i can not figure it out any help would be greatly appreciated.
import plotly.figure_factory as ff
import plotly.express as px
# Load the data from the CSV file
data = pd.read_csv('/Users/kelse/seniorcomp/tswift.csv')
# Extract the audio features from the data
audio_features = data[["energy", "acousticness", "mode","instrumentalness", "loudness","tempo","danceability",'speechiness','valence' , "liveness", "time_signature","key"]]
song_titles = data["title"]
# Calculate the distances between the songs
distances = linkage(audio_features, method='ward')
def ensure_consistent_dimensions(distances, song_titles):
# Check the dimensions of the distances and song_titles variables
print("distances shape:", distances.shape)
print("song_titles shape:", song_titles.shape)
# Check if the dimensions are consistent
if distances.shape[0] != song_titles.shape[0]:
# If the dimensions are not consistent, find the minimum number of elements
min_elements = min(distances.shape[0], song_titles.shape[0])
# Trim the distances and song_titles variables to the minimum number of elements
distances = distances[:min_elements]
song_titles = song_titles[:min_elements]
print("Trimmed distances shape:", distances.shape)
print("Trimmed song_titles shape:", song_titles.shape)
# Return the distances and song_titles variables with consistent dimensions
return distances, song_titles
# Ensure that the distances and song_titles variables have consistent dimensions
distances, song_titles = ensure_consistent_dimensions(distances, song_titles)
# Determine the number of clusters
num_clusters = determine_num_clusters(audio_features)
# Cluster the songs based on their audio features
model = AgglomerativeClustering(n_clusters=num_clusters)
model.fit(audio_features)
# Predict the cluster labels for each song
cluster_labels = model.labels_
# Create a dendrogram plot
fig = ff.create_dendrogram(distances, labels=song_titles.tolist(), color_threshold=num_clusters)
fig.update_layout(width=2000, height=2000)
# Add the animation parameter to the figure
fig.update_layout(updatemenus=[
dict(
buttons=list([
dict(
args=[{'yaxis.range': [0, 1]}, {'frame': {'duration': 500, 'redraw': True}, 'fromcurrent': True, 'transition': {'duration': 300, 'easing': 'quadratic-in-out'}}],
label='Play',
method='animate'
)
]),
direction='left',
pad={'r': 10, 't': 87},
showactive=True,
type='buttons',
x=0.1,
xanchor='right',
y=0,
yanchor='top'
),
])
# Color the clusters with different colors
fig.data[0].marker.color = px.colors.qualitative.Plotly[:num_clusters]
# Initialize the Dash app
app = dash.Dash()
# Add the dendrogram plot to the Dash app
app.layout = html.Div([
dcc.Graph(figure=fig)
])
app.run_server(host='localhost', port=8057)
Ive also tried this but this has an error of “NONETYPE object not iterable”
import plotly.figure_factory as ff
# Load the data from the CSV file
data = pd.read_csv('/Users/kelse/seniorcomp/tswift.csv')
# Extract the audio features from the data
audio_features = data[["energy", "acousticness", "mode","instrumentalness", "loudness","tempo","danceability",'speechiness','valence' , "liveness", "time_signature","key"]]
song_titles = data["title"]
# Check if the song_titles variable is None
if song_titles is None:
# Set the song_titles variable to an empty list
song_titles = []
# Calculate the distances between the songs
distances = linkage(audio_features, method='ward')
def ensure_consistent_dimensions(distances, song_titles):
# Check the dimensions of the distances and song_titles variables
print("distances shape:", distances.shape)
print("song_titles shape:", song_titles.shape)
# Check if the dimensions are consistent
if distances.shape[0] != song_titles.shape[0]:
# If the dimensions are not consistent, find the minimum number of elements
min_elements = min(distances.shape[0], song_titles.shape[0])
# Trim the distances and song_titles variables to the minimum number of elements
distances = distances[:min_elements]
song_titles = song_titles[:min_elements]
print("Trimmed distances shape:", distances.shape)
print("Trimmed song_titles shape:", song_titles.shape)
# Return the distances and song_titles variables with consistent dimensions
return distances, song_titles
# Ensure that the distances and song_titles variables have consistent dimensions
distances, song_titles = ensure_consistent_dimensions(distances, song_titles)
# Sort the distances matrix
distances[:,2].sort()
# Create a dendrogram plot
fig = ff.create_dendrogram(distances, labels=song_titles.tolist())
# Create a list of frames
frames = []
for i in range(len(distances)):
frame = {'data': [], 'name': str(i)}
dendro_leaves = fig['layout']['yaxis']['ticktext']
dendro_distances = list(fig['layout']['yaxis']['tickvals'])
for j in range(len(dendro_leaves)):
inds = [k for k, x in enumerate(song_titles) if x == dendro_leaves[j]]
frame['data'].append({'x': list(audio_features.iloc[inds, :].iloc[0]),
'y': dendro_distances[j],
'text': dendro_leaves[j],
'mode': 'markers',
'marker': {'size': 12}})
frames.append(frame)
fig.frames = frames
# Check if the fig object has a yaxis attribute in its layout
if 'yaxis' in fig['layout']:
# Set the dendro_leaves and dendro_distances variables
dendro_leaves = fig['layout']['yaxis']['ticktext']
dendro_distances = list(fig['layout']['yaxis']['tickvals'])
else:
# Set the dendro_leaves and dendro_distances variables to empty lists
dendro_leaves = []
dendro_distances = []
# Iterate over the leaves in the dendrogram
for j in range(len(dendro_leaves)):
# Find the indices of the songs with the current title
inds = [k for k, x in enumerate(song_titles) if x == dendro_leaves[j]]
# Append a trace to the frame data
frame['data'].append({'x': list(audio_features.iloc[inds, :].iloc[0]),
'y': dendro_distances[j],
'text': dendro_leaves[j],
'mode': 'markers',
'marker': {'opacity': 0.5, 'size': 14}})
# Append the frame to the list of frames
frames.append(frame)
# Update the layout with the frames
fig.update_layout(updatemenus=[
dict(
buttons=list([
dict(
args=[{'yaxis.range': [0, 1]}, {'frame': {'duration': 500, 'redraw': True}, 'fromcurrent': True, 'transition': {'duration': 300, 'easing': 'quadratic-in-out'}}],
label='Play',
method='animate'
)
]),
direction='left',
pad={'r': 10, 't': 87},
showactive=True,
type='buttons',
x=0.1,
xanchor='right',
y=0,
yanchor='top'
),
], frames=frames)
# Initialize the Dash app
app = dash.Dash()
# Add the dendrogram plot to the Dash app
app.layout = html.Div([
dcc.Graph(figure=fig)
])
if __name__ == '__main__':
app.run_server(host='0.0.0.0', port=8051)