Problem: AttributeError: 'Series' object has no attribute 'style'
Traceback:
File "/usr/local/lib/python3.10/dist-packages/streamlit/runtime/scriptrunner/exec_code.py", line 88, in exec_func_with_error_handling
    result = func()
File "/usr/local/lib/python3.10/dist-packages/streamlit/runtime/scriptrunner/script_runner.py", line 579, in code_to_exec
    exec(code, module.__dict__)
File "/content/portfolio_optimizer.py", line 4293, in <module>
    display_backtest_results(result["backtest_results"], comparison_type, portfolio1)
File "/content/portfolio_optimizer.py", line 598, in display_backtest_results
    display_portfolio_vs_benchmark(results)
File "/content/portfolio_optimizer.py", line 750, in display_portfolio_vs_benchmark
    styled_metrics = metrics_comparison.apply(row_formatter, axis=1).style.applymap(
File "/usr/local/lib/python3.10/dist-packages/pandas/core/generic.py", line 6299, in __getattr__
    return object.__getattribute__(self, name)

if the segments code need update, please return the full updated segments code, no need import or other stuff, only the full version of the updated segment

### Relevant Code Segments

#### 1. **File: `portfolio_optimizer.py`**
   - **Function: `display_portfolio_vs_benchmark`**
     - **Relevance:** This function is directly related to the problem described, as it attempts to style the metrics comparison DataFrame, which is where the `AttributeError: 'Series' object has no attribute 'style'` occurs.
     - **Code Segment:**
       ```python
       def display_portfolio_vs_benchmark(results):
           # Extract data from results
           returns = results['returns']
           cum_returns = results['cum_returns']
           weights = results['weights']
           price_data = results['price_data']
           available_selected = results['available_selected']
           metrics = results['metrics']
           benchmark_metrics = results['benchmark_metrics']
           benchmark_returns = results['benchmark_returns']
           benchmark_cum_returns = results['benchmark_cum_returns']

           # Create comparison metrics table assuming both metrics and benchmark_metrics have same keys
           portfolio_metrics_df = pd.DataFrame(metrics.items(), columns=['Metric', 'Portfolio'])
           benchmark_metrics_df = pd.DataFrame(benchmark_metrics.items(), columns=['Metric', 'Benchmark'])
           metrics_comparison = pd.merge(portfolio_metrics_df, benchmark_metrics_df, on='Metric', how='outer')

           # Define which metrics are percentage-based
           percentage_metrics = [
               'Total Return', 'Annualized Return (CAGR)', 'Best Year', 'Worst Year',
               'Arithmetic Mean (Monthly)', 'Arithmetic Mean (Annualized)',
               'Geometric Mean (Monthly)', 'Geometric Mean (Annualized)',
               'Standard Deviation (Monthly)', 'Standard Deviation (Annualized)',
               'Downside Deviation (Monthly)', 'Maximum Drawdown',
               'Alpha (annualized)', 'Calmar Ratio',
               'Tracking Error', 'Active Return',
               'Upside Capture Ratio', 'Downside Capture Ratio'
           ]

           # Style the metrics table
           def highlight_better(row):
               styles = [''] * len(row)
               portfolio_val = row["Portfolio"]
               benchmark_val = row["Benchmark"]
               # Skip if either is N/A
               if portfolio_val == "N/A" or benchmark_val == "N/A":
                   return styles
               # Convert to float if possible
               try:
                   # Remove '%' and other symbols for comparison
                   portfolio_num = float(str(portfolio_val).strip('%').strip('$').replace(',', ''))
                   benchmark_num = float(str(benchmark_val).strip('%').strip('$').replace(',', ''))
               except:
                   return styles
               # Determine if higher is better or lower is better based on metric name
               metric_name = row['Metric']
               if any(keyword in metric_name for keyword in ['Drawdown', 'Deviation', 'Tracking Error']):
                   better = portfolio_num < benchmark_num
               else:
                   better = portfolio_num > benchmark_num
               styles[1] = 'background-color: #d4f7d4' if better else 'background-color: #f7d4d4'
               return styles

           styled_metrics = metrics_comparison.style.apply(highlight_better, axis=1)

           # Apply the custom formatter only to numeric cells
           def custom_formatter(x, metric):
               try:
                   # Attempt to convert to float after stripping possible symbols
                   value = float(str(x).strip('%').strip('$').replace(',', ''))
                   if metric in percentage_metrics:
                       return f"{value:.2f}%"
                   else:
                       return f"{value:.2f}"
               except (ValueError, TypeError):
                   return x  # Return the original value if conversion fails

           # Apply formatter row-wise to access metric name
           def row_formatter(row):
               metric = row['Metric']
               return [custom_formatter(row[col], metric) if col in ['Portfolio', 'Benchmark'] else row[col] for col in row.index]

           styled_metrics = metrics_comparison.apply(row_formatter, axis=1).style.applymap(
               lambda x: 'background-color: #d4f7d4' if 'd4f7d4' in x else ('background-color: #f7d4d4' if 'f7d4d4' in x else ''),
               subset=['Portfolio', 'Benchmark']
           )

           st.dataframe(styled_metrics, height=400)
       ```

#### 2. **File: `portfolio_optimizer.py`**
   - **Function: `display_backtest_results`**
     - **Relevance:** This function calls `display_portfolio_vs_benchmark`, making it indirectly related to the problem. It determines which comparison type to display.
     - **Code Segment:**
       ```python
       def display_backtest_results(results, comparison_type, portfolio1, portfolio2=None):
           if comparison_type == "Portfolio vs Benchmark":
               display_portfolio_vs_benchmark(results)
           else:
               # For portfolio vs portfolio comparison
               display_portfolio_vs_portfolio(results, portfolio1, portfolio2)
       ```

#### 3. **File: `portfolio_optimizer.py`**
   - **Function: `run_portfolio_comparison`**
     - **Relevance:** This function is responsible for running the portfolio comparison, which includes the backtest results that are passed to `display_backtest_results`.
     - **Code Segment:**
       ```python
       def run_portfolio_comparison(comparison_type, portfolio1, portfolio2=None):
           try:
               if comparison_type == "vs_benchmark":
                   # Handle primary portfolio data
                   available, missing, price_data = handle_portfolio_data(portfolio1)
                   if not available:
                       return {"error": "No valid tickers with available data."}
                   
                   # Adjust allocations based on available tickers
                   allocations = adjust_allocations([portfolio1['allocations'][portfolio1['selected'].index(t)] for t in available])
                   
                   # Perform backtest for the portfolio
                   returns, cum_returns = backtest(
                       weights=np.array(allocations) / 100,
                       prices=price_data[available],
                       rebalance_freq=portfolio1['rebalance_freq'],
                       broker_fee=portfolio1['broker_fee']
                   )
                   if cum_returns.empty:
                       return {"error": "Cumulative returns are empty."}
                   
                   # Download benchmark data
                   benchmark_data = download_data(
                       [portfolio1['benchmark_symbol']],
                       portfolio1['start_date'].strftime('%Y-%m-%d'),
                       portfolio1['end_date'].strftime('%Y-%m-%d')
                   )
                   if benchmark_data.empty:
                       return {"error": "Benchmark data could not be downloaded."}
                   
                   # Process benchmark data and handle potential errors
                   try:
                       benchmark_returns, benchmark_cum_returns, benchmark_metrics = process_benchmark(benchmark_data, returns, portfolio1)
                   except ValueError as ve:
                       return {"error": str(ve)}
                   except Exception as e:
                       return {"error": f"Benchmark processing failed: {e}"}
                   
                   # Calculate metrics for the portfolio without benchmark comparison
                   portfolio_metrics = calculate_metrics(returns, cum_returns, portfolio1['rf_rate'])
                   
                   # Compile backtest results
                   backtest_results = {
                       'returns': returns,
                       'cum_returns': cum_returns,
                       'weights': allocations,
                       'price_data': price_data[available],
                       'available_selected': available,
                       'metrics': portfolio_metrics,
                       'benchmark_returns': benchmark_returns,
                       'benchmark_cum_returns': benchmark_cum_returns,
                       'benchmark_metrics': benchmark_metrics
                   }
                   
                   return {"backtest_results": backtest_results}

               elif comparison_type == "vs_portfolio":
                   # Determine overlapping date range between the two portfolios
                   overlap_start = max(portfolio1['start_date'], portfolio2['start_date'])
                   overlap_end = min(portfolio1['end_date'], portfolio2['end_date'])
                   if overlap_start >= overlap_end:
                       return {"error": "No overlapping date ranges."}
                   
                   # Handle data for both portfolios within the overlapping date range
                   available_a, missing_a, price_data_a = handle_portfolio_data(portfolio1, (overlap_start, overlap_end))
                   available_b, missing_b, price_data_b = handle_portfolio_data(portfolio2, (overlap_start, overlap_end))
                   if not available_a or not available_b:
                       return {"error": "One of the portfolios has no valid tickers with available data."}
                   
                   # Adjust allocations based on available tickers for both portfolios
                   allocations_a = adjust_allocations([portfolio1['allocations'][portfolio1['selected'].index(t)] for t in available_a])
                   allocations_b = adjust_allocations([portfolio2['allocations'][portfolio2['selected'].index(t)] for t in available_b])
                   
                   # Perform backtest for both portfolios
                   returns_a, cum_returns_a = backtest(
                       weights=np.array(allocations_a) / 100,
                       prices=price_data_a[available_a],
                       rebalance_freq=portfolio1['rebalance_freq'],
                       broker_fee=portfolio1['broker_fee']
                   )
                   returns_b, cum_returns_b = backtest(
                       weights=np.array(allocations_b) / 100,
                       prices=price_data_b[available_b],
                       rebalance_freq=portfolio2['rebalance_freq'],
                       broker_fee=portfolio2['broker_fee']
                   )
                   if cum_returns_a.empty or cum_returns_b.empty:
                       return {"error": "One of the cumulative returns is empty."}
                   
                   # Align two portfolios' returns
                   common_index = cum_returns_a.index.intersection(cum_returns_b.index)
                   if common_index.empty:
                       return {"error": "No overlapping dates after backtesting."}
                   returns_a = returns_a.loc[common_index]
                   returns_b = returns_b.loc[common_index]
                   cum_returns_a = cum_returns_a.loc[common_index]
                   cum_returns_b = cum_returns_b.loc[common_index]
                   
                   # Calculate metrics for both portfolios
                   metrics_a = calculate_metrics(returns_a, cum_returns_a, portfolio1['rf_rate'])
                   metrics_b = calculate_metrics(returns_b, cum_returns_b, portfolio2['rf_rate'])
                   
                   # Compile backtest results
                   backtest_results = {
                       'returns_a': returns_a,
                       'cum_returns_a': cum_returns_a,
                       'weights_a': allocations_a,
                       'price_data_a': price_data_a[available_a],
                       'metrics_a': metrics_a,
                       'returns_b': returns_b,
                       'cum_returns_b': cum_returns_b,
                       'weights_b': allocations_b,
                       'price_data_b': price_data_b[available_b],
                       'metrics_b': metrics_b
                   }
                   return {"backtest_results": backtest_results}
           except Exception as e:
               return {"error": f"An unexpected error occurred: {e}"}
       ```

### Summary
- The `display_portfolio_vs_benchmark` function is the primary focus, as it contains the code where the `AttributeError` occurs. This function attempts to style a DataFrame containing portfolio and benchmark metrics.
- The `display_backtest_results` function calls `display_portfolio_vs_benchmark`, making it indirectly related to the problem.
- The `run_portfolio_comparison` function generates the backtest results that are passed to `display_backtest_results`.

These segments are essential for understanding the context and flow of the code related to the described problem.