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()
Log in or sign up for Devpost to join the conversation.