GLOBEC data

Contents

GLOBEC data#

The U.S. GLOBal ocean ECosystems dynamics (GLOBEC) dataset here is organized from the BCO-DMO website and found by searching the key word ‘Globec’ in the database and then combing through the results for relevant data.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import geopandas as gpd
from datetime import datetime
import os
from matplotlib import ticker
import datetime as dt
import plotly.express as px
import cmocean as cm
import cmocean.cm as cmo
import matplotlib.gridspec as gridspec
import time
import matplotlib.ticker as mticker
bottle = pd.read_excel('bottle_data.xlsx')
bottle=bottle.rename(columns={'yr':'year','sta_std':'station_std','chl_a':'chl'})
bottle['datetime']= pd.to_datetime(bottle[['year', 'month', 'day']]) #datetime variable
bottle = bottle[bottle['chl_qcf'] != 1] #remove rows where control flag states bad chl
bottle['depth'] = bottle['pressure'] / 1.02 #convert pressure to depth 
bottle['HPLC'] = 1 #no hplc
#triplicate flag
counts_series = bottle[['depth','datetime','lat','lon']].value_counts() #count how many unique depth, datetime, lat, and lons there are
counts_df = counts_series.reset_index(name='freq_uniq')
bottle = pd.merge(bottle, counts_df, on=['depth','datetime','lat','lon'], how='left') #add frequency column to original dataframe
#sometimes, triplicate specific times are recorded (ex: 3:00, 3:05, 3:10 ), so also check for unique datehour entries
bottle['date_hour'] = bottle['datetime'].dt.strftime('%Y-%m-%d %H')
counts_series = bottle[['depth','date_hour','lat','lon']].value_counts() #count how many unique datehour, lat, and lons there are
counts_df = counts_series.reset_index(name='freq_hour')
bottle = pd.merge(bottle, counts_df, on=['depth','date_hour','lat','lon'], how='left') #add frequency column to original dataframe
bottle['triplicate'] = 1 #assume bad unless otherwise said
bottle.loc[bottle['freq_uniq'] == 3, 'triplicate'] = 0 #if there was a unique datetime, lat, and lon that happened 3 times, triplicate
bottle.loc[(bottle['freq_uniq'] == 1) &(bottle['freq_hour'] == 3), 'triplicate'] = 0 #if 1 unique datetime and 3 unique date_hours, assume triplicate

bottle=bottle[['datetime', 'cruise_id','station_std', 'lat', 'lon', 'chl', 'depth','HPLC','triplicate']]
bottle['project']='U.S. GLOBEC Northeast Pacific (NEP)'
bottle['source']='BCO-DMO'
bottle['contact']='Suzan S Pool'
bottle['affiliation']='Oregon State University'
bottle['url']='https://www.bco-dmo.org/dataset/2461'
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[2], line 1
----> 1 bottle = pd.read_excel('bottle_data.xlsx')
      2 bottle=bottle.rename(columns={'yr':'year','sta_std':'station_std','chl_a':'chl'})
      3 bottle['datetime']= pd.to_datetime(bottle[['year', 'month', 'day']]) #datetime variable

File ~\AppData\Local\anaconda3\Lib\site-packages\pandas\io\excel\_base.py:495, in read_excel(io, sheet_name, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skiprows, nrows, na_values, keep_default_na, na_filter, verbose, parse_dates, date_parser, date_format, thousands, decimal, comment, skipfooter, storage_options, dtype_backend, engine_kwargs)
    493 if not isinstance(io, ExcelFile):
    494     should_close = True
--> 495     io = ExcelFile(
    496         io,
    497         storage_options=storage_options,
    498         engine=engine,
    499         engine_kwargs=engine_kwargs,
    500     )
    501 elif engine and engine != io.engine:
    502     raise ValueError(
    503         "Engine should not be specified when passing "
    504         "an ExcelFile - ExcelFile already has the engine set"
    505     )

File ~\AppData\Local\anaconda3\Lib\site-packages\pandas\io\excel\_base.py:1550, in ExcelFile.__init__(self, path_or_buffer, engine, storage_options, engine_kwargs)
   1548     ext = "xls"
   1549 else:
-> 1550     ext = inspect_excel_format(
   1551         content_or_path=path_or_buffer, storage_options=storage_options
   1552     )
   1553     if ext is None:
   1554         raise ValueError(
   1555             "Excel file format cannot be determined, you must specify "
   1556             "an engine manually."
   1557         )

File ~\AppData\Local\anaconda3\Lib\site-packages\pandas\io\excel\_base.py:1402, in inspect_excel_format(content_or_path, storage_options)
   1399 if isinstance(content_or_path, bytes):
   1400     content_or_path = BytesIO(content_or_path)
-> 1402 with get_handle(
   1403     content_or_path, "rb", storage_options=storage_options, is_text=False
   1404 ) as handle:
   1405     stream = handle.handle
   1406     stream.seek(0)

File ~\AppData\Local\anaconda3\Lib\site-packages\pandas\io\common.py:882, in get_handle(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)
    873         handle = open(
    874             handle,
    875             ioargs.mode,
   (...)
    878             newline="",
    879         )
    880     else:
    881         # Binary mode
--> 882         handle = open(handle, ioargs.mode)
    883     handles.append(handle)
    885 # Convert BytesIO or file objects passed with an encoding

FileNotFoundError: [Errno 2] No such file or directory: 'bottle_data.xlsx'
nuts_chl = pd.read_excel('nuts_chl.xlsx')
nuts_chl = nuts_chl[nuts_chl['year'] >=2000] #for this specific algorithm, only keep dates from 2000 on
nuts_chl=nuts_chl.rename(columns={'month_gmt':'month','day_gmt':'day','time_gmt':'time','cruiseid':'cruise_id','chl_a':'chl'})
nuts_chl['time'] = [f"{int(t):04d}" for t in nuts_chl['time']]  #sometimes the leading 0s are removed, so pad until time is 4 integers long
nuts_chl['hour'] = [int(t[:2]) for t in nuts_chl['time']]
nuts_chl['minute'] = [int(t[2:4]) for t in nuts_chl['time']]
nuts_chl['datetime']= pd.to_datetime(nuts_chl[['year', 'month', 'day', 'hour','minute']]) 
nuts_chl['HPLC']=1 #no HPLC
nuts_chl['depth'] = nuts_chl['press'] / 1.02 #convert pressure to depth 
#triplicate flag
counts_series = nuts_chl[['depth','datetime','lat','lon']].value_counts()
counts_df = counts_series.reset_index(name='freq_uniq')
nuts_chl = pd.merge(nuts_chl, counts_df, on=['depth','datetime','lat','lon'], how='left') #add frequency column to original dataframe
nuts_chl['date_hour'] = nuts_chl['datetime'].dt.strftime('%Y-%m-%d %H')
counts_series = nuts_chl[['depth','date_hour','lat','lon']].value_counts() 
counts_df = counts_series.reset_index(name='freq_hour')
nuts_chl = pd.merge(nuts_chl, counts_df, on=['depth','date_hour','lat','lon'], how='left') #add frequency column to original dataframe
nuts_chl['triplicate'] = 1 #assume bad unless otherwise said
nuts_chl.loc[nuts_chl['freq_uniq'] == 3, 'triplicate'] = 0 
nuts_chl.loc[(nuts_chl['freq_uniq'] == 1) &(nuts_chl['freq_hour'] == 3), 'triplicate'] = 0
nuts_chl=nuts_chl[[ 'datetime','cruise_id', 'station_std', 'lat', 'lon', 'chl', 'HPLC', 'depth','triplicate']]
nuts_chl['project']='U.S. GLOBEC Northeast Pacific (NEP)'
nuts_chl['source']='BCO-DMO'
nuts_chl['contact']='Patricia Wheeler'
nuts_chl['affiliation']='Oregon State University'
nuts_chl['url']='https://www.bco-dmo.org/dataset/2456'
phyto = pd.read_excel('phytoplankton.xlsx')
phyto=phyto.rename(columns={'month_utc':'month','day_utc':'day','time_utc':'time','cruise':'cruise_id', 'chl_a':'chl','chl_a_hplc':'chl_a'})
phyto['time'] = [f"{int(t):04d}" for t in phyto['time']]  
phyto['hour'] = [int(t[:2]) for t in phyto['time']]
phyto['minute'] = [int(t[2:4]) for t in phyto['time']]
phyto['datetime']= pd.to_datetime(phyto[['year', 'month', 'day', 'hour','minute']]) 
phyto = phyto[phyto['platform'] == 'ShipCTD']
#this dataset has both chlorophyll and HPLC chlorophyll (chl_a)
phyto['HPLC'] = 1 #initiate all points as no hplc
phyto.loc[phyto['chl_a'] != 'nd', 'HPLC'] = 0 #if hplc column is not empty, change to 0
#triplicate flag
counts_series = phyto[['depth','datetime','lat','lon']].value_counts() #count how many unique depth, datetime, lat, and lons there are
counts_df = counts_series.reset_index(name='freq_uniq')
phyto = pd.merge(phyto, counts_df, on=['depth','datetime','lat','lon'], how='left') #add frequency column to original dataframe
phyto['date_hour'] = phyto['datetime'].dt.strftime('%Y-%m-%d %H')
counts_series = phyto[['depth','date_hour','lat','lon']].value_counts() #count how many unique datehour, lat, and lons there are
counts_df = counts_series.reset_index(name='freq_hour')
phyto = pd.merge(phyto, counts_df, on=['depth','date_hour','lat','lon'], how='left') #add frequency column to original dataframe
phyto['triplicate'] = 1 #assume bad unless otherwise said
phyto.loc[phyto['freq_uniq'] == 3, 'triplicate'] = 0 #if there was a unique datetime, lat, and lon that happened 3 times, triplicate
phyto.loc[(phyto['freq_uniq'] == 1) &(phyto['freq_hour'] == 3), 'triplicate'] = 0
phyto=phyto[[ 'cruise_id','datetime','lat', 'lon',  'depth', 'chl', 'chl_a','HPLC','triplicate']]
phyto['project']='U.S. GLOBEC Northeast Pacific (NEP)'
phyto['source']='BCO-DMO'
phyto['contact']='Ricardo Letelier'
phyto['affiliation']='Oregon State University'
phyto['url']='https://www.bco-dmo.org/dataset/3097'

Concatinate the dataframes together, and then remove any repeat data found on SeaBASS

globec = pd.concat([bottle,nuts_chl,phyto]).reset_index(drop=True)
globec = globec[globec['depth'] <=150] #for this algorithm, only keep epths above 150m

#SEABASS already has globec data, so compare and remove repeated data 
sb_west = pd.read_excel('allchl_SB_tripFLAGS.xlsx')
sb_west=sb_west[sb_west['experiment']=='GLOBEC'] #reduce to only GLOBEC data

sb_west=sb_west[['lat', 'lon', 'datetime','depth']]
#merge sb_west with globec on lat, lon, datetime, and depth
globec2 = globec.merge(sb_west[['datetime', 'lat', 'lon','depth']], on=['datetime', 'lat', 'lon','depth'], how='left', indicator=True) 
globec2 =  globec2[globec2['_merge'] != 'both'] #remove rows that are in both seabass and globec (i.e. seabass will keep the unique points)
globec2=globec2[['datetime', 'lat', 'lon', 'chl', 'chl_a','depth','cruise_id', 'station_std', 'HPLC', 'triplicate', 'project', 'source', 'contact', 
                 'affiliation', 'url']]
globec2 = globec2.rename(columns={'cruise_id': 'cruise','station_std':'station','project':'experiment','contact':'investigators','affiliation':'affiliations'})

globec2.loc[globec2['chl'].str.contains('nd', na=False), 'chl'] = np.nan #turn 'nd' to nan
globec2['chl'] = globec2['chl'].astype(float) 
globec2.loc[globec2['chl_a'].str.contains('nd', na=False), 'chl_a'] = np.nan
globec2['chl_a'] = globec2['chl_a'].astype(float) 

plots#

globec = pd.read_excel(r'C:\Users\gianna.milton\Documents\Python\Coastal_chl_final\globec_chl_qc.xlsx')
year_test=globec.copy()
year_test['datetime'] = pd.to_datetime(year_test['datetime'])
year_test['year'] = year_test['datetime'].dt.year
grouped = year_test.groupby(['year']).size().reset_index(name='DataPoints')

# Create bar chart
fig = px.bar(grouped,x='year', y='DataPoints', title='Yearly distribution all data',
             labels={'year': 'Year', 'DataPoints': 'Number of Data Points', 'metadata': 'Metadata'},)
fig.update_xaxes(range=[1999,2027])
fig.update_layout(barmode='stack')  # ensures stacking
fig.show()
_images/b5c92fce0a3ff8e0ae11dd751e44b9cc028a03b585a27ef3018cd43869787be6.png
from matplotlib.colors import LogNorm # Important for high-variance data
fig = plt.figure(figsize=(15, 13))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS)
ax.add_feature(cfeature.STATES)
hb = ax.hexbin(year_test.lon, year_test.lat, gridsize=(10,10), cmap='inferno_r', mincnt=1, transform=ccrs.PlateCarree(),norm=LogNorm()) 
cb = plt.colorbar(hb, ax=ax, orientation='horizontal', pad=0.02, shrink=0.8)
cb.set_label('Number of Datapoints', fontsize=14)
gl=ax.gridlines(linewidth=0.2,color='grey',alpha=0.7,linestyle='-', draw_labels=True, x_inline= False,y_inline=False)
gl.xformatter=LONGITUDE_FORMATTER
gl.yformatter=LATITUDE_FORMATTER
gl.top_labels = False    # Disable top labels
gl.right_labels = False  # Disable right labels
ax.set_xlim(min(year_test.lon)-2,max(year_test.lon)+2)
ax.set_ylim(min(year_test.lat)-2,max(year_test.lat)+2)

ax.set_title('Spatial Data Density', fontsize=18, fontweight='bold')

plt.show()
_images/1d8305d3b9c8201345fd817bce06df9b3f6e982158c0648e8f7b99543182e1e8.png