SPATIAL DEPTH

import pandas as pd import numpy as np import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore')

FILE = 'Data_sets.xlsx' # update path if needed

sp = pd.read_excel(FILE, sheet_name='Spatial Data', header=1) sp.columns = ['X_m','Y_m','Depth_m','Solids_pct','Clay_pct','BaselineDOC_mgL','BaselineCH4Prod']

Bin depth every 0.5 m

sp_d = sp[['Depth_m','BaselineCH4Prod']].dropna().copy() sp_d['Depth_bin'] = (sp_d['Depth_m'] / 0.5).round(0) * 0.5 binned = sp_d.groupby('Depth_bin')['BaselineCH4Prod'].agg( mean='mean', std='std', count='count').reset_index()

r = sp_d['Depth_m'].corr(sp_d['BaselineCH4Prod'])

── Style ─────────────────────────────────────────────────────────────────

BG = '#0f1923' GRID = '#1e2d3d' MUTED = '#6b7f90' TEXT = '#e2e8f0' LABEL = '#a0b4c8' PURPLE = '#bf7fff'

plt.rcParams.update({ 'figure.facecolor': BG, 'axes.facecolor': BG, 'axes.edgecolor': GRID, 'axes.labelcolor': LABEL, 'xtick.color': MUTED, 'ytick.color': MUTED, 'grid.color': GRID, 'grid.alpha': 0.8, 'axes.grid': True, 'text.color': TEXT, 'figure.dpi': 150, 'axes.spines.top': False, 'axes.spines.right': False, 'font.family': 'monospace', })

fig, ax = plt.subplots(figsize=(10, 6))

Raw scatter

ax.scatter(sp_d['Depth_m'], sp_d['BaselineCH4Prod'] * 1000, alpha=0.25, s=8, color=PURPLE, rasterized=True, zorder=2)

Binned mean line

ax.plot(binned['Depth_bin'], binned['mean'] * 1000, color='white', lw=2.5, marker='o', ms=6, markerfacecolor='white', markeredgecolor='white', markeredgewidth=1.5, zorder=5, label='Binned mean (0.5 m)')

ax.set_xlabel('Depth (m)', fontsize=10) ax.set_ylabel('CH₄ Production Rate (×10⁻³ kg/m³/day)', fontsize=9) ax.set_title('Depth: No Control — Uniform Spatial Distribution\n' '1,000 spatial sediment samples', fontsize=11, fontweight='bold', color=PURPLE, pad=12)

ax.text(0.02, 0.97, f'r = {r:.3f}\n(no depth effect)', transform=ax.transAxes, fontsize=9, color=LABEL, va='top', bbox=dict(boxstyle='round,pad=0.4', facecolor='#1a2535', edgecolor=GRID, alpha=0.95))

ax.legend(fontsize=8, loc='upper right')

plt.tight_layout() plt.savefig('depth_ch4.png', dpi=150, bbox_inches='tight', facecolor=BG, edgecolor='none') plt.show() print('Saved: depth_ch4.png')

DISSOLVED OXYGEN

import pandas as pd import numpy as np import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore')

FILE = 'Data sets.xlsx' # update path if needed

df = pd.read_excel(FILE, sheet_name='Hourly Data', header=1) df.columns = ['Timestamp','AirTemp','SurfaceWaterTemp','DeepWaterTemp', 'pH','DOC','DissolvedO2','WindSpeed','IceCoverFraction','MeasuredCH4Flux'] for col in df.columns[1:]: df[col] = pd.to_numeric(df[col], errors='coerce')

Bin at 0.5 mg/L — require n >= 100 to remove sparse noisy edge bins

df_do = df[['DissolvedO2','MeasuredCH4Flux']].dropna().copy() df_do['DO_bin'] = (df_do['DissolvedO2'] / 0.5).round(0) * 0.5 stats = df_do.groupby('DO_bin')['MeasuredCH4Flux'].agg( mean='mean', std='std', count='count').reset_index() stats = stats[(stats['count'] >= 100) & (stats['DO_bin'].between(4, 11))].copy()

3-pt rolling smooth

stats['mean_smooth'] = stats['mean'].rolling(window=3, center=True, min_periods=1).mean()

Linear trend (expect downward slope: more O2 = less CH4)

z = np.polyfit(stats['DO_bin'], stats['mean_smooth'] * 1e6, 1) p = np.poly1d(z) x_trend = np.linspace(stats['DO_bin'].min(), stats['DO_bin'].max(), 300)

── Style ─────────────────────────────────────────────────────────────────

BG = '#0f1923' GRID = '#1e2d3d' MUTED = '#6b7f90' TEXT = '#e2e8f0' LABEL = '#a0b4c8' GREEN = '#5adc6e' RED = '#ff5555'

plt.rcParams.update({ 'figure.facecolor': BG, 'axes.facecolor': BG, 'axes.edgecolor': GRID, 'axes.labelcolor': LABEL, 'xtick.color': MUTED, 'ytick.color': MUTED, 'grid.color': GRID, 'grid.alpha': 0.8, 'axes.grid': True, 'text.color': TEXT, 'figure.dpi': 150, 'axes.spines.top': False, 'axes.spines.right': False, 'font.family': 'monospace', })

y_smooth = stats['mean_smooth'] * 1e6 y_min, y_max = y_smooth.min(), y_smooth.max() y_pad = (y_max - y_min) * 0.9

fig, ax = plt.subplots(figsize=(10, 6))

Zone boundary lines only — no heavy fills

ax.axvline(5.0, color=RED, lw=1.0, ls='--', alpha=0.55, zorder=2) ax.axvline(8.0, color=GREEN, lw=1.0, ls='--', alpha=0.55, zorder=2)

Zone labels at top

ax.text(4.75, y_max + y_pad * 0.52, 'ANAEROBIC', fontsize=7.5, color=RED, ha='center', style='italic', alpha=0.85) ax.text(6.5, y_max + y_pad * 0.52, 'TRANSITION', fontsize=7.5, color=MUTED, ha='center', style='italic') ax.text(9.2, y_max + y_pad * 0.52, 'AEROBIC', fontsize=7.5, color=GREEN, ha='center', style='italic', alpha=0.85)

SD band

ax.fill_between( stats['DO_bin'], (stats['mean'] - stats['std']) * 1e6, (stats['mean'] + stats['std']) * 1e6, alpha=0.10, color=GREEN, zorder=3, label='\u00b11 SD (raw bins)' )

Raw bin dots (faint)

ax.scatter(stats['DO_bin'], stats['mean'] * 1e6, color=GREEN, alpha=0.30, s=16, zorder=4)

Smoothed line

ax.plot(stats['DO_bin'], y_smooth, color=GREEN, lw=2.5, marker='o', ms=6, markerfacecolor='white', markeredgecolor=GREEN, markeredgewidth=1.8, zorder=5, label='3-pt smoothed mean')

Trend line

ax.plot(x_trend, p(x_trend), color='white', lw=1.5, ls='--', alpha=0.45, zorder=4, label='Linear trend')

Annotate the key message

ax.annotate('CH\u2084 highest\nat low O\u2082', xy=(stats['DO_bin'].iloc[0], y_smooth.iloc[0]), xytext=(stats['DO_bin'].iloc[0] + 1.0, y_smooth.iloc[0] + y_pad * 0.35), arrowprops=dict(arrowstyle='->', color=RED, lw=1.3), fontsize=8, color=RED, ha='center')

ax.set_ylim(y_min - y_pad, y_max + y_pad) ax.set_xlim(4.3, 10.1) ax.set_xlabel('Dissolved O\u2082 (mg/L)', fontsize=10) ax.set_ylabel('Mean CH\u2084 Flux (\u00d710\u207b\u2076 kg/m\u00b2/hr)', fontsize=9) ax.set_title('DO Controls CH\u2084 \u2014 Primary Driver\n' '26,280 hourly observations \u00b7 0.5 mg/L bins \u00b7 n \u2265 100 per bin shown', fontsize=11, fontweight='bold', color=GREEN, pad=12)

ax.text(0.02, 0.97, 'Y-axis zoomed to show variation', transform=ax.transAxes, fontsize=7.5, color=MUTED, va='top', bbox=dict(boxstyle='round,pad=0.4', facecolor='#1a2535', edgecolor=GRID, alpha=0.95))

ax.legend(fontsize=8, loc='lower left')

plt.tight_layout() plt.savefig('do_ch4_v2.png', dpi=150, bbox_inches='tight', facecolor=BG, edgecolor='none') plt.show() print('Saved: do_ch4_v2.png')

DOC

import pandas as pd import numpy as np import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore')

FILE = 'Data sets.xlsx' # update path if needed

df = pd.read_excel(FILE, sheet_name='Hourly Data', header=1) df.columns = ['Timestamp','AirTemp','SurfaceWaterTemp','DeepWaterTemp', 'pH','DOC','DissolvedO2','WindSpeed','IceCoverFraction','MeasuredCH4Flux'] for col in df.columns[1:]: df[col] = pd.to_numeric(df[col], errors='coerce')

Bin at 5 mg/L — require n >= 100 to remove sparse edge bins (70 n=17, 130 n=14)

df_h = df[['DOC','MeasuredCH4Flux']].dropna().copy() df_h['DOC_bin'] = (df_h['DOC'] / 5).round(0) * 5 stats = df_h.groupby('DOC_bin')['MeasuredCH4Flux'].agg( mean='mean', std='std', count='count').reset_index() stats = stats[stats['count'] >= 100].copy()

3-pt rolling smooth

stats['mean_smooth'] = stats['mean'].rolling(window=3, center=True, min_periods=1).mean()

Linear trend

z = np.polyfit(stats['DOC_bin'], stats['mean_smooth'] * 1e6, 1) p = np.poly1d(z) x_trend = np.linspace(stats['DOC_bin'].min(), stats['DOC_bin'].max(), 300)

r = df_h['DOC'].corr(df_h['MeasuredCH4Flux'])

── Style ─────────────────────────────────────────────────────────────────

BG = '#0f1923' GRID = '#1e2d3d' MUTED = '#6b7f90' TEXT = '#e2e8f0' LABEL = '#a0b4c8' ORANGE = '#ff6b35'

plt.rcParams.update({ 'figure.facecolor': BG, 'axes.facecolor': BG, 'axes.edgecolor': GRID, 'axes.labelcolor': LABEL, 'xtick.color': MUTED, 'ytick.color': MUTED, 'grid.color': GRID, 'grid.alpha': 0.8, 'axes.grid': True, 'text.color': TEXT, 'figure.dpi': 150, 'axes.spines.top': False, 'axes.spines.right': False, 'font.family': 'monospace', })

y_smooth = stats['mean_smooth'] * 1e6 y_min, y_max = y_smooth.min(), y_smooth.max() y_pad = (y_max - y_min) * 0.8

fig, ax = plt.subplots(figsize=(10, 6))

High-DOC boundary line only — no fill

ax.axvline(110, color=ORANGE, lw=1.0, ls='--', alpha=0.45, zorder=2) ax.text(116, y_max + y_pad * 0.45, '\u2191 elevated\nemissions', fontsize=7.5, color=ORANGE, ha='center', style='italic', alpha=0.75)

SD band

ax.fill_between( stats['DOC_bin'], (stats['mean'] - stats['std']) * 1e6, (stats['mean'] + stats['std']) * 1e6, alpha=0.10, color=ORANGE, zorder=3, label='\u00b11 SD (raw bins)' )

Raw bin dots (faint)

ax.scatter(stats['DOC_bin'], stats['mean'] * 1e6, color=ORANGE, alpha=0.30, s=16, zorder=4)

Smoothed line

ax.plot(stats['DOC_bin'], y_smooth, color=ORANGE, lw=2.5, marker='o', ms=6, markerfacecolor='white', markeredgecolor=ORANGE, markeredgewidth=1.8, zorder=5, label='3-pt smoothed mean')

Trend line

ax.plot(x_trend, p(x_trend), color='white', lw=1.5, ls='--', alpha=0.45, zorder=4, label='Linear trend')

ax.set_ylim(y_min - y_pad, y_max + y_pad) ax.set_xlabel('DOC (mg/L)', fontsize=10) ax.set_ylabel('Mean CH\u2084 Flux (\u00d710\u207b\u2076 kg/m\u00b2/hr)', fontsize=9) ax.set_title('DOC Feeds CH\u2084 \u2014 Hourly Time-Series\n' '26,280 observations \u00b7 5 mg/L bins \u00b7 n \u2265 100 per bin shown', fontsize=11, fontweight='bold', color=ORANGE, pad=12)

ax.text(0.02, 0.97, f'Pearson r = {r:.4f}\nY-axis zoomed to show variation', transform=ax.transAxes, fontsize=8, color=LABEL, va='top', bbox=dict(boxstyle='round,pad=0.4', facecolor='#1a2535', edgecolor=GRID, alpha=0.95))

ax.legend(fontsize=8, loc='lower right')

plt.tight_layout()

plt.savefig('doc_ch4_hourly_v2.png', dpi=150, bbox_inches='tight', facecolor=BG, edgecolor='none')

plt.show()

print('Saved: doc_ch4_hourly_v2.png')

Built With

Share this project:

Updates