html出力したPlotlyでプロットクリック時に表形式で詳細データを表示する

## 出力されるHTMLファイル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4083558/685e5830-a36d-4951-8407-d0a039908d1c.png)

## コード
### customdataに１個の列を渡す場合
```python
import plotly.graph_objs as go
import pandas as pd

# Sample data
df = pd.DataFrame({
    'x': [1, 2, 3, 4, 5],
    'y': [10, 20, 15, 25, 30],
    'details': ['A', 'B', 'C', 'D', 'E'],
    'extra': ['Info1', 'Info2', 'Info3', 'Info4', 'Info5']
})

# Create plot
fig = go.Figure()

fig.add_trace(go.Scatter(
    x=df['x'],
    y=df['y'],
    mode='markers',
    text=df['details'],
    customdata=df['extra'],
    # hoverinfo='x+y',  # Show only X and Y on hover
    name='Data points'
))

fig.update_layout(
    title='Click to Show Details',
    xaxis_title='X-axis',
    yaxis_title='Y-axis'
)

# Export the plot to HTML
html_content = fig.to_html(full_html=False, include_plotlyjs='cdn')

# HTML template
html_template = f"""
<!DOCTYPE html>
<html>
<head>
    <title>Click to Show Details</title>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
    <div id="plotly-div">{html_content}</div>
    <div id="details">
        <h3>Clicked Data Details</h3>
        <table border="1" id="detail-table">
            <tr><th>X</th><th>Y</th><th>Details</th><th>Extra</th></tr>
        </table>
    </div>
    <script>
        // Bind click event
        var plotDiv = document.getElementById('plotly-div').getElementsByClassName('plotly-graph-div')[0];
        plotDiv.on('plotly_click', function(data) {{
            var point = data.points[0];
            var x = point.x;
            var y = point.y;
            var text = point.text;
            var extra = point.customdata;

            // Clear the table and insert new data
            var table = document.getElementById('detail-table');
            while (table.rows.length > 1) {{
                table.deleteRow(1);
            }}

            // Add a new row with the clicked data
            var newRow = table.insertRow(-1);
            newRow.insertCell(0).innerHTML = x;
            newRow.insertCell(1).innerHTML = y;
            newRow.insertCell(2).innerHTML = text;
            newRow.insertCell(3).innerHTML = extra;
        }});
    </script>
</body>
</html>
"""

# Save HTML file
output_dir = "xxx"
file_name = "plotly_click_details.html"
with open("/".join([output_dir,file_name]), "w", encoding="utf-8") as f:
    f.write(html_template)

print(f"HTML file generated: {file_name}")
```

`output_dir` には個人のフォルダを指定してください
```python
output_dir = "xxx"
```

### customdataに複数個の列を渡す場合

```python
import plotly.express as px
import pandas as pd
import json

# Sample data with multiple extra fields
df = pd.DataFrame({
    'x': [1, 2, 3, 4, 5],
    'y': [10, 20, 15, 25, 30],
    'details': ['A', 'B', 'C', 'D', 'E'],
    'extra1': ['Info1', 'Info2', 'Info3', 'Info4', 'Info5'],
    'extra2': ['More1', 'More2', 'More3', 'More4', 'More5'],
    'extra3': ['Data1', 'Data2', 'Data3', 'Data4', 'Data5']
})

# Define extra columns as a list and convert to JSON
extra_columns = ['extra1', 'extra2', 'extra3']
extra_columns_json = json.dumps(extra_columns)

# Combine multiple fields into customdata as a list of values
df['customdata'] = df[extra_columns].values.tolist()

# Create plot using Plotly Express
fig = px.scatter(df, x='x', y='y', text='details', title='Click to Show Multiple Details')

# Update trace to include custom data and hover template
fig.update_traces(
    mode='markers+text',
    textposition='top center',
    hovertemplate='X: %{x}<br>Y: %{y}<extra></extra>',
    customdata=df['customdata'].tolist()
)

# Export the plot to HTML
html_content = fig.to_html(full_html=False, include_plotlyjs='cdn')

# HTML template
html_template = f"""
<!DOCTYPE html>
<html>
<head>
    <title>Click to Show Multiple Details</title>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    <script>
        // Column names for extra data
        var extraColumns = {extra_columns_json};
    </script>
    <style>
        table {{
            width: 100%;
            border-collapse: collapse;
            margin: 10px 0;
            font-size: 1.2em;
            text-align: center;
        }}
        th, td {{
            padding: 8px;
            border: 1px solid #ddd;
        }}
        th {{
            background-color: #f2f2f2;
            color: #333;
        }}
        tr:hover {{
            background-color: #f1f1f1;
        }}
    </style>
</head>
<body>
    <div id="details">
        <h3>Clicked Data Details</h3>
        <table id="detail-table">
            <tr>
                <th>X</th><th>Y</th><th>Details</th>
                <script>
                    // Add headers for extra columns dynamically
                    extraColumns.forEach(function(colName) {{
                        document.write('<th>' + colName + '</th>');
                    }});
                </script>
            </tr>
            <tr>
                <td colspan="{3 + len(extra_columns)}" style="text-align: center;">No data available</td>
            </tr>
        </table>
    </div>
    <div id="plotly-div">{html_content}</div>
    <script>
        // Bind click event
        var plotDiv = document.getElementById('plotly-div').getElementsByClassName('plotly-graph-div')[0];
        plotDiv.on('plotly_click', function(data) {{
            var point = data.points[0];
            var x = point.x;
            var y = point.y;
            var text = point.text;
            var extras = point.customdata;

            // Get the table and clear only data rows
            var table = document.getElementById('detail-table');

            // Remove all rows except the header
            while (table.rows.length > 1) {{
                table.deleteRow(1);
            }}

            // Create the data row
            var newRow = table.insertRow(-1);
            newRow.insertCell(0).innerHTML = x;
            newRow.insertCell(1).innerHTML = y;
            newRow.insertCell(2).innerHTML = text;

            // Add each extra data as a new cell
            extras.forEach(function(extra) {{
                var newCell = newRow.insertCell(-1);
                newCell.innerHTML = extra;
            }});
        }});
    </script>
</body>
</html>
"""

output_dir = "xxx"
file_name = "plotly_click_details.html"
with open("/".join([output_dir,file_name]), "w", encoding="utf-8") as f:
    f.write(html_template)

print("HTML file generated: plotly_click_details.html")
```

表示したい列名を `extra_columns` に定義する
```python
extra_columns = ['extra1', 'extra2', 'extra3']
```