Hey, o1, the average temperature heatmaps in this code have their scales on right overlapping with each other. Can you make them be side by side?

import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
from statsmodels.tsa.arima.model import ARIMA
from pandas.tseries.offsets import MonthEnd
import plotly.io as pio

# Function to load and preprocess the data
def load_and_preprocess_data(file_path):
    # Load the CSV file
    df = pd.read_csv(file_path)

    # Convert date column to datetime
    df['Date'] = pd.to_datetime(df['Date'], format='%d-%m-%Y')

    # Replace 'Tr' and other invalid entries with NaN
    df.replace({
        'Rain': {'Tr': np.nan, '-----': np.nan},
        'Temp_Max': {'Tr': np.nan, '-----': np.nan},
        'Temp_Min': {'Tr': np.nan, '-----': np.nan}
    }, inplace=True)

    # Convert numeric columns to floats
    df['Rain'] = pd.to_numeric(df['Rain'], errors='coerce')
    df['Temp_Max'] = pd.to_numeric(df['Temp_Max'], errors='coerce')
    df['Temp_Min'] = pd.to_numeric(df['Temp_Min'], errors='coerce')

    # Drop any rows with NaN values
    df.dropna(inplace=True)

    return df

# Function to create Plotly figures and collect them
def create_figures(df):
    figures = []

    # 1. Plot each month separately for Temp_Max and Temp_Min
    fig_months = plot_each_month_separately(df)
    figures.append(fig_months)

    # 2. Predict future temperatures using ARIMA and plot forecasts
    fig_forecasts, predictions_df = predict_and_plot_forecasts(df)
    figures.append(fig_forecasts)

    # 3. Additional Visualizations
    fig_additional = additional_visualizations(df)
    figures.extend(fig_additional)

    return figures, predictions_df

# Function to plot each month separately for Temp_Max and Temp_Min using Plotly
def plot_each_month_separately(df):
    months = range(1, 13)

    # Create a subplot for each month
    fig = make_subplots(rows=6, cols=2, subplot_titles=[f'Month {month}' for month in months],
                        vertical_spacing=0.05, horizontal_spacing=0.05)

    for i, month in enumerate(months):
        row = i // 2 + 1
        col = i % 2 + 1
        month_data = df[df['Date'].dt.month == month]

        # Add Temp_Max trace
        fig.add_trace(
            go.Scatter(x=month_data['Date'], y=month_data['Temp_Max'],
                       mode='lines', name='Temp Max', line=dict(color='blue')),
            row=row, col=col
        )

        # Add Temp_Min trace
        fig.add_trace(
            go.Scatter(x=month_data['Date'], y=month_data['Temp_Min'],
                       mode='lines', name='Temp Min', line=dict(color='red')),
            row=row, col=col
        )

        # Update axes
        fig.update_xaxes(title_text="Year", row=row, col=col)
        fig.update_yaxes(title_text="Temperature (°C)", row=row, col=col)

    fig.update_layout(height=1800, width=1200, title_text="Monthly Temperature Data", showlegend=False)

    return fig

# Function to predict future temperatures using ARIMA and visualize forecasts
def predict_and_plot_forecasts(df):
    # Split the data into train and test sets for Temp_Max and Temp_Min
    train_years = 50  # Using the last 50 years as training data
    total_years = df['Date'].dt.year.nunique()
    train_size = train_years * 12  # Monthly data

    train_temp_max = df['Temp_Max'].iloc[:train_size]
    test_temp_max = df['Temp_Max'].iloc[train_size:]

    train_temp_min = df['Temp_Min'].iloc[:train_size]
    test_temp_min = df['Temp_Min'].iloc[train_size:]

    # Fit ARIMA model for Temp_Max
    try:
        model_temp_max = ARIMA(train_temp_max, order=(5, 1, 0))
        model_fit_temp_max = model_temp_max.fit()
        forecast_temp_max = model_fit_temp_max.forecast(steps=240).values
        confidence_max = model_fit_temp_max.get_forecast(steps=240).conf_int()
    except Exception as e:
        print(f"ARIMA model failed to fit Temp_Max: {e}. Using moving average instead.")
        forecast_temp_max = train_temp_max.rolling(window=5).mean().iloc[-1:].repeat(240).values
        confidence_max = pd.DataFrame({
            'lower Temp_Max': forecast_temp_max.flatten(),
            'upper Temp_Max': forecast_temp_max.flatten()
        })

    # Fit ARIMA model for Temp_Min
    try:
        model_temp_min = ARIMA(train_temp_min, order=(5, 1, 0))
        model_fit_temp_min = model_temp_min.fit()
        forecast_temp_min = model_fit_temp_min.forecast(steps=240).values
        confidence_min = model_fit_temp_min.get_forecast(steps=240).conf_int()
    except Exception as e:
        print(f"ARIMA model failed to fit Temp_Min: {e}. Using moving average instead.")
        forecast_temp_min = train_temp_min.rolling(window=5).mean().iloc[-1:].repeat(240).values
        confidence_min = pd.DataFrame({
            'lower Temp_Min': forecast_temp_min.flatten(),
            'upper Temp_Min': forecast_temp_min.flatten()
        })

    # Create a future date range for the predictions using MonthEnd()
    end_date = df['Date'].max()
    future_dates = pd.date_range(start=end_date + MonthEnd(), periods=240, freq=MonthEnd())

    # Create traces for Plotly
    fig = go.Figure()

    # Historical Temp_Max
    fig.add_trace(go.Scatter(x=df['Date'], y=df['Temp_Max'],
                             mode='lines', name='Temp Max Historical', line=dict(color='blue')))

    # Forecast Temp_Max
    fig.add_trace(go.Scatter(x=future_dates, y=forecast_temp_max,
                             mode='lines', name='Temp Max Forecast', line=dict(color='lightblue', dash='dash')))

    # Confidence Interval for Temp_Max
    fig.add_trace(go.Scatter(x=future_dates, y=confidence_max.iloc[:, 0],
                             mode='lines', name='Temp Max CI Lower', line=dict(color='lightblue', dash='dot')))
    fig.add_trace(go.Scatter(x=future_dates, y=confidence_max.iloc[:, 1],
                             mode='lines', name='Temp Max CI Upper', line=dict(color='lightblue', dash='dot'),
                             fill='tonexty', fillcolor='rgba(173,216,230,0.2)', showlegend=False))

    # Historical Temp_Min
    fig.add_trace(go.Scatter(x=df['Date'], y=df['Temp_Min'],
                             mode='lines', name='Temp Min Historical', line=dict(color='red')))

    # Forecast Temp_Min
    fig.add_trace(go.Scatter(x=future_dates, y=forecast_temp_min,
                             mode='lines', name='Temp Min Forecast', line=dict(color='lightcoral', dash='dash')))

    # Confidence Interval for Temp_Min
    fig.add_trace(go.Scatter(x=future_dates, y=confidence_min.iloc[:, 0],
                             mode='lines', name='Temp Min CI Lower', line=dict(color='lightcoral', dash='dot')))
    fig.add_trace(go.Scatter(x=future_dates, y=confidence_min.iloc[:, 1],
                             mode='lines', name='Temp Min CI Upper', line=dict(color='lightcoral', dash='dot'),
                             fill='tonexty', fillcolor='rgba(240,128,128,0.2)', showlegend=False))

    fig.update_layout(title='Temperature Forecasts with ARIMA',
                      xaxis_title='Date',
                      yaxis_title='Temperature (°C)',
                      template='plotly_white')

    # Create a DataFrame with the predictions
    predictions_df = pd.DataFrame({
        'Date': future_dates,
        'Forecast_Temp_Max': forecast_temp_max,
        'Forecast_Temp_Min': forecast_temp_min
    })

    # Print the first few predictions
    print(predictions_df.head())

    return fig, predictions_df

# Function to add additional visualizations
def additional_visualizations(df):
    figures = []

    # 1. Seasonal Decomposition
    import statsmodels.api as sm
    decomposition_max = sm.tsa.seasonal_decompose(df.set_index('Date')['Temp_Max'], model='additive', period=12)
    decomposition_min = sm.tsa.seasonal_decompose(df.set_index('Date')['Temp_Min'], model='additive', period=12)

    fig_decomposition = make_subplots(rows=2, cols=1, subplot_titles=['Seasonal Decomposition of Temp_Max', 'Seasonal Decomposition of Temp_Min'])

    # Add Temp_Max decomposition
    fig_decomposition.add_trace(go.Scatter(x=decomposition_max.trend.index, y=decomposition_max.trend,
                                           mode='lines', name='Trend'), row=1, col=1)
    fig_decomposition.add_trace(go.Scatter(x=decomposition_max.seasonal.index, y=decomposition_max.seasonal,
                                           mode='lines', name='Seasonal'), row=1, col=1)
    fig_decomposition.add_trace(go.Scatter(x=decomposition_max.resid.index, y=decomposition_max.resid,
                                           mode='lines', name='Residual'), row=1, col=1)

    # Add Temp_Min decomposition
    fig_decomposition.add_trace(go.Scatter(x=decomposition_min.trend.index, y=decomposition_min.trend,
                                           mode='lines', name='Trend'), row=2, col=1)
    fig_decomposition.add_trace(go.Scatter(x=decomposition_min.seasonal.index, y=decomposition_min.seasonal,
                                           mode='lines', name='Seasonal'), row=2, col=1)
    fig_decomposition.add_trace(go.Scatter(x=decomposition_min.resid.index, y=decomposition_min.resid,
                                           mode='lines', name='Residual'), row=2, col=1)

    fig_decomposition.update_layout(height=800, width=1200, title_text="Seasonal Decomposition")
    figures.append(fig_decomposition)

    # 2. Correlation Heatmap
    corr = df[['Rain', 'Temp_Max', 'Temp_Min']].corr()
    fig_corr = px.imshow(corr, text_auto=True,
                         x=corr.columns, y=corr.columns,
                         color_continuous_scale='RdBu_r', range_color=[-1,1],
                         title='Correlation Heatmap')
    figures.append(fig_corr)

    # 3. Rain vs Temperature Scatter Plot
    fig_scatter_max = px.scatter(df, x='Rain', y='Temp_Max', trendline='ols',
                                 title='Rain vs Temp_Max',
                                 labels={'Rain': 'Rain (mm)', 'Temp_Max': 'Maximum Temperature (°C)'})
    figures.append(fig_scatter_max)

    fig_scatter_min = px.scatter(df, x='Rain', y='Temp_Min', trendline='ols',
                                 title='Rain vs Temp_Min',
                                 labels={'Rain': 'Rain (mm)', 'Temp_Min': 'Minimum Temperature (°C)'})
    figures.append(fig_scatter_min)

    # 4. Heatmap of Average Temperatures by Month and Year
    df['Year'] = df['Date'].dt.year
    df['Month'] = df['Date'].dt.month
    pivot_max = df.pivot_table(values='Temp_Max', index='Month', columns='Year', aggfunc='mean')
    pivot_min = df.pivot_table(values='Temp_Min', index='Month', columns='Year', aggfunc='mean')

    fig_heatmap = make_subplots(rows=2, cols=1, subplot_titles=['Average Temp_Max Heatmap', 'Average Temp_Min Heatmap'])

    # Temp_Max Heatmap
    fig_heatmap.add_trace(go.Heatmap(z=pivot_max.values,
                                     x=pivot_max.columns,
                                     y=pivot_max.index,
                                     colorscale='Viridis',
                                     colorbar=dict(title='Temp Max (°C)')),
                          row=1, col=1)

    # Temp_Min Heatmap
    fig_heatmap.add_trace(go.Heatmap(z=pivot_min.values,
                                     x=pivot_min.columns,
                                     y=pivot_min.index,
                                     colorscale='Viridis',
                                     colorbar=dict(title='Temp Min (°C)')),
                          row=2, col=1)

    fig_heatmap.update_layout(height=800, width=1200, title_text="Average Temperatures Heatmaps")
    figures.append(fig_heatmap)

    return figures

# Function to generate the HTML file with all figures and the card
def generate_html(figures, predictions_df=None):
    # Define explanations for each figure
    explanations = [
        "This visualization displays the maximum and minimum temperatures for each month over the years, providing a comprehensive view of seasonal temperature trends in Ahmedabad.",
        "The ARIMA model forecasts future maximum and minimum temperatures, allowing us to predict weather patterns and prepare for upcoming climate conditions with confidence intervals indicating the reliability of the predictions.",
        "Seasonal decomposition breaks down the temperature data into trend, seasonal, and residual components, helping us understand underlying patterns and irregular fluctuations in Ahmedabad's climate.",
        "The correlation heatmap illustrates the relationships between rainfall and temperature variables, highlighting how different weather factors interact with each other in Ahmedabad.",
        "This scatter plot explores the relationship between rainfall and maximum temperatures, revealing trends and potential dependencies that can inform weather-related decisions.",
        "Similarly, this scatter plot examines the association between rainfall and minimum temperatures, providing insights into how precipitation influences lower temperature extremes.",
        "The heatmaps of average temperatures by month and year present a detailed perspective on temperature variations across different periods, enabling the identification of long-term climate trends in Ahmedabad."
    ]

    # Start constructing the HTML content
    html_content = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>Weather Data Visualization</title>
        <!-- Include Plotly.js via CDN -->
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
        <!-- Include Bootstrap CSS and JS for the card -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
        <style>
            /* Custom styles for the card */
            .top-left-card {{
                position: fixed;
                top: 20px;
                left: 20px;
                z-index: 1000;
                width: 250px;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            }}
            body {{
                padding: 50px; /* To avoid overlap with the card */
            }}
            h1 {{
                text-align: center;
                margin-bottom: 20px;
            }}
            .plot-section {{
                margin-bottom: 50px;
            }}
            .explanation {{
                font-size: 1em;
                margin-top: 10px;
            }}
        </style>
    </head>
    <body>
        <!-- Card in the top-left corner -->
        <div class="card top-left-card" id="info-card">
            <div class="card-body">
                <h5 class="card-title">Made by Abhy-Kumar</h5>
                <p class="card-text"><a href="https://github.com/abhy-kumar" target="_blank">GitHub</a></p>
                <button type="button" class="btn-close" aria-label="Close" onclick="document.getElementById('info-card').style.display='none';"></button>
            </div>
        </div>

        <div class="container">
            <!-- Main Title -->
            <h1>Ahmedabad Weather Projection and Historical Trends</h1>
            <p class="text-center">Explore the historical weather patterns and future projections for Ahmedabad. This comprehensive analysis includes temperature trends, rainfall correlations, and forecast models to provide a clear picture of the city's climate dynamics.</p>

    """

    # Iterate over all figures and append their HTML and explanations to the content
    for idx, fig in enumerate(figures):
        fig_html = pio.to_html(fig, include_plotlyjs=False, full_html=False, div_id=f'plot_{idx}')
        explanation = explanations[idx] if idx < len(explanations) else ""
        html_content += f"""
            <div class="plot-section">
                {fig_html}
                <p class="explanation">{explanation}</p>
            </div>
            <hr>
        """

    # Close the container div and add Plotly.js script
    html_content += """
        </div>
        <!-- Include Plotly.js -->
        <script>
            // Ensure Plotly.js is loaded
            if (typeof Plotly === 'undefined') {
                var script = document.createElement('script');
                script.src = 'https://cdn.plot.ly/plotly-latest.min.js';
                document.head.appendChild(script);
            }
        </script>
    </body>
    </html>
    """

    # Save the HTML content to 'index.html'
    with open('index.html', 'w') as f:
        f.write(html_content)

    print("index.html has been generated successfully.")

# Main Execution
if __name__ == "__main__":
    # Load and preprocess data
    file_path = 'amd-temp-rains.csv'  # Update this path to your CSV file
    df = load_and_preprocess_data(file_path)

    # Create Plotly figures
    figures, predictions_df = create_figures(df)

    # Generate the HTML file with all figures and the card
    generate_html(figures, predictions_df)