diff --git a/notebooks/01_exploration.ipynb b/notebooks/01_exploration.ipynb index 7d3e2d5..0430e6f 100644 --- a/notebooks/01_exploration.ipynb +++ b/notebooks/01_exploration.ipynb @@ -17,13 +17,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-19T02:55:24.823837Z", - "start_time": "2018-12-19T02:55:16.373122Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Enable autoreloading of our modules. \n", @@ -36,14 +31,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-19T02:55:24.844241Z", - "start_time": "2018-12-19T02:55:24.823837Z" - }, - "scrolled": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from IPython.core.debugger import set_trace\n", @@ -80,36 +69,13 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": { - "ExecuteTime": { - "end_time": "2018-12-19T02:57:41.855174Z", - "start_time": "2018-12-19T02:57:13.770371Z" - }, "pixiedust": { "displayParams": {} - }, - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Importing waves.csv\n", - "Importing tides.csv\n", - "Importing profiles.csv\n", - "Importing sites.csv\n", - "Importing profile_features_crest_toes.csv\n", - "Importing impacts_forecasted_foreshore_slope_sto06.csv\n", - "Importing impacts_forecasted_mean_slope_sto06.csv\n", - "Importing impacts_observed.csv\n", - "Importing twl_foreshore_slope_sto06.csv\n", - "Importing twl_mean_slope_sto06.csv\n", - "Done!\n" - ] } - ], + }, + "outputs": [], "source": [ "def df_from_csv(csv, index_col, data_folder='../data/interim'):\n", " print('Importing {}'.format(csv))\n", @@ -142,12 +108,7 @@ }, { "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-11-27T23:02:57.631306Z", - "start_time": "2018-11-27T23:02:57.615263Z" - } - }, + "metadata": {}, "source": [ "## Profile/timeseries dashboard" ] @@ -163,33 +124,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": { - "ExecuteTime": { - "end_time": "2018-12-19T02:59:16.919983Z", - "start_time": "2018-12-19T02:59:15.659950Z" - }, "code_folding": [ 408 ], "hide_input": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a209da7c5b98416990efed34f38e82f0", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(VBox(children=(HTML(value='Filter by observed and predicted impacts:'), HBox(children=(V…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Create widgets for filtering by observed and forecasted impacts\n", "filter_title = widgets.HTML(\n", @@ -665,13 +607,8 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-18T21:44:39.877356Z", - "start_time": "2018-12-18T21:44:39.864319Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "df_sites.site_no.to_csv('temp.csv')" @@ -680,12 +617,7 @@ { "cell_type": "markdown", "metadata": { - "ExecuteTime": { - "end_time": "2018-11-22T22:52:36.039701Z", - "start_time": "2018-11-22T22:52:36.035189Z" - }, - "hide_input": true, - "scrolled": true + "hide_input": true }, "source": [ "## Confusion matrix\n", @@ -694,32 +626,12 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": { - "ExecuteTime": { - "end_time": "2018-12-19T03:23:53.913428Z", - "start_time": "2018-12-19T03:23:52.722678Z" - }, "code_folding": [], - "hide_input": false, - "scrolled": false + "hide_input": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "6693731b472f4a9987be65ae4d528530", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(VBox(children=(HTML(value='Filter by beach:'), SelectMultiple(index=(0, 1, 2, 3, 4, 5, 6…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Create colorscale\n", "rdylgr_cmap = matplotlib.cm.get_cmap('RdYlGn')\n", @@ -823,12 +735,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-11T22:17:29.215527Z", - "start_time": "2018-12-11T22:16:56.023Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "# To output to file\n", @@ -841,12 +748,7 @@ }, { "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-03T23:02:47.179180Z", - "start_time": "2018-12-03T23:02:46.367273Z" - } - }, + "metadata": {}, "source": [ "## Identify sites with no results" ] @@ -861,26 +763,9 @@ }, { "cell_type": "code", - "execution_count": 57, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-13T02:09:10.914191Z", - "start_time": "2018-12-13T02:09:09.913622Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following sites have no slope defined in the twl csv file:\n", - "['ENTRA0078', 'ENTRA0079', 'MANNING0109']\n", - "\n", - "The following sites have no R_high defined in the twl csv file:\n", - "['ENTRA0078', 'ENTRA0079', 'MANNING0109']\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "df_twls = twls['forecasted']['mean_slope_sto06']\n", "\n", @@ -904,1062 +789,9 @@ }, { "cell_type": "code", - "execution_count": 58, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-13T02:09:14.785919Z", - "start_time": "2018-12-13T02:09:14.520986Z" - }, - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
prestorm_swash_volpoststorm_swash_volswash_vol_changeswash_pct_changeprestorm_dune_face_volpoststorm_dune_face_voldune_face_vol_changedune_face_pct_changestorm_regime
site_id
AVOCAn00094.57830.11104.467397.5750NaNNaNNaNNaNNaN
AVOCAs0001NaNNaNNaNNaNNaNNaNNaNNaNNaN
AVOCAs000297.946326.663871.282572.7771NaNNaNNaNNaNNaN
AVOCAs000370.730640.202030.723243.4369NaNNaNNaNNaNNaN
AVOCAs000498.285945.498652.633053.5509NaNNaNNaNNaNNaN
AVOCAs000595.584154.975340.573342.4478NaNNaNNaNNaNNaN
AVOCAs0006113.044167.891245.258240.0359NaNNaNNaNNaNNaN
AVOCAs000765.328344.282121.454432.8409NaNNaNNaNNaNNaN
AVOCAs000852.393345.22437.172813.6904NaNNaNNaNNaNNaN
BILG000120.34057.620712.719862.5344NaNNaNNaNNaNNaN
BILG0002156.420598.171658.165937.1856NaNNaNNaNNaNNaN
BOAT000123.836123.6865-0.0926-0.3885NaNNaNNaNNaNNaN
BOAT000238.839814.081924.757963.7436NaNNaNNaNNaNNaN
BOAT000373.680917.854555.826475.7678NaNNaNNaNNaNNaN
BOAT000473.195423.158350.037268.3610NaNNaNNaNNaNNaN
BOAT000553.512222.453731.058558.0400NaNNaNNaNNaNNaN
BOOM0001236.4540218.491820.87258.8273NaNNaNNaNNaNNaN
CATHIE002463.645238.426125.219139.62450.00.00.0NaNNaN
CATHIE002675.133443.717931.094041.3851NaNNaNNaNNaNNaN
CRESn006937.58968.349529.240177.7877NaNNaNNaNNaNNaN
DEEWHYn0008NaNNaNNaNNaNNaNNaNNaNNaNNaN
DEEWHYn0009NaNNaNNaNNaNNaNNaNNaNNaNNaN
DEEWHYs000562.351424.979737.371659.9372NaNNaNNaNNaNNaN
DEEWHYs00081.06881.36400.00000.0000NaNNaNNaNNaNNaN
DIAMONDn002367.941621.181246.760368.8244NaNNaNNaNNaNNaN
DIAMONDs000674.935742.438232.253643.0416NaNNaNNaNNaNNaN
DIAMONDs0007153.7639127.946926.159517.0128NaNNaNNaNNaNNaN
DUNBn003136.53016.328930.201282.6748NaNNaNNaNNaNNaN
DUNBn0055189.5283134.276056.713929.9237NaNNaNNaNNaNNaN
ELIZA0002NaNNaNNaNNaNNaNNaNNaNNaNNaN
..............................
STOCNs0170198.2785216.6368-18.6067-9.3841NaNNaNNaNNaNNaN
STOCNs0175136.2126114.171522.498416.5171NaNNaNNaNNaNNaN
STOCNs017967.779545.398122.381533.0210NaNNaNNaNNaNNaN
STOCNs0180166.0813149.519515.34419.2389NaNNaNNaNNaNNaN
STOCNs018190.114798.9808-9.1107-10.1102NaNNaNNaNNaNNaN
STOCNs018267.862286.0118-18.1671-26.7705NaNNaNNaNNaNNaN
STOCNs0183125.9085137.7342-12.6233-10.0257NaNNaNNaNNaNNaN
STOCNs0184146.6586123.537123.260315.8602NaNNaNNaNNaNNaN
STOCNs0185141.5421142.7279-1.2619-0.8915NaNNaNNaNNaNNaN
STOCNs0186115.9148123.1507-7.8392-6.7629NaNNaNNaNNaNNaN
STOCNs0187126.5519147.4371-22.4452-17.7359NaNNaNNaNNaNNaN
STOCNs0188345.5234353.8766-11.3322-3.2797NaNNaNNaNNaNNaN
STOCNs0189171.6354134.919235.869720.8988NaNNaNNaNNaNNaN
STOCNs0190151.4113116.638135.016123.1264NaNNaNNaNNaNNaN
STOCS001498.499157.349540.900641.5238NaNNaNNaNNaNNaN
STOCS004336.425611.720824.704867.8225NaNNaNNaNNaNNaN
WAMBE0005NaNNaNNaNNaNNaNNaNNaNNaNNaN
WAMBE001556.272416.042840.229671.4908NaNNaNNaNNaNNaN
WAMBE001697.884939.843258.041759.2958NaNNaNNaNNaNNaN
WAMBE001736.56838.538028.030376.6520NaNNaNNaNNaNNaN
WAMBE001842.142310.549831.592574.9662NaNNaNNaNNaNNaN
WAMBE001939.60979.240430.369376.6714NaNNaNNaNNaNNaN
WAMBE0020NaNNaNNaNNaNNaNNaNNaNNaNNaN
WAMBE0021NaNNaNNaNNaNNaNNaNNaNNaNNaN
WAMBE00221.10340.44780.655659.4166NaNNaNNaNNaNNaN
WAMBE00234.47960.33564.144092.5081NaNNaNNaNNaNNaN
WAMBE002461.847831.300730.547049.3907NaNNaNNaNNaNNaN
WAMBE002545.970714.612531.358268.2134NaNNaNNaNNaNNaN
WAMBE002632.859112.947919.911260.5957NaNNaNNaNNaNNaN
WAMBE002726.413218.71427.699029.1484NaNNaNNaNNaNNaN
\n", - "

242 rows × 9 columns

\n", - "
" - ], - "text/plain": [ - " prestorm_swash_vol poststorm_swash_vol swash_vol_change \\\n", - "site_id \n", - "AVOCAn0009 4.5783 0.1110 4.4673 \n", - "AVOCAs0001 NaN NaN NaN \n", - "AVOCAs0002 97.9463 26.6638 71.2825 \n", - "AVOCAs0003 70.7306 40.2020 30.7232 \n", - "AVOCAs0004 98.2859 45.4986 52.6330 \n", - "AVOCAs0005 95.5841 54.9753 40.5733 \n", - "AVOCAs0006 113.0441 67.8912 45.2582 \n", - "AVOCAs0007 65.3283 44.2821 21.4544 \n", - "AVOCAs0008 52.3933 45.2243 7.1728 \n", - "BILG0001 20.3405 7.6207 12.7198 \n", - "BILG0002 156.4205 98.1716 58.1659 \n", - "BOAT0001 23.8361 23.6865 -0.0926 \n", - "BOAT0002 38.8398 14.0819 24.7579 \n", - "BOAT0003 73.6809 17.8545 55.8264 \n", - "BOAT0004 73.1954 23.1583 50.0372 \n", - "BOAT0005 53.5122 22.4537 31.0585 \n", - "BOOM0001 236.4540 218.4918 20.8725 \n", - "CATHIE0024 63.6452 38.4261 25.2191 \n", - "CATHIE0026 75.1334 43.7179 31.0940 \n", - "CRESn0069 37.5896 8.3495 29.2401 \n", - "DEEWHYn0008 NaN NaN NaN \n", - "DEEWHYn0009 NaN NaN NaN \n", - "DEEWHYs0005 62.3514 24.9797 37.3716 \n", - "DEEWHYs0008 1.0688 1.3640 0.0000 \n", - "DIAMONDn0023 67.9416 21.1812 46.7603 \n", - "DIAMONDs0006 74.9357 42.4382 32.2536 \n", - "DIAMONDs0007 153.7639 127.9469 26.1595 \n", - "DUNBn0031 36.5301 6.3289 30.2012 \n", - "DUNBn0055 189.5283 134.2760 56.7139 \n", - "ELIZA0002 NaN NaN NaN \n", - "... ... ... ... \n", - "STOCNs0170 198.2785 216.6368 -18.6067 \n", - "STOCNs0175 136.2126 114.1715 22.4984 \n", - "STOCNs0179 67.7795 45.3981 22.3815 \n", - "STOCNs0180 166.0813 149.5195 15.3441 \n", - "STOCNs0181 90.1147 98.9808 -9.1107 \n", - "STOCNs0182 67.8622 86.0118 -18.1671 \n", - "STOCNs0183 125.9085 137.7342 -12.6233 \n", - "STOCNs0184 146.6586 123.5371 23.2603 \n", - "STOCNs0185 141.5421 142.7279 -1.2619 \n", - "STOCNs0186 115.9148 123.1507 -7.8392 \n", - "STOCNs0187 126.5519 147.4371 -22.4452 \n", - "STOCNs0188 345.5234 353.8766 -11.3322 \n", - "STOCNs0189 171.6354 134.9192 35.8697 \n", - "STOCNs0190 151.4113 116.6381 35.0161 \n", - "STOCS0014 98.4991 57.3495 40.9006 \n", - "STOCS0043 36.4256 11.7208 24.7048 \n", - "WAMBE0005 NaN NaN NaN \n", - "WAMBE0015 56.2724 16.0428 40.2296 \n", - "WAMBE0016 97.8849 39.8432 58.0417 \n", - "WAMBE0017 36.5683 8.5380 28.0303 \n", - "WAMBE0018 42.1423 10.5498 31.5925 \n", - "WAMBE0019 39.6097 9.2404 30.3693 \n", - "WAMBE0020 NaN NaN NaN \n", - "WAMBE0021 NaN NaN NaN \n", - "WAMBE0022 1.1034 0.4478 0.6556 \n", - "WAMBE0023 4.4796 0.3356 4.1440 \n", - "WAMBE0024 61.8478 31.3007 30.5470 \n", - "WAMBE0025 45.9707 14.6125 31.3582 \n", - "WAMBE0026 32.8591 12.9479 19.9112 \n", - "WAMBE0027 26.4132 18.7142 7.6990 \n", - "\n", - " swash_pct_change prestorm_dune_face_vol \\\n", - "site_id \n", - "AVOCAn0009 97.5750 NaN \n", - "AVOCAs0001 NaN NaN \n", - "AVOCAs0002 72.7771 NaN \n", - "AVOCAs0003 43.4369 NaN \n", - "AVOCAs0004 53.5509 NaN \n", - "AVOCAs0005 42.4478 NaN \n", - "AVOCAs0006 40.0359 NaN \n", - "AVOCAs0007 32.8409 NaN \n", - "AVOCAs0008 13.6904 NaN \n", - "BILG0001 62.5344 NaN \n", - "BILG0002 37.1856 NaN \n", - "BOAT0001 -0.3885 NaN \n", - "BOAT0002 63.7436 NaN \n", - "BOAT0003 75.7678 NaN \n", - "BOAT0004 68.3610 NaN \n", - "BOAT0005 58.0400 NaN \n", - "BOOM0001 8.8273 NaN \n", - "CATHIE0024 39.6245 0.0 \n", - "CATHIE0026 41.3851 NaN \n", - "CRESn0069 77.7877 NaN \n", - "DEEWHYn0008 NaN NaN \n", - "DEEWHYn0009 NaN NaN \n", - "DEEWHYs0005 59.9372 NaN \n", - "DEEWHYs0008 0.0000 NaN \n", - "DIAMONDn0023 68.8244 NaN \n", - "DIAMONDs0006 43.0416 NaN \n", - "DIAMONDs0007 17.0128 NaN \n", - "DUNBn0031 82.6748 NaN \n", - "DUNBn0055 29.9237 NaN \n", - "ELIZA0002 NaN NaN \n", - "... ... ... \n", - "STOCNs0170 -9.3841 NaN \n", - "STOCNs0175 16.5171 NaN \n", - "STOCNs0179 33.0210 NaN \n", - "STOCNs0180 9.2389 NaN \n", - "STOCNs0181 -10.1102 NaN \n", - "STOCNs0182 -26.7705 NaN \n", - "STOCNs0183 -10.0257 NaN \n", - "STOCNs0184 15.8602 NaN \n", - "STOCNs0185 -0.8915 NaN \n", - "STOCNs0186 -6.7629 NaN \n", - "STOCNs0187 -17.7359 NaN \n", - "STOCNs0188 -3.2797 NaN \n", - "STOCNs0189 20.8988 NaN \n", - "STOCNs0190 23.1264 NaN \n", - "STOCS0014 41.5238 NaN \n", - "STOCS0043 67.8225 NaN \n", - "WAMBE0005 NaN NaN \n", - "WAMBE0015 71.4908 NaN \n", - "WAMBE0016 59.2958 NaN \n", - "WAMBE0017 76.6520 NaN \n", - "WAMBE0018 74.9662 NaN \n", - "WAMBE0019 76.6714 NaN \n", - "WAMBE0020 NaN NaN \n", - "WAMBE0021 NaN NaN \n", - "WAMBE0022 59.4166 NaN \n", - "WAMBE0023 92.5081 NaN \n", - "WAMBE0024 49.3907 NaN \n", - "WAMBE0025 68.2134 NaN \n", - "WAMBE0026 60.5957 NaN \n", - "WAMBE0027 29.1484 NaN \n", - "\n", - " poststorm_dune_face_vol dune_face_vol_change \\\n", - "site_id \n", - "AVOCAn0009 NaN NaN \n", - "AVOCAs0001 NaN NaN \n", - "AVOCAs0002 NaN NaN \n", - "AVOCAs0003 NaN NaN \n", - "AVOCAs0004 NaN NaN \n", - "AVOCAs0005 NaN NaN \n", - "AVOCAs0006 NaN NaN \n", - "AVOCAs0007 NaN NaN \n", - "AVOCAs0008 NaN NaN \n", - "BILG0001 NaN NaN \n", - "BILG0002 NaN NaN \n", - "BOAT0001 NaN NaN \n", - "BOAT0002 NaN NaN \n", - "BOAT0003 NaN NaN \n", - "BOAT0004 NaN NaN \n", - "BOAT0005 NaN NaN \n", - "BOOM0001 NaN NaN \n", - "CATHIE0024 0.0 0.0 \n", - "CATHIE0026 NaN NaN \n", - "CRESn0069 NaN NaN \n", - "DEEWHYn0008 NaN NaN \n", - "DEEWHYn0009 NaN NaN \n", - "DEEWHYs0005 NaN NaN \n", - "DEEWHYs0008 NaN NaN \n", - "DIAMONDn0023 NaN NaN \n", - "DIAMONDs0006 NaN NaN \n", - "DIAMONDs0007 NaN NaN \n", - "DUNBn0031 NaN NaN \n", - "DUNBn0055 NaN NaN \n", - "ELIZA0002 NaN NaN \n", - "... ... ... \n", - "STOCNs0170 NaN NaN \n", - "STOCNs0175 NaN NaN \n", - "STOCNs0179 NaN NaN \n", - "STOCNs0180 NaN NaN \n", - "STOCNs0181 NaN NaN \n", - "STOCNs0182 NaN NaN \n", - "STOCNs0183 NaN NaN \n", - "STOCNs0184 NaN NaN \n", - "STOCNs0185 NaN NaN \n", - "STOCNs0186 NaN NaN \n", - "STOCNs0187 NaN NaN \n", - "STOCNs0188 NaN NaN \n", - "STOCNs0189 NaN NaN \n", - "STOCNs0190 NaN NaN \n", - "STOCS0014 NaN NaN \n", - "STOCS0043 NaN NaN \n", - "WAMBE0005 NaN NaN \n", - "WAMBE0015 NaN NaN \n", - "WAMBE0016 NaN NaN \n", - "WAMBE0017 NaN NaN \n", - "WAMBE0018 NaN NaN \n", - "WAMBE0019 NaN NaN \n", - "WAMBE0020 NaN NaN \n", - "WAMBE0021 NaN NaN \n", - "WAMBE0022 NaN NaN \n", - "WAMBE0023 NaN NaN \n", - "WAMBE0024 NaN NaN \n", - "WAMBE0025 NaN NaN \n", - "WAMBE0026 NaN NaN \n", - "WAMBE0027 NaN NaN \n", - "\n", - " dune_face_pct_change storm_regime \n", - "site_id \n", - "AVOCAn0009 NaN NaN \n", - "AVOCAs0001 NaN NaN \n", - "AVOCAs0002 NaN NaN \n", - "AVOCAs0003 NaN NaN \n", - "AVOCAs0004 NaN NaN \n", - "AVOCAs0005 NaN NaN \n", - "AVOCAs0006 NaN NaN \n", - "AVOCAs0007 NaN NaN \n", - "AVOCAs0008 NaN NaN \n", - "BILG0001 NaN NaN \n", - "BILG0002 NaN NaN \n", - "BOAT0001 NaN NaN \n", - "BOAT0002 NaN NaN \n", - "BOAT0003 NaN NaN \n", - "BOAT0004 NaN NaN \n", - "BOAT0005 NaN NaN \n", - "BOOM0001 NaN NaN \n", - "CATHIE0024 NaN NaN \n", - "CATHIE0026 NaN NaN \n", - "CRESn0069 NaN NaN \n", - "DEEWHYn0008 NaN NaN \n", - "DEEWHYn0009 NaN NaN \n", - "DEEWHYs0005 NaN NaN \n", - "DEEWHYs0008 NaN NaN \n", - "DIAMONDn0023 NaN NaN \n", - "DIAMONDs0006 NaN NaN \n", - "DIAMONDs0007 NaN NaN \n", - "DUNBn0031 NaN NaN \n", - "DUNBn0055 NaN NaN \n", - "ELIZA0002 NaN NaN \n", - "... ... ... \n", - "STOCNs0170 NaN NaN \n", - "STOCNs0175 NaN NaN \n", - "STOCNs0179 NaN NaN \n", - "STOCNs0180 NaN NaN \n", - "STOCNs0181 NaN NaN \n", - "STOCNs0182 NaN NaN \n", - "STOCNs0183 NaN NaN \n", - "STOCNs0184 NaN NaN \n", - "STOCNs0185 NaN NaN \n", - "STOCNs0186 NaN NaN \n", - "STOCNs0187 NaN NaN \n", - "STOCNs0188 NaN NaN \n", - "STOCNs0189 NaN NaN \n", - "STOCNs0190 NaN NaN \n", - "STOCS0014 NaN NaN \n", - "STOCS0043 NaN NaN \n", - "WAMBE0005 NaN NaN \n", - "WAMBE0015 NaN NaN \n", - "WAMBE0016 NaN NaN \n", - "WAMBE0017 NaN NaN \n", - "WAMBE0018 NaN NaN \n", - "WAMBE0019 NaN NaN \n", - "WAMBE0020 NaN NaN \n", - "WAMBE0021 NaN NaN \n", - "WAMBE0022 NaN NaN \n", - "WAMBE0023 NaN NaN \n", - "WAMBE0024 NaN NaN \n", - "WAMBE0025 NaN NaN \n", - "WAMBE0026 NaN NaN \n", - "WAMBE0027 NaN NaN \n", - "\n", - "[242 rows x 9 columns]" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "df_impacts = impacts['observed']\n", "df_no_obs_impacts = df_impacts[df_impacts.storm_regime.isnull()]\n", @@ -2000,253 +832,9 @@ }, { "cell_type": "code", - "execution_count": 74, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-13T02:37:07.225965Z", - "start_time": "2018-12-13T02:37:07.213921Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "231 sites have no dune crests:\n", - "AVOCAn0009\n", - "AVOCAs0001\n", - "AVOCAs0002\n", - "AVOCAs0003\n", - "AVOCAs0004\n", - "AVOCAs0005\n", - "AVOCAs0006\n", - "AVOCAs0007\n", - "AVOCAs0008\n", - "BILG0001\n", - "BILG0002\n", - "BOAT0001\n", - "BOAT0002\n", - "BOAT0003\n", - "BOAT0004\n", - "BOAT0005\n", - "BOOM0001\n", - "CATHIE0026\n", - "CRESn0069\n", - "DEEWHYn0008\n", - "DEEWHYn0009\n", - "DEEWHYs0005\n", - "DEEWHYs0008\n", - "DIAMONDs0006\n", - "DIAMONDs0007\n", - "ENTRA0005\n", - "ENTRA0006\n", - "ENTRA0077\n", - "ENTRA0078\n", - "ENTRA0079\n", - "FOST0003\n", - "GRANTSn0004\n", - "GRANTSn0005\n", - "GRANTSn0006\n", - "GRANTSn0007\n", - "GRANTSn0008\n", - "GRANTSn0009\n", - "GRANTSn0021\n", - "GRANTSs0014\n", - "HARGs0003\n", - "HARGs0004\n", - "HARGs0005\n", - "HARR0056\n", - "LHOUSE0001\n", - "LHOUSE0002\n", - "LHOUSE0003\n", - "LHOUSE0004\n", - "LHOUSE0012\n", - "LHOUSE0013\n", - "LHOUSEs0015\n", - "MACM0008\n", - "MACM0012\n", - "MACM0013\n", - "MACM0014\n", - "MACM0015\n", - "MACM0016\n", - "MANNING0001\n", - "MANNING0002\n", - "MANNING0003\n", - "MANNING0004\n", - "MANNING0005\n", - "MANNING0101\n", - "MANNING0102\n", - "MANNING0103\n", - "MANNING0104\n", - "MANNING0105\n", - "MANNING0106\n", - "MANNING0107\n", - "MANNING0108\n", - "MANNING0109\n", - "MONA0001\n", - "MONA0002\n", - "MONA0003\n", - "MONA0014\n", - "MONA0015\n", - "MONA0016\n", - "MONA0017\n", - "MONA0018\n", - "MONA0019\n", - "MONA0020\n", - "MONA0021\n", - "NAMB0027\n", - "NAMB0041\n", - "NARRA0001\n", - "NARRA0028\n", - "NARRA0035\n", - "NINEMn0050\n", - "OLDBAR0035\n", - "PEARLn0001\n", - "PEARLn0002\n", - "PEARLn0003\n", - "PEARLn0004\n", - "PEARLs0003\n", - "PEARLs0004\n", - "PEARLs0005\n", - "STOCNn0012\n", - "STOCNn0013\n", - "STOCNn0014\n", - "STOCNn0015\n", - "STOCNn0016\n", - "STOCNn0017\n", - "STOCNn0018\n", - "STOCNn0019\n", - "STOCNn0020\n", - "STOCNn0021\n", - "STOCNn0022\n", - "STOCNn0023\n", - "STOCNn0024\n", - "STOCNn0025\n", - "STOCNn0026\n", - "STOCNn0027\n", - "STOCNn0028\n", - "STOCNn0029\n", - "STOCNn0030\n", - "STOCNn0031\n", - "STOCNn0032\n", - "STOCNn0033\n", - "STOCNn0034\n", - "STOCNn0035\n", - "STOCNn0036\n", - "STOCNn0037\n", - "STOCNn0038\n", - "STOCNn0039\n", - "STOCNn0044\n", - "STOCNn0059\n", - "STOCNn0062\n", - "STOCNn0063\n", - "STOCNn0064\n", - "STOCNn0065\n", - "STOCNs0022\n", - "STOCNs0025\n", - "STOCNs0026\n", - "STOCNs0031\n", - "STOCNs0045\n", - "STOCNs0048\n", - "STOCNs0049\n", - "STOCNs0053\n", - "STOCNs0055\n", - "STOCNs0056\n", - "STOCNs0057\n", - "STOCNs0058\n", - "STOCNs0059\n", - "STOCNs0060\n", - "STOCNs0061\n", - "STOCNs0062\n", - "STOCNs0073\n", - "STOCNs0079\n", - "STOCNs0088\n", - "STOCNs0089\n", - "STOCNs0090\n", - "STOCNs0091\n", - "STOCNs0092\n", - "STOCNs0093\n", - "STOCNs0094\n", - "STOCNs0095\n", - "STOCNs0096\n", - "STOCNs0097\n", - "STOCNs0098\n", - "STOCNs0099\n", - "STOCNs0100\n", - "STOCNs0101\n", - "STOCNs0102\n", - "STOCNs0103\n", - "STOCNs0104\n", - "STOCNs0105\n", - "STOCNs0106\n", - "STOCNs0107\n", - "STOCNs0108\n", - "STOCNs0109\n", - "STOCNs0110\n", - "STOCNs0111\n", - "STOCNs0112\n", - "STOCNs0113\n", - "STOCNs0114\n", - "STOCNs0115\n", - "STOCNs0116\n", - "STOCNs0117\n", - "STOCNs0118\n", - "STOCNs0119\n", - "STOCNs0120\n", - "STOCNs0121\n", - "STOCNs0122\n", - "STOCNs0123\n", - "STOCNs0124\n", - "STOCNs0125\n", - "STOCNs0126\n", - "STOCNs0127\n", - "STOCNs0128\n", - "STOCNs0137\n", - "STOCNs0141\n", - "STOCNs0144\n", - "STOCNs0150\n", - "STOCNs0155\n", - "STOCNs0156\n", - "STOCNs0157\n", - "STOCNs0158\n", - "STOCNs0159\n", - "STOCNs0160\n", - "STOCNs0167\n", - "STOCNs0168\n", - "STOCNs0169\n", - "STOCNs0170\n", - "STOCNs0175\n", - "STOCNs0179\n", - "STOCNs0180\n", - "STOCNs0181\n", - "STOCNs0182\n", - "STOCNs0183\n", - "STOCNs0184\n", - "STOCNs0185\n", - "STOCNs0186\n", - "STOCNs0187\n", - "STOCNs0188\n", - "STOCNs0189\n", - "STOCNs0190\n", - "STOCS0014\n", - "STOCS0043\n", - "WAMBE0005\n", - "WAMBE0015\n", - "WAMBE0016\n", - "WAMBE0017\n", - "WAMBE0018\n", - "WAMBE0019\n", - "WAMBE0020\n", - "WAMBE0021\n", - "WAMBE0022\n", - "WAMBE0023\n", - "WAMBE0024\n", - "WAMBE0025\n", - "WAMBE0026\n", - "WAMBE0027\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "df_no_crests = df_profile_features_crest_toes.query('profile_type==\"prestorm\" & (dune_crest_x != dune_crest_x)')\n", "print('{} sites have no dune crests:'.format(len(df_no_crests)))\n", diff --git a/notebooks/04_profile_picker.ipynb b/notebooks/04_profile_picker.ipynb deleted file mode 100644 index ce31898..0000000 --- a/notebooks/04_profile_picker.ipynb +++ /dev/null @@ -1,743 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-05T00:54:50.235522Z", - "start_time": "2018-12-05T00:54:42.731587Z" - } - }, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import os\n", - "import numpy.ma as ma\n", - "\n", - "import numpy\n", - "from pyearth import Earth\n", - "from matplotlib import pyplot\n", - "\n", - "np.random.seed(2017)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-05T00:54:54.936556Z", - "start_time": "2018-12-05T00:54:50.271465Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Importing profiles.csv\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\z5189959\\Desktop\\nsw-2016-storm-impact\\.venv\\lib\\site-packages\\numpy\\lib\\arraysetops.py:522: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", - " mask |= (ar1 == a)\n" - ] - } - ], - "source": [ - "def df_from_csv(csv, index_col, data_folder='../data/interim'):\n", - " print('Importing {}'.format(csv))\n", - " return pd.read_csv(os.path.join(data_folder,csv), index_col=index_col)\n", - "\n", - "df_profiles = df_from_csv('profiles.csv', index_col=[0, 1, 2])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-04T23:49:04.770025Z", - "start_time": "2018-12-04T23:49:04.265699Z" - } - }, - "source": [ - "## Try using pyearth" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-05T01:54:00.320555Z", - "start_time": "2018-12-05T01:53:58.905803Z" - }, - "code_folding": [ - 5, - 20, - 31, - 40 - ], - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ { 'slope': -0.06617795750241562,\n", - " 'type': 'land',\n", - " 'x_end': 63.0,\n", - " 'x_start': 40.5,\n", - " 'z_end': 9.943990321404,\n", - " 'z_mean': 9.20564731619437,\n", - " 'z_start': 8.348161899068224},\n", - " { 'slope': -0.09424321140128608,\n", - " 'x_end': 69.0,\n", - " 'x_start': 63.0,\n", - " 'z_end': 10.532233612481347,\n", - " 'z_mean': 10.310033111599461,\n", - " 'z_start': 10.062895267627406},\n", - " { 'slope': 0.07278057230588836,\n", - " 'x_end': 96.0,\n", - " 'x_start': 69.0,\n", - " 'z_end': 9.071344271845113,\n", - " 'z_mean': 9.942154478828064,\n", - " 'z_start': 10.600758995514095},\n", - " { 'slope': 0.010222110457455531,\n", - " 'x_end': 105.0,\n", - " 'x_start': 96.0,\n", - " 'z_end': 8.940005362742282,\n", - " 'z_mean': 8.960960594412768,\n", - " 'z_start': 9.032076613769703},\n", - " { 'slope': -0.006783434031405405,\n", - " 'x_end': 171.0,\n", - " 'x_start': 105.0,\n", - " 'z_end': 9.377956662680647,\n", - " 'z_mean': 9.12742372282666,\n", - " 'z_start': 8.933411617025225},\n", - " { 'slope': 0.04487864391014401,\n", - " 'x_end': 177.0,\n", - " 'x_start': 171.0,\n", - " 'z_end': 9.159019876027982,\n", - " 'z_mean': 9.312931484934994,\n", - " 'z_start': 9.382296403384132},\n", - " { 'slope': 0.28077356578561746,\n", - " 'x_end': 198.0,\n", - " 'x_start': 177.0,\n", - " 'z_end': 3.9204468853264824,\n", - " 'z_mean': 6.663384763062234,\n", - " 'z_start': 9.046622396024778},\n", - " { 'slope': 0.08534897213040515,\n", - " 'x_end': 204.0,\n", - " 'x_start': 198.0,\n", - " 'z_end': 3.3242471595653202,\n", - " 'z_mean': 3.4936366209579663,\n", - " 'z_start': 3.754184455095742},\n", - " { 'slope': 0.02118839446027552,\n", - " 'type': 'berm',\n", - " 'x_end': 252.0,\n", - " 'x_start': 204.0,\n", - " 'z_end': 2.11417905440144,\n", - " 'z_mean': 2.7099232730105225,\n", - " 'z_start': 3.26349144192863},\n", - " { 'slope': 0.06273209072355844,\n", - " 'type': 'foreshore',\n", - " 'x_end': 285.5,\n", - " 'x_start': 252.0,\n", - " 'z_end': 0.0614306329519847,\n", - " 'z_mean': 1.0727411577535628,\n", - " 'z_start': 2.070753280242818}]\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ { 'slope': -0.006232531713848901,\n", - " 'type': 'land',\n", - " 'x_end': 87.0,\n", - " 'x_start': 54.0,\n", - " 'z_end': 18.907362973310256,\n", - " 'z_mean': 18.226859544431967,\n", - " 'z_start': 18.244707421270647},\n", - " { 'slope': 0.10048504940215892,\n", - " 'x_end': 90.0,\n", - " 'x_start': 87.0,\n", - " 'z_end': 18.81114066206634,\n", - " 'z_mean': 18.95124513461371,\n", - " 'z_start': 19.011103385573893},\n", - " { 'slope': 0.7867562476656698,\n", - " 'x_end': 105.0,\n", - " 'x_start': 90.0,\n", - " 'z_end': 8.264119331473214,\n", - " 'z_mean': 13.443236505259332,\n", - " 'z_start': 18.482680684877423},\n", - " { 'slope': 0.138231538826561,\n", - " 'x_end': 108.0,\n", - " 'x_start': 105.0,\n", - " 'z_end': 7.625424767531349,\n", - " 'z_mean': 7.719382219664138,\n", - " 'z_start': 7.899377829775406},\n", - " { 'slope': -0.24669278691155727,\n", - " 'x_end': 111.0,\n", - " 'x_start': 108.0,\n", - " 'z_end': 8.25183247996525,\n", - " 'z_mean': 8.037437899312298,\n", - " 'z_start': 7.767262334494268},\n", - " { 'slope': -0.022224358740496428,\n", - " 'x_end': 138.0,\n", - " 'x_start': 111.0,\n", - " 'z_end': 8.805851879663944,\n", - " 'z_mean': 8.69776858676918,\n", - " 'z_start': 8.321441165942321},\n", - " { 'slope': 0.011690412029154119,\n", - " 'x_end': 174.0,\n", - " 'x_start': 138.0,\n", - " 'z_end': 8.399119455457473,\n", - " 'z_mean': 8.591198407135863,\n", - " 'z_start': 8.769268316237742},\n", - " { 'slope': 0.06596545786924302,\n", - " 'x_end': 183.0,\n", - " 'x_start': 174.0,\n", - " 'z_end': 7.84469125226732,\n", - " 'z_mean': 8.148131103000907,\n", - " 'z_start': 8.376950179198564},\n", - " { 'slope': 0.19014969366205653,\n", - " 'x_end': 213.0,\n", - " 'x_start': 183.0,\n", - " 'z_end': 2.9990666738937883,\n", - " 'z_mean': 5.087790707786215,\n", - " 'z_start': 7.748699573256347},\n", - " { 'slope': 0.012096582737314357,\n", - " 'type': 'berm',\n", - " 'x_end': 219.0,\n", - " 'x_start': 213.0,\n", - " 'z_end': 2.9163517442302123,\n", - " 'z_mean': 2.9347601110839676,\n", - " 'z_start': 2.975348176164513},\n", - " { 'slope': 0.03056014910314705,\n", - " 'x_end': 255.0,\n", - " 'x_start': 219.0,\n", - " 'z_end': 2.050738949067936,\n", - " 'z_mean': 2.653057687157337,\n", - " 'z_start': 2.927273834441084},\n", - " { 'slope': 0.06806395674070564,\n", - " 'type': 'foreshore',\n", - " 'x_end': 285.5,\n", - " 'x_start': 255.0,\n", - " 'z_end': 0.04691924522162938,\n", - " 'z_mean': 1.100297110086715,\n", - " 'z_start': 2.0068617905167647}]\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ { 'slope': 0.0008328773436106309,\n", - " 'type': 'berm',\n", - " 'x_end': 58.0,\n", - " 'x_start': 37.0,\n", - " 'z_end': 1.8572595562110041,\n", - " 'z_mean': 1.8943805115420846,\n", - " 'z_start': 1.910394386927169},\n", - " { 'slope': -0.021220543877635122,\n", - " 'type': 'berm',\n", - " 'x_end': 138.0,\n", - " 'x_start': 58.0,\n", - " 'z_end': 3.5977328867797485,\n", - " 'z_mean': 2.7247048500781106,\n", - " 'z_start': 1.8468598845719795},\n", - " { 'slope': -0.06381773295732189,\n", - " 'x_end': 142.0,\n", - " 'x_start': 138.0,\n", - " 'z_end': 3.8359998018824712,\n", - " 'z_mean': 3.741723519647647,\n", - " 'z_start': 3.6479493664613014},\n", - " { 'slope': -0.016157748520191067,\n", - " 'x_end': 157.0,\n", - " 'x_start': 142.0,\n", - " 'z_end': 4.169920934351783,\n", - " 'z_mean': 4.013083138991948,\n", - " 'z_start': 3.8843177342116757},\n", - " { 'slope': -0.1322171985012303,\n", - " 'x_end': 177.0,\n", - " 'x_start': 157.0,\n", - " 'z_end': 6.427354838550462,\n", - " 'z_mean': 5.4360212510513115,\n", - " 'z_start': 4.223480595415142},\n", - " { 'slope': 0.34782985777944603,\n", - " 'x_end': 193.0,\n", - " 'x_start': 177.0,\n", - " 'z_end': 1.3683244048034364,\n", - " 'z_mean': 3.876447007928749,\n", - " 'z_start': 6.295019014555152},\n", - " { 'slope': 0.06019958288565148,\n", - " 'type': 'foreshore',\n", - " 'x_end': 213.5,\n", - " 'x_start': 193.0,\n", - " 'z_end': 0.06618164722605435,\n", - " 'z_mean': 0.6722026822899186,\n", - " 'z_start': 1.1980470061124415}]\n" - ] - } - ], - "source": [ - "from scipy.signal import savgol_filter\n", - "import re\n", - "from scipy.stats import linregress\n", - "import warnings\n", - "warnings.simplefilter(action='ignore', category=FutureWarning)\n", - "\n", - "def get_breakpoints(model, min_distance=20):\n", - " # Get breakpoints\n", - " breakpoints = []\n", - " for line in model.summary().split('\\n'):\n", - " # Get unpruned lines\n", - " if 'No' in line and 'None' not in line:\n", - " # Get break points\n", - " m = re.search(\"h\\(x0-(\\d*\\.?\\d+)\\)\", line)\n", - " if m:\n", - " breakpoints.append(float(m.groups()[0]))\n", - " m = re.search(\"h\\((\\d*\\.?\\d+)-x0\\)\", line)\n", - " if m:\n", - " breakpoints.append(float(m.groups()[0]))\n", - " return sorted(list(set(breakpoints)))\n", - " \n", - "def get_segments(breakpoints, x_min, x_max):\n", - " segments = []\n", - " breakpoints = [x_min] + breakpoints + [x_max]\n", - "\n", - " for x1, x2 in zip(breakpoints, breakpoints[1:]):\n", - " segments.append({\n", - " 'x_start': x1,\n", - " 'x_end': x2\n", - " })\n", - " return segments \n", - "\n", - "def get_segment_slopes(segments, x, z):\n", - " for segment in segments:\n", - " mask = ma.masked_where((segment['x_start'] < x) & (x < segment['x_end']),x ).mask\n", - " segment['z_mean'] = np.mean(z[mask])\n", - " segment['z_start'] = np.mean(z[mask][0])\n", - " segment['z_end'] = np.mean(z[mask][-1])\n", - " segment['slope'] = -linregress(x[mask], z[mask]).slope\n", - " return segments\n", - " \n", - "def classify_segments(segments, x,z):\n", - " \n", - " # Most seaward slope must be foreshore\n", - " segments[-1]['type'] = 'foreshore'\n", - " \n", - " # Most landward slope must be land\n", - " segments[0]['type'] = 'land'\n", - " \n", - " # Segments with really high slopes must be structures\n", - " for seg in segments:\n", - " if seg['slope'] > 2.0:\n", - " seg['type'] = 'structure'\n", - " \n", - " # Segments with large change of slope and \n", - " # Segment with max slope should be dune face\n", - "# dune_face_idx = [n for n, seg in enumerate(segments) if seg['slope']==max(x['slope'] for x in segments)][0]\n", - "# segments[dune_face_idx]['type'] = 'dune_face'\n", - " \n", - " # Pick out berms \n", - " for seg in segments:\n", - " if (-0.03 < seg['slope'] < 0.03 # berms should be relatively flat\n", - " and 0 < seg['z_mean'] < 4 # berms should be located between 0-4 m AHD\n", - " ): # berms should be seaward of dune face\n", - " seg['type'] = 'berm'\n", - " \n", - "# slope = None\n", - "# for seg in reversed(segments):\n", - "# if slope is None:\n", - "# continue\n", - "# elif slope - 0.03 < seg['slope'] < slope + 0.03:\n", - "# seg['type'] = 'foreshore'\n", - "# else:\n", - "# break\n", - " \n", - " return segments\n", - "\n", - "def get_piecewise_linear_model(x,z):\n", - " #Fit an Earth model\n", - " model = Earth(penalty=3,thresh=0.0005)\n", - " model.fit(x,z)\n", - " return model\n", - "\n", - "def plot_profile_classification(site_id, profile_type):\n", - " df_profile = df_profiles.query(\"site_id == '{}' and profile_type == '{}'\".format(site_id, profile_type))\n", - " x = np.array(df_profile.index.get_level_values('x').tolist())\n", - " z = np.array(df_profile.z.tolist()) \n", - " \n", - " nan_mask = ma.masked_invalid(z).mask\n", - " x = x[~nan_mask]\n", - " z_unfiltered = z[~nan_mask]\n", - " z = savgol_filter(z_unfiltered, 51, 3)\n", - " \n", - " model = get_piecewise_linear_model(x,z)\n", - " breakpoints = get_breakpoints(model)\n", - " segments = get_segments(breakpoints, x_min=x.min(), x_max=x.max())\n", - " segments = get_segment_slopes(segments, x=x, z=z)\n", - "# segments = merge_similar_segments(segments)\n", - " segments = classify_segments(segments, x=x, z=z)\n", - " \n", - " pyplot.figure()\n", - " pyplot.plot(x,z_unfiltered, color='0.5',marker='.', alpha=.2, ms=10,linestyle=\"None\")\n", - "\n", - " # Plot different segments\n", - " foreshore_segments = [x for x in segments if x.get('type') == 'foreshore']\n", - " for seg in foreshore_segments:\n", - " pyplot.plot([seg['x_start'], seg['x_end']],\n", - " [seg['z_start'], seg['z_end']],\n", - " linewidth=4, \n", - " color='b')\n", - "\n", - " land_segments = [x for x in segments if x.get('type') == 'land']\n", - " for seg in land_segments:\n", - " pyplot.plot([seg['x_start'], seg['x_end']],\n", - " [seg['z_start'], seg['z_end']],\n", - " linewidth=4, \n", - " color='g')\n", - "\n", - " berm_segments = [x for x in segments if x.get('type') == 'berm']\n", - " for seg in berm_segments:\n", - " pyplot.plot([seg['x_start'], seg['x_end']],\n", - " [seg['z_start'], seg['z_end']],\n", - " linewidth=4, \n", - " color='y')\n", - "\n", - " dune_face_segments = [x for x in segments if x.get('type') == 'dune_face']\n", - " for seg in dune_face_segments:\n", - " pyplot.plot([seg['x_start'], seg['x_end']],\n", - " [seg['z_start'], seg['z_end']],\n", - " linewidth=4, \n", - " color='r')\n", - " \n", - " structure_segments = [x for x in segments if x.get('type') == 'structure']\n", - " for seg in structure_segments:\n", - " pyplot.plot([seg['x_start'], seg['x_end']],\n", - " [seg['z_start'], seg['z_end']],\n", - " linewidth=4, \n", - " color='m')\n", - " \n", - " unclassified_segments = [x for x in segments if x.get('type') is None]\n", - " for seg in unclassified_segments:\n", - " pyplot.plot([seg['x_start'], seg['x_end']],\n", - " [seg['z_start'], seg['z_end']],\n", - " linewidth=4, \n", - " color='0.4')\n", - "\n", - " pyplot.xlabel('x (m)')\n", - " pyplot.ylabel('z (m AHD)')\n", - " pyplot.title('{} profile at {}'.format(profile_type, site_id))\n", - " pyplot.show()\n", - "\n", - " import pprint\n", - " pp = pprint.PrettyPrinter(indent=4)\n", - " pp.pprint(segments)\n", - "\n", - "plot_profile_classification('NARRA0018', 'prestorm')\n", - "plot_profile_classification('NARRA0019', 'prestorm')\n", - "plot_profile_classification('CRESn0017', 'poststorm')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "heading_collapsed": true - }, - "source": [ - "## Try lmfit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "hidden": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-05T01:13:41.773484Z", - "start_time": "2018-12-05T01:13:27.230746Z" - }, - "code_folding": [ - 0 - ], - "hidden": true, - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[Model]]\n", - " Model(piecewise_linear)\n", - "[[Fit Statistics]]\n", - " # fitting method = ampgo, with L-BFGS-B as local solver\n", - " # function evals = 79866\n", - " # data points = 491\n", - " # variables = 8\n", - " chi-square = 65.4379204\n", - " reduced chi-square = 0.13548224\n", - " Akaike info crit = -973.533027\n", - " Bayesian info crit = -939.961474\n", - "[[Variables]]\n", - " x0: 62.1499306 (init = 40.5)\n", - " x1: 181.207436 (init = 40.5)\n", - " x2: 196.301872 (init = 40.5)\n", - " b: 5.81709441 (init = 0)\n", - " k1: 0.06544372 (init = 0)\n", - " k2: -0.07421739 (init = 0)\n", - " k3: -0.32199700 (init = 0)\n", - " k4: 0.29325377 (init = 0)\n", - "\n" - ] - } - ], - "source": [ - "from lmfit import Model, Parameters\n", - "\n", - "def get_data():\n", - " site_id='NARRA0018'\n", - " profile_type='prestorm'\n", - " df_profile = df_profiles.query(\"site_id == '{}' and profile_type == '{}'\".format(site_id, profile_type))\n", - " x = np.array(df_profile.index.get_level_values('x').tolist())\n", - " z = np.array(df_profile.z.tolist()) \n", - "\n", - " nan_mask = ma.masked_invalid(z).mask\n", - " x = x[~nan_mask]\n", - " z = z[~nan_mask]\n", - " return x,z\n", - "\n", - "# def piecewise_linear(x, x0, x1, b, k1, k2, k3):\n", - "# condlist = [x < x0, (x >= x0) & (x < x1), x >= x1]\n", - "# funclist = [lambda x: k1*x + b, lambda x: k1*x + b + k2*(x-x0), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1)]\n", - "# return np.piecewise(x, condlist, funclist)\n", - "\n", - "# x,z = get_data()\n", - "\n", - "# fmodel = Model(piecewise_linear)\n", - "# params = Parameters()\n", - "# params.add('x0', value=0, vary=True, min=min(x), max=max(x))\n", - "# params.add('x1', value=0, vary=True, min=min(x), max=max(x))\n", - "# params.add('b', value=0, vary=True)\n", - "# params.add('k1', value=0, vary=True, min=-0.01, max=0.01)\n", - "# params.add('k2', value=0, vary=True, min=-0.1, max=-0.5)\n", - "# params.add('k3', value=0, vary=True, min=0.1, max=0.5)\n", - "\n", - "def piecewise_linear(x, x0, x1, x2, b, k1, k2, k3,k4):\n", - " condlist = [x < x0, (x >= x0) & (x < x1), (x >= x1) & (x < x2), x >= x2]\n", - " funclist = [lambda x: k1*x + b, lambda x: k1*x + b + k2*(x-x0), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1) +k4*(x-x2)]\n", - " return np.piecewise(x, condlist, funclist)\n", - "\n", - "x,z = get_data()\n", - "\n", - "fmodel = Model(piecewise_linear)\n", - "params = Parameters()\n", - "params.add('x0', value=0, vary=True, min=min(x), max=max(x))\n", - "params.add('x1', value=0, vary=True, min=min(x), max=max(x))\n", - "params.add('x2', value=0, vary=True, min=min(x), max=max(x))\n", - "params.add('b', value=0, vary=True)\n", - "params.add('k1', value=0, vary=True, min=-0.5, max=0.5)\n", - "params.add('k2', value=0, vary=True, min=-0.5, max=0.5)\n", - "params.add('k3', value=0, vary=True, min=-0.5, max=0.5)\n", - "params.add('k4', value=0, vary=True, min=-0.5, max=0.5)\n", - "\n", - "\n", - "result = fmodel.fit(z, params, x=x,method='ampgo')\n", - "\n", - "\n", - "pyplot.figure()\n", - "pyplot.plot(x,z, color='0.5',marker='.', alpha=.2, ms=10,linestyle=\"None\")\n", - "pyplot.plot(x,result.best_fit, color='r')\n", - "pyplot.show()\n", - "print(result.fit_report())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Try spline" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-05T01:49:30.968871Z", - "start_time": "2018-12-05T01:49:30.648994Z" - }, - "code_folding": [ - 2 - ] - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from scipy.signal import savgol_filter\n", - "\n", - "def get_data():\n", - " site_id='NARRA0018'\n", - " profile_type='prestorm'\n", - " df_profile = df_profiles.query(\"site_id == '{}' and profile_type == '{}'\".format(site_id, profile_type))\n", - " x = np.array(df_profile.index.get_level_values('x').tolist())\n", - " z = np.array(df_profile.z.tolist()) \n", - "\n", - " nan_mask = ma.masked_invalid(z).mask\n", - " x = x[~nan_mask]\n", - " z = z[~nan_mask]\n", - " return x,z\n", - "\n", - "x,z = get_data()\n", - "\n", - "z_filtered = savgol_filter(z, 31, 3)\n", - "\n", - "\n", - "pyplot.figure()\n", - "pyplot.plot(x,z, color='0.5',marker='.', alpha=.2, ms=10,linestyle=\"None\")\n", - "pyplot.plot(x,z_filtered, color='r')\n", - "pyplot.show()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/04a_profile_picker_superseded.ipynb b/notebooks/04a_profile_picker_superseded.ipynb new file mode 100644 index 0000000..bbb228c --- /dev/null +++ b/notebooks/04a_profile_picker_superseded.ipynb @@ -0,0 +1,407 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import os\n", + "import numpy.ma as ma\n", + "\n", + "import numpy\n", + "from pyearth import Earth\n", + "from matplotlib import pyplot\n", + "\n", + "np.random.seed(2017)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def df_from_csv(csv, index_col, data_folder='../data/interim'):\n", + " print('Importing {}'.format(csv))\n", + " return pd.read_csv(os.path.join(data_folder,csv), index_col=index_col)\n", + "\n", + "df_profiles = df_from_csv('profiles.csv', index_col=[0, 1, 2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Try using pyearth" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 5, + 20, + 31, + 40 + ] + }, + "outputs": [], + "source": [ + "from scipy.signal import savgol_filter\n", + "import re\n", + "from scipy.stats import linregress\n", + "import warnings\n", + "warnings.simplefilter(action='ignore', category=FutureWarning)\n", + "\n", + "def get_breakpoints(model, min_distance=20):\n", + " # Get breakpoints\n", + " breakpoints = []\n", + " for line in model.summary().split('\\n'):\n", + " # Get unpruned lines\n", + " if 'No' in line and 'None' not in line:\n", + " # Get break points\n", + " m = re.search(\"h\\(x0-(\\d*\\.?\\d+)\\)\", line)\n", + " if m:\n", + " breakpoints.append(float(m.groups()[0]))\n", + " m = re.search(\"h\\((\\d*\\.?\\d+)-x0\\)\", line)\n", + " if m:\n", + " breakpoints.append(float(m.groups()[0]))\n", + " return sorted(list(set(breakpoints)))\n", + " \n", + "def get_segments(breakpoints, x_min, x_max):\n", + " segments = []\n", + " breakpoints = [x_min] + breakpoints + [x_max]\n", + "\n", + " for x1, x2 in zip(breakpoints, breakpoints[1:]):\n", + " segments.append({\n", + " 'x_start': x1,\n", + " 'x_end': x2\n", + " })\n", + " return segments \n", + "\n", + "def get_segment_slopes(segments, x, z):\n", + " for segment in segments:\n", + " mask = ma.masked_where((segment['x_start'] < x) & (x < segment['x_end']),x ).mask\n", + " segment['z_mean'] = np.mean(z[mask])\n", + " segment['z_start'] = np.mean(z[mask][0])\n", + " segment['z_end'] = np.mean(z[mask][-1])\n", + " segment['slope'] = -linregress(x[mask], z[mask]).slope\n", + " return segments\n", + " \n", + "def classify_segments(segments, x,z):\n", + " \n", + " # Most seaward slope must be foreshore\n", + " segments[-1]['type'] = 'foreshore'\n", + " \n", + " # Most landward slope must be land\n", + " segments[0]['type'] = 'land'\n", + " \n", + " # Segments with really high slopes must be structures\n", + " for seg in segments:\n", + " if seg['slope'] > 2.0:\n", + " seg['type'] = 'structure'\n", + " \n", + " # Segments with large change of slope and \n", + " # Segment with max slope should be dune face\n", + "# dune_face_idx = [n for n, seg in enumerate(segments) if seg['slope']==max(x['slope'] for x in segments)][0]\n", + "# segments[dune_face_idx]['type'] = 'dune_face'\n", + " \n", + " # Pick out berms \n", + " for seg in segments:\n", + " if (-0.03 < seg['slope'] < 0.03 # berms should be relatively flat\n", + " and 0 < seg['z_mean'] < 4 # berms should be located between 0-4 m AHD\n", + " ): # berms should be seaward of dune face\n", + " seg['type'] = 'berm'\n", + " \n", + "# slope = None\n", + "# for seg in reversed(segments):\n", + "# if slope is None:\n", + "# continue\n", + "# elif slope - 0.03 < seg['slope'] < slope + 0.03:\n", + "# seg['type'] = 'foreshore'\n", + "# else:\n", + "# break\n", + " \n", + " return segments\n", + "\n", + "def get_piecewise_linear_model(x,z):\n", + " #Fit an Earth model\n", + " model = Earth(penalty=3,thresh=0.0005)\n", + " model.fit(x,z)\n", + " return model\n", + "\n", + "def plot_profile_classification(site_id, profile_type):\n", + " df_profile = df_profiles.query(\"site_id == '{}' and profile_type == '{}'\".format(site_id, profile_type))\n", + " x = np.array(df_profile.index.get_level_values('x').tolist())\n", + " z = np.array(df_profile.z.tolist()) \n", + " \n", + " nan_mask = ma.masked_invalid(z).mask\n", + " x = x[~nan_mask]\n", + " z_unfiltered = z[~nan_mask]\n", + " z = savgol_filter(z_unfiltered, 51, 3)\n", + " \n", + " model = get_piecewise_linear_model(x,z)\n", + " breakpoints = get_breakpoints(model)\n", + " segments = get_segments(breakpoints, x_min=x.min(), x_max=x.max())\n", + " segments = get_segment_slopes(segments, x=x, z=z)\n", + "# segments = merge_similar_segments(segments)\n", + " segments = classify_segments(segments, x=x, z=z)\n", + " \n", + " pyplot.figure()\n", + " pyplot.plot(x,z_unfiltered, color='0.5',marker='.', alpha=.2, ms=10,linestyle=\"None\")\n", + "\n", + " # Plot different segments\n", + " foreshore_segments = [x for x in segments if x.get('type') == 'foreshore']\n", + " for seg in foreshore_segments:\n", + " pyplot.plot([seg['x_start'], seg['x_end']],\n", + " [seg['z_start'], seg['z_end']],\n", + " linewidth=4, \n", + " color='b')\n", + "\n", + " land_segments = [x for x in segments if x.get('type') == 'land']\n", + " for seg in land_segments:\n", + " pyplot.plot([seg['x_start'], seg['x_end']],\n", + " [seg['z_start'], seg['z_end']],\n", + " linewidth=4, \n", + " color='g')\n", + "\n", + " berm_segments = [x for x in segments if x.get('type') == 'berm']\n", + " for seg in berm_segments:\n", + " pyplot.plot([seg['x_start'], seg['x_end']],\n", + " [seg['z_start'], seg['z_end']],\n", + " linewidth=4, \n", + " color='y')\n", + "\n", + " dune_face_segments = [x for x in segments if x.get('type') == 'dune_face']\n", + " for seg in dune_face_segments:\n", + " pyplot.plot([seg['x_start'], seg['x_end']],\n", + " [seg['z_start'], seg['z_end']],\n", + " linewidth=4, \n", + " color='r')\n", + " \n", + " structure_segments = [x for x in segments if x.get('type') == 'structure']\n", + " for seg in structure_segments:\n", + " pyplot.plot([seg['x_start'], seg['x_end']],\n", + " [seg['z_start'], seg['z_end']],\n", + " linewidth=4, \n", + " color='m')\n", + " \n", + " unclassified_segments = [x for x in segments if x.get('type') is None]\n", + " for seg in unclassified_segments:\n", + " pyplot.plot([seg['x_start'], seg['x_end']],\n", + " [seg['z_start'], seg['z_end']],\n", + " linewidth=4, \n", + " color='0.4')\n", + "\n", + " pyplot.xlabel('x (m)')\n", + " pyplot.ylabel('z (m AHD)')\n", + " pyplot.title('{} profile at {}'.format(profile_type, site_id))\n", + " pyplot.show()\n", + "\n", + " import pprint\n", + " pp = pprint.PrettyPrinter(indent=4)\n", + " pp.pprint(segments)\n", + "\n", + "plot_profile_classification('NARRA0018', 'prestorm')\n", + "plot_profile_classification('NARRA0019', 'prestorm')\n", + "plot_profile_classification('CRESn0017', 'poststorm')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "## Try lmfit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 0 + ], + "hidden": true + }, + "outputs": [], + "source": [ + "from lmfit import Model, Parameters\n", + "\n", + "def get_data():\n", + " site_id='NARRA0018'\n", + " profile_type='prestorm'\n", + " df_profile = df_profiles.query(\"site_id == '{}' and profile_type == '{}'\".format(site_id, profile_type))\n", + " x = np.array(df_profile.index.get_level_values('x').tolist())\n", + " z = np.array(df_profile.z.tolist()) \n", + "\n", + " nan_mask = ma.masked_invalid(z).mask\n", + " x = x[~nan_mask]\n", + " z = z[~nan_mask]\n", + " return x,z\n", + "\n", + "# def piecewise_linear(x, x0, x1, b, k1, k2, k3):\n", + "# condlist = [x < x0, (x >= x0) & (x < x1), x >= x1]\n", + "# funclist = [lambda x: k1*x + b, lambda x: k1*x + b + k2*(x-x0), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1)]\n", + "# return np.piecewise(x, condlist, funclist)\n", + "\n", + "# x,z = get_data()\n", + "\n", + "# fmodel = Model(piecewise_linear)\n", + "# params = Parameters()\n", + "# params.add('x0', value=0, vary=True, min=min(x), max=max(x))\n", + "# params.add('x1', value=0, vary=True, min=min(x), max=max(x))\n", + "# params.add('b', value=0, vary=True)\n", + "# params.add('k1', value=0, vary=True, min=-0.01, max=0.01)\n", + "# params.add('k2', value=0, vary=True, min=-0.1, max=-0.5)\n", + "# params.add('k3', value=0, vary=True, min=0.1, max=0.5)\n", + "\n", + "def piecewise_linear(x, x0, x1, x2, b, k1, k2, k3,k4):\n", + " condlist = [x < x0, (x >= x0) & (x < x1), (x >= x1) & (x < x2), x >= x2]\n", + " funclist = [lambda x: k1*x + b, lambda x: k1*x + b + k2*(x-x0), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1) +k4*(x-x2)]\n", + " return np.piecewise(x, condlist, funclist)\n", + "\n", + "x,z = get_data()\n", + "\n", + "fmodel = Model(piecewise_linear)\n", + "params = Parameters()\n", + "params.add('x0', value=0, vary=True, min=min(x), max=max(x))\n", + "params.add('x1', value=0, vary=True, min=min(x), max=max(x))\n", + "params.add('x2', value=0, vary=True, min=min(x), max=max(x))\n", + "params.add('b', value=0, vary=True)\n", + "params.add('k1', value=0, vary=True, min=-0.5, max=0.5)\n", + "params.add('k2', value=0, vary=True, min=-0.5, max=0.5)\n", + "params.add('k3', value=0, vary=True, min=-0.5, max=0.5)\n", + "params.add('k4', value=0, vary=True, min=-0.5, max=0.5)\n", + "\n", + "\n", + "result = fmodel.fit(z, params, x=x,method='ampgo')\n", + "\n", + "\n", + "pyplot.figure()\n", + "pyplot.plot(x,z, color='0.5',marker='.', alpha=.2, ms=10,linestyle=\"None\")\n", + "pyplot.plot(x,result.best_fit, color='r')\n", + "pyplot.show()\n", + "print(result.fit_report())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Try spline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 2 + ] + }, + "outputs": [], + "source": [ + "from scipy.signal import savgol_filter\n", + "\n", + "def get_data():\n", + " site_id='NARRA0018'\n", + " profile_type='prestorm'\n", + " df_profile = df_profiles.query(\"site_id == '{}' and profile_type == '{}'\".format(site_id, profile_type))\n", + " x = np.array(df_profile.index.get_level_values('x').tolist())\n", + " z = np.array(df_profile.z.tolist()) \n", + "\n", + " nan_mask = ma.masked_invalid(z).mask\n", + " x = x[~nan_mask]\n", + " z = z[~nan_mask]\n", + " return x,z\n", + "\n", + "x,z = get_data()\n", + "\n", + "z_filtered = savgol_filter(z, 31, 3)\n", + "\n", + "\n", + "pyplot.figure()\n", + "pyplot.plot(x,z, color='0.5',marker='.', alpha=.2, ms=10,linestyle=\"None\")\n", + "pyplot.plot(x,z_filtered, color='r')\n", + "pyplot.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/04b_profile_picker.ipynb b/notebooks/04b_profile_picker.ipynb new file mode 100644 index 0000000..160904f --- /dev/null +++ b/notebooks/04b_profile_picker.ipynb @@ -0,0 +1,1374 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Beach profile classifier\n", + "Using RANSAC algorithm" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "## Setup notebook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "# Enable autoreloading of our modules. \n", + "# Most of the code will be located in the /src/ folder, \n", + "# and then called from the notebook.\n", + "%matplotlib inline\n", + "%reload_ext autoreload\n", + "%autoreload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "from IPython.core.debugger import set_trace\n", + "\n", + "import pandas as pd\n", + "import numpy as np\n", + "import os\n", + "import decimal\n", + "import plotly\n", + "import plotly.graph_objs as go\n", + "import plotly.plotly as py\n", + "import plotly.tools as tls\n", + "import plotly.figure_factory as ff\n", + "from plotly import tools\n", + "import plotly.io as pio\n", + "from scipy import stats\n", + "import math\n", + "import matplotlib\n", + "from matplotlib import cm\n", + "import colorlover as cl\n", + "import numpy.ma as ma\n", + "\n", + "from ipywidgets import widgets, Output\n", + "from IPython.display import display, clear_output, Image, HTML\n", + "\n", + "from sklearn.metrics import confusion_matrix\n", + "\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "\n", + "from sklearn import linear_model, datasets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "# Matplot lib default settings\n", + "plt.rcParams[\"figure.figsize\"] = (10,3)\n", + "plt.rcParams['axes.grid']=True\n", + "plt.rcParams['grid.alpha'] = 0.5\n", + "plt.rcParams['grid.color'] = \"grey\"\n", + "plt.rcParams['grid.linestyle'] = \"--\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "## Import data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "def df_from_csv(csv, index_col, data_folder='../data/interim'):\n", + " print('Importing {}'.format(csv))\n", + " return pd.read_csv(os.path.join(data_folder,csv), index_col=index_col)\n", + "\n", + "df_waves = df_from_csv('waves.csv', index_col=[0, 1])\n", + "df_tides = df_from_csv('tides.csv', index_col=[0, 1])\n", + "df_profiles = df_from_csv('profiles.csv', index_col=[0, 1, 2])\n", + "df_sites = df_from_csv('sites.csv', index_col=[0])\n", + "df_profile_features_crest_toes = df_from_csv('profile_features_crest_toes.csv', index_col=[0,1])\n", + "\n", + "# Note that the forecasted data sets should be in the same order for impacts and twls\n", + "impacts = {\n", + " 'forecasted': {\n", + " 'foreshore_slope_sto06': df_from_csv('impacts_forecasted_foreshore_slope_sto06.csv', index_col=[0]),\n", + " 'mean_slope_sto06': df_from_csv('impacts_forecasted_mean_slope_sto06.csv', index_col=[0]),\n", + " },\n", + " 'observed': df_from_csv('impacts_observed.csv', index_col=[0])\n", + " }\n", + "\n", + "\n", + "twls = {\n", + " 'forecasted': {\n", + " 'foreshore_slope_sto06': df_from_csv('twl_foreshore_slope_sto06.csv', index_col=[0, 1]),\n", + " 'mean_slope_sto06':df_from_csv('twl_mean_slope_sto06.csv', index_col=[0, 1]),\n", + " }\n", + "}\n", + "print('Done!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Implement algorithm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get profile data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_profile_data(site, profile_type, df_profiles):\n", + " \"\"\"\n", + " Returns a list of x and z coordinates for a given profile.\n", + " \"\"\"\n", + "\n", + " # Get x and z coorindates of profile\n", + " x = df_profiles.loc[(site, profile_type)].index.values\n", + " z = df_profiles.loc[(site, profile_type)].z.values\n", + "\n", + " # Get land limit\n", + " land_lim = df_profiles.loc[(site, 'poststorm')].land_lim.loc[0]\n", + "\n", + " # Remove nan values\n", + " m = ma.masked_invalid(z)\n", + " x = x[~m.mask]\n", + " z = z[~m.mask]\n", + "\n", + "# # Remove landwards of landlim\n", + "# mask = ma.masked_where(x < land_lim, x)\n", + "# x = x[~mask.mask]\n", + "# z = z[~mask.mask]\n", + "\n", + " return x, z\n", + "\n", + "\n", + "# Load profile data\n", + "# site = 'WAMBE0010'\n", + "# site = 'NINEMs0048'\n", + "# site = 'NAMB0013'\n", + "# site = 'AVOCAn0009'\n", + "# site = 'GRANTSs0014'\n", + "site = 'NARRA0001'\n", + "profile_type = 'prestorm'\n", + "x, z = get_profile_data(site, profile_type, df_profiles)\n", + "\n", + "print('x = {} ...'.format(x[0:5]))\n", + "print('z = {} ...'.format(z[0:5]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hide_input": true + }, + "outputs": [], + "source": [ + "\n", + "\n", + "\n", + "# # n_samples = 1000\n", + "# # n_outliers = 50\n", + "\n", + "\n", + "# # X, y, coef = datasets.make_regression(n_samples=n_samples, n_features=1,\n", + "# # n_informative=1, noise=10,\n", + "# # coef=True, random_state=0)\n", + "\n", + "# # # Add outlier data\n", + "# # np.random.seed(0)\n", + "# # X[:n_outliers] = 3 + 0.5 * np.random.normal(size=(n_outliers, 1))\n", + "# # y[:n_outliers] = -3 + 10 * np.random.normal(size=n_outliers)\n", + "\n", + "# # # Fit line using all data\n", + "# # lr = linear_model.LinearRegression()\n", + "# # lr.fit(X, y)\n", + "\n", + "# # Robustly fit linear model with RANSAC algorithm\n", + "# ransac = linear_model.RANSACRegressor()\n", + "# ransac.fit(x, z)\n", + "# inlier_mask = ransac.inlier_mask_\n", + "# outlier_mask = np.logical_not(inlier_mask)\n", + "\n", + "# # Predict data of estimated models\n", + "# line_x = np.arange(x.min(), x.max())[:, np.newaxis]\n", + "# line_y_ransac = ransac.predict(line_x)\n", + "\n", + "# lw = 2\n", + "# plt.scatter(x[inlier_mask], z[inlier_mask], color='yellowgreen', marker='.',\n", + "# label='Inliers')\n", + "# plt.scatter(x[outlier_mask], z[outlier_mask], color='gold', marker='.',\n", + "# label='Outliers')\n", + "# plt.plot(line_x, line_y_ransac, color='cornflowerblue', linewidth=lw,\n", + "# label='RANSAC regressor')\n", + "# plt.legend(loc='lower right')\n", + "# plt.xlabel(\"Input\")\n", + "# plt.ylabel(\"Response\")\n", + "# plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fit linear interpolated spline to profile\n", + "- Linear interpolated spline used to simplify the profile.\n", + "- Need to do a bit of optimization to calculate the best smoothing parameter for the profile." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 4, + 11, + 20, + 25, + 30, + 42, + 92 + ] + }, + "outputs": [], + "source": [ + "from scipy.interpolate import UnivariateSpline\n", + "from scipy.interpolate import interp1d\n", + "\n", + "\n", + "def pairwise(iterable):\n", + " \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n", + " a, b = itertools.tee(iterable)\n", + " next(b, None)\n", + " return zip(a, b)\n", + "\n", + "\n", + "def xz(x_start, x_end, x, z):\n", + " \"\"\"\n", + " Returns a list of x coordinates and z coorindates which lie between x_start and x_end\n", + " \"\"\"\n", + " x_seg = [val for val in x if x_start <= val <= x_end]\n", + " z_seg = [z_val for x_val, z_val in zip(x, z) if x_start <= x_val <= x_end]\n", + " return x_seg, z_seg\n", + "\n", + "\n", + "def get_slope(x, z):\n", + " slope, intercept, r_value, p_value, std_err = linregress(x, z)\n", + " return slope\n", + "\n", + "\n", + "def get_r_squared(x, z):\n", + " slope, intercept, r_value, p_value, std_err = linregress(x, z)\n", + " return r_value**2\n", + "\n", + "\n", + "def rolling_std(a, w):\n", + " \"\"\"\n", + " Calculates a rolling window standard deviation\n", + " https://stackoverflow.com/a/40773366\n", + " \"\"\"\n", + " a = np.array(a)\n", + " nrows = a.size - w + 1\n", + " n = a.strides[0]\n", + " a2D = np.lib.stride_tricks.as_strided(a, shape=(nrows, w), strides=(n, n))\n", + " return np.std(a2D, 1)\n", + "\n", + "\n", + "def iterate_over_smoothing_params(x, z):\n", + " \"\"\"\n", + " Finds the best smoothing parameter for a linear interpolated spline. \n", + " Checks a number of smoothing factors, and then tries to minimize the \n", + " number of knots (changepoints) while retaining decent match to the \n", + " original profile.\n", + " \"\"\"\n", + "\n", + " # Store the results of our iterations in a list of dictionaries\n", + " results = []\n", + "\n", + " # Iterate through different value of smoothing parameters\n", + " s_vals = [x for x in np.arange(0, 10, 0.01)]\n", + "\n", + " r2_vals = []\n", + " n_knots = []\n", + " s_vals_to_keep = []\n", + "\n", + " # Iterate backwards just to make further calcultions more easier.\n", + " for s_val in s_vals[::-1]:\n", + "\n", + " # Fit spline to profile using the s_val for this iteration\n", + " # k=1 is used to force linear line segments between knots\n", + " s = UnivariateSpline(x, z, s=s_val, k=1)\n", + " xs = np.linspace(x[0], x[-1], 1000)\n", + " zs = s(xs)\n", + "\n", + " # Find knots\n", + " x_knots = s.get_knots()\n", + " z_knots = s(x_knots)\n", + "\n", + " # Get number of knots\n", + " n = len(s.get_knots())\n", + "\n", + " # If we've already recorded data about this number of knots, just skip.\n", + " # Need to also, only have increasing results for spline interpolation\n", + " if n in [x['n'] for x in results\n", + " ] or n < max([x['n'] for x in results], default=0):\n", + " continue\n", + "\n", + " # Get r2, how well does the simplification fit the original profile?\n", + " # Create interp model from the knots at our original points.\n", + " f = interp1d(x_knots, z_knots)\n", + " z_lin = f(x)\n", + " r2 = get_r_squared(z, z_lin)\n", + "\n", + " results.append({'n': n, 's': s_val, 'r2': r2})\n", + " return results\n", + "\n", + "\n", + "def find_best_smoothing_param(results, min_std=0.0002):\n", + " \"\"\"\n", + " Given a list of smoothing parameters and their fits, determine what the best smoothing parameter is.\n", + " Larger min_std = more smoothing\n", + " \"\"\"\n", + "\n", + " # Get the 2nd derivate of the n_knots vs r2_vals.\n", + " y_spl = UnivariateSpline([x['n'] for x in results],\n", + " [x['r2'] for x in results],\n", + " s=0)\n", + " y_spl_2d = y_spl.derivative(n=2)\n", + " y_spl_2d_vals = y_spl_2d([x['n'] for x in results])\n", + "\n", + " # Get a rolling standard deviation of the derivative\n", + " std = rolling_std(y_spl_2d_vals, w=3)\n", + "\n", + " # Best smoothing parameter will be when the standard deviation stops changing\n", + " best_i = np.argmax(std < min_std)\n", + " best_s = results[best_i]['s']\n", + " return best_s\n", + "\n", + "\n", + "def simplify_profile(x, z, site):\n", + " results = iterate_over_smoothing_params(x, z)\n", + " best_s = find_best_smoothing_param(results)\n", + "\n", + " # Fit spline to profile\n", + " s = UnivariateSpline(x, z, s=best_s, k=1)\n", + " xs = np.linspace(x[0], x[-1], 1000)\n", + " zs = s(xs)\n", + "\n", + " # Find knots\n", + " x_knots = s.get_knots()\n", + " z_knots = s(x_knots)\n", + "\n", + " # Plot for checking\n", + " plt.title('{}: Linear spline simplification'.format(site))\n", + " plt.xlabel('Distance (m)')\n", + " plt.ylabel('Elevation (m AHD)')\n", + " plt.plot(x, z, 'k.-', label='Raw profile')\n", + " plt.plot(\n", + " xs, zs, 'r-', label='Interpolated profile (s={:.2f})'.format(best_s))\n", + " plt.plot(\n", + " x_knots,\n", + " z_knots,\n", + " 'ro',\n", + " markersize=8,\n", + " markeredgewidth=1.5,\n", + " markeredgecolor='orange',\n", + " label='Interpolated knots (n={})'.format(len(x_knots)))\n", + " plt.legend(loc='best')\n", + "# plt.show()\n", + "\n", + " return x_knots, z_knots, best_s\n", + "\n", + "\n", + "x_knots, z_knots, best_s = simplify_profile(x, z, site)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 0 + ] + }, + "outputs": [], + "source": [ + "def get_x_z_from_knots(x_knots, z_knots):\n", + " # Fit spline to profile\n", + " s = UnivariateSpline(x_knots, z_knots, s=0, k=1)\n", + " xs = np.linspace(x_knots[0], x_knots[-1], 1000)\n", + " zs = s(xs)\n", + " return xs, zs\n", + "\n", + "def simplify_segments_with_same_slopes(x_knots, x, z, site, best_s):\n", + " \"\"\"\n", + " Removes knots which have similar slopes on either side. \n", + " This is an extra simplifcation as the linear splines may still include\n", + " some nodes that aren't required.\n", + " \"\"\"\n", + " removed_x_knots = []\n", + " while True:\n", + " for x_knot1, x_knot2, x_knot3 in zip(x_knots[0:-2], x_knots[1:-1], x_knots[2:]):\n", + "\n", + " # Get slope of segment behind point at x_knot2\n", + " x_seg, z_seg = xz(x_knot1, x_knot2, x, z)\n", + " slope1 = get_slope(x_seg, z_seg)\n", + "\n", + " # Get slope of line in front of point at x_knot2\n", + " x_seg, z_seg = xz(x_knot2, x_knot3, x, z)\n", + " slope2 = get_slope(x_seg, z_seg)\n", + "\n", + " slope_diff = abs(slope1 - slope2)\n", + "\n", + " # Also check if points are too close together\n", + " if x_knot3 - x_knot1 < 3: # m\n", + " too_close = True\n", + " else:\n", + " too_close = False\n", + " \n", + " # Good, the slopes are different on each side, keep checking\n", + " if slope_diff > 0.01 and too_close == False:\n", + " continue\n", + " # Else bad, slopes are the same so we have to remove this point\n", + " else:\n", + " print('Knot at x={} removed (slope_diff={:.3f})'.format(\n", + " x_knot2, slope_diff))\n", + " x_knots = np.delete(x_knots, np.where(x_knots == x_knot2), axis=0)\n", + " removed_x_knots.append(x_knot2)\n", + " break\n", + "\n", + " else:\n", + " print(\"All segments have different slopes\")\n", + " x_knots_simplified = x_knots\n", + " z_knots_simplified = [\n", + " z_val for x_val, z_val in zip(x, z) if x_val in x_knots_simplified\n", + " ]\n", + " break\n", + " \n", + " # Find z location of our removed knots\n", + " removed_z_knots = [\n", + " z_val for x_val, z_val in zip(x, z) if x_val in removed_x_knots\n", + " ]\n", + " \n", + " # Get the new interpolated spline from our simplified knots\n", + " xs, zs = get_x_z_from_knots(x_knots_simplified, z_knots_simplified)\n", + " \n", + " # Plot for checking\n", + "# plt.title('{}: Linear spline simplification by comparing slopes'.format(site))\n", + "# plt.xlabel('Distance (m)')\n", + "# plt.ylabel('Elevation (m AHD)')\n", + "# plt.plot(x, z, 'k.-', label='Raw profile')\n", + "# plt.plot(\n", + "# xs, zs, 'r-', label='Interpolated profile (s={:.2f})'.format(best_s))\n", + "# plt.plot(\n", + "# x_knots_simplified,\n", + "# z_knots_simplified,\n", + "# 'ro',\n", + "# markersize=8,\n", + "# markeredgewidth=1.5,\n", + "# markeredgecolor='orange',\n", + "# label='Interpolated knots (n={})'.format(len(x_knots)))\n", + "# plt.plot(\n", + "# removed_x_knots,\n", + "# removed_z_knots,\n", + "# 'ro',\n", + "# markersize=10,\n", + "# markeredgewidth=2.5,\n", + "# markerfacecolor=\"None\",\n", + "# markeredgecolor='orange',\n", + "# label='Removed knots (n={})'.format(len(removed_x_knots)))\n", + "# plt.legend(loc='best')\n", + "# plt.show()\n", + "\n", + " return x_knots_simplified,z_knots_simplified\n", + "\n", + "x_knots, z_knots = simplify_segments_with_same_slopes(x_knots, x, z, site, best_s)\n", + "xs, zs = get_x_z_from_knots(x_knots, z_knots)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 1 + ] + }, + "outputs": [], + "source": [ + "## BACKUP\n", + "def find_crest_and_toes(x,z,x_knots,best_s):\n", + "\n", + " # Get a cubic spline so we can have continuous second order deriviates for our profile\n", + "# spl = interpolate.splrep(x, z, k=4,t=x_knots[1:-1])\n", + " spl = interpolate.splrep(x, z, k=3,s=best_s)\n", + "\n", + " xx = np.linspace(x[0], x[-1], 1000)\n", + " zz = interpolate.splev(xx, spl)\n", + " zz_d1 = interpolate.splev(xx, spl, der=1)\n", + " zz_d2 = interpolate.splev(xx, spl, der=2)\n", + " zz_d3 = interpolate.splev(xx, spl, der=3)\n", + "\n", + " try:\n", + " # Find dune crest by looking at elevation\n", + " peaks_crest, props_crest = find_peaks(zz, height=3, prominence=0.5)\n", + " \n", + " # Save potential crests\n", + " x_potential_crests = xx[peaks_crest]\n", + " z_potential_crests = zz[peaks_crest]\n", + "\n", + " # Take most seaward peak as dune crest\n", + " i_crest = peaks_crest[-1]\n", + " x_crest = xx[i_crest]\n", + " z_crest = zz[i_crest]\n", + "\n", + " print('Found {} potential dune crests from elevation'.format(len(peaks_crest)))\n", + "\n", + " except:\n", + " print('No crests found from elevation')\n", + " x_crest = np.nan\n", + " z_crest = np.nan\n", + " pass\n", + "\n", + " try:\n", + " if np.isnan(x_crest) and np.isnan(x_crest):\n", + " # Find dune crest by looking at 2nd deriviate\n", + " # Multiply by -1 since crest will have a negative 2nd derivative and we want to find peaks (i.e. positive)\n", + " peaks_crest, props_crest = find_peaks(zz_d2*-1, height=0.02,width=0)\n", + " \n", + " \n", + " # Save potential crests\n", + " x_potential_crests = xx[peaks_crest]\n", + " z_potential_crests = zz[peaks_crest]\n", + " print('Found {} potential dune crests from 2nd derivitive'.format(len(peaks_crest)))\n", + "\n", + " # Take peak with biggest height\n", + " i_biggest_crest = np.argmax(props_crest['peak_heights'])\n", + " i_crest = peaks_crest[i_biggest_crest]\n", + "# x_crest = xx[i_crest]\n", + "# z_crest = zz[i_crest]\n", + "\n", + " # Then take the left base of that peak\n", + " x_crest = xx[props_crest['left_bases'][i_biggest_crest]]\n", + " z_crest= zz[props_crest['left_bases'][i_biggest_crest]]\n", + "\n", + "\n", + " except:\n", + " # Take crest as location with maximum elevation\n", + " i_crest = np.argmax(zz)\n", + " x_crest = xx[i_crest]\n", + " z_crest = zz[i_crest]\n", + " print('No crest found, taking maximum elevation as crest')\n", + "\n", + " try:\n", + " # Find due toe\n", + " peaks_toe, props_toe = find_peaks(zz_d2, height=0, prominence=0.02)\n", + "# peaks_toe, props_toe = find_peaks(zz_d2, height=-3, prominence=0.00)\n", + "\n", + " # Save potential crests\n", + " x_potential_toes = xx[peaks_toe]\n", + " z_potential_toes = zz[peaks_toe]\n", + "\n", + " # Remove toes which are behind dune crest\n", + " heights_toe = props_toe['peak_heights'][peaks_toe > i_crest]\n", + " peaks_toe = peaks_toe[peaks_toe > i_crest]\n", + "\n", + " # Remove toes that are less than 0.5m in height from dune crest\n", + "\n", + " mask = z_crest-0.5>zz[peaks_toe]\n", + " heights_toe = heights_toe[mask]\n", + " peaks_toe = peaks_toe[mask]\n", + "\n", + " # Take toe with biggest 2nd derivative height\n", + " i_toe = peaks_toe[np.argmax(heights_toe)]\n", + " x_toe = xx[i_toe]\n", + " z_toe = zz[i_toe]\n", + " print('Found {} potential dune toes from 2nd derivitive'.format(len(peaks_toe)))\n", + "\n", + "# # The above method usually results in a toe value which is slightly too high up.\n", + "# # We can improve this by moving the toe seaward to the next negative peak in the third\n", + "# # derivative of elevation\n", + "# peaks_toe_d3, props_toe_d3 = find_peaks(zz_d3*-1, height=0.03, prominence=0.02)\n", + "\n", + "# # Filter everything landward of the previous toe position, then take the first (most landward) peak\n", + "# mask = xx[peaks_toe_d3] > x_toe\n", + "# peaks_toe_d3 = peaks_toe_d3[mask]\n", + "\n", + "# if peaks_toe_d3.size != 0:\n", + "# print('Adjusting toe position based on d3')\n", + "# x_toe = xx[peaks_toe_d3[0]]\n", + "# z_toe = zz[peaks_toe_d3[0]]\n", + "\n", + "# # Move toe forward to based on the third derivative of elevation\n", + "# print('Adjusting toe position based on d3')\n", + "# mask = (zz_d3 > -0.0008) & (xx > x_toe) & (np.r_[np.diff(zz_d3)[0],np.diff(zz_d3)] > 0)\n", + "# x_toe = xx[mask][1]\n", + "# z_toe = zz[mask][1]\n", + "\n", + " except:\n", + " x_toe = np.nan\n", + " z_toe = np.nan\n", + " print('No toe found')\n", + " \n", + " return xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 126 + ] + }, + "outputs": [], + "source": [ + "def find_crest_and_toes(x,z,x_knots,best_s):\n", + "\n", + " # Get a cubic spline so we can have continuous second order deriviates for our profile\n", + "# spl = interpolate.splrep(x, z, k=4,t=x_knots[1:-1])\n", + " spl = interpolate.splrep(x, z, k=3,s=best_s)\n", + "\n", + " xx = np.linspace(x[0], x[-1], 1000)\n", + " zz = interpolate.splev(xx, spl)\n", + " zz_d1 = interpolate.splev(xx, spl, der=1)\n", + " zz_d2 = interpolate.splev(xx, spl, der=2)\n", + " zz_d3 = interpolate.splev(xx, spl, der=3)\n", + "\n", + " # Find the biggest slopes\n", + " peaks, props = find_peaks(zz_d1*-1, height=0.15, width=0,prominence=0.1)\n", + "\n", + " if len(peaks) != 0:\n", + "\n", + " x_potential_crests =[xx[val] for val in props['left_bases']]\n", + " x_potential_toes =[xx[val] for val in props['right_bases']]\n", + " x_potential_heights = [val for val in props['peak_heights']]\n", + " x_potential_face_center = xx[peaks]\n", + "\n", + " # Put a limit on how when a slope ends.\n", + " # Dune faces has to be steeper than this value\n", + "# min_slope = -0.10\n", + "\n", + " x_new_potential_crests = []\n", + " x_new_potential_toes = []\n", + " z_new_potential_crests = []\n", + " z_new_potential_toes = []\n", + " dune_heights = []\n", + " slopes = []\n", + "\n", + " for x_crest, x_center, x_toe,height in zip(x_potential_crests, x_potential_face_center, x_potential_toes,x_potential_heights):\n", + " print('Checking x_crest={:.2f}m,x_center={:.2f}m,x_toe={:.2f}m'.format(x_crest, x_center, x_toe))\n", + "\n", + " min_slope = height/-6\n", + " # Check the land side of the dune face\n", + " land_mask = ((x_crest<=xx)&(xx<=x_center))\n", + " land_slope_okay = zz_d1[land_mask] min_dune_ele) & (np.array(dune_heights) > min_dune_height )\n", + "\n", + " slopes = np.array(slopes)[mask]\n", + " dune_heights = np.array(dune_heights)[mask] \n", + " x_potential_crests = np.array(x_new_potential_crests)[mask]\n", + " x_potential_toes = np.array(x_new_potential_toes)[mask]\n", + " z_potential_crests = np.array(z_new_potential_crests)[mask]\n", + " z_potential_toes = np.array(z_new_potential_toes)[mask]\n", + "\n", + "# # Select dune by largest dune height\n", + "# i_dune = np.argmax(dune_heights)\n", + "\n", + " ## TODO Throws error if x_potential_crests is empty\n", + "\n", + " # Select dune by most seaward\n", + " i_dune = np.argmax(x_potential_crests)\n", + "\n", + "\n", + " \n", + "# # Select dune by biggest slope\n", + "# i_dune =np.argmax(slopes*-1)\n", + " x_crest = x_potential_crests[i_dune]\n", + " x_toe = x_potential_toes[i_dune]\n", + " z_crest = z_potential_crests[i_dune]\n", + " z_toe = z_potential_toes[i_dune]\n", + "\n", + " # try:\n", + " # # Find dune crest by looking at elevation\n", + " # peaks_crest, props_crest = find_peaks(zz, height=3, prominence=0.5)\n", + "\n", + " # # Save potential crests\n", + " # x_potential_crests = xx[peaks_crest]\n", + " # z_potential_crests = zz[peaks_crest]\n", + "\n", + " # # Take most seaward peak as dune crest\n", + " # i_crest = peaks_crest[-1]\n", + " # x_crest = xx[i_crest]\n", + " # z_crest = zz[i_crest]\n", + "\n", + " # print('Found {} potential dune crests from elevation'.format(len(peaks_crest)))\n", + "\n", + " # except:\n", + " # print('No crests found from elevation')\n", + " # x_crest = np.nan\n", + " # z_crest = np.nan\n", + " # pass\n", + "\n", + " # try:\n", + " # if np.isnan(x_crest) and np.isnan(x_crest):\n", + " # # Find dune crest by looking at 2nd deriviate\n", + " # # Multiply by -1 since crest will have a negative 2nd derivative and we want to find peaks (i.e. positive)\n", + " # peaks_crest, props_crest = find_peaks(zz_d2*-1, height=0.02,width=0)\n", + "\n", + "\n", + " # # Save potential crests\n", + " # x_potential_crests = xx[peaks_crest]\n", + " # z_potential_crests = zz[peaks_crest]\n", + " # print('Found {} potential dune crests from 2nd derivitive'.format(len(peaks_crest)))\n", + "\n", + " # # Take peak with biggest height\n", + " # i_biggest_crest = np.argmax(props_crest['peak_heights'])\n", + " # i_crest = peaks_crest[i_biggest_crest]\n", + " # # x_crest = xx[i_crest]\n", + " # # z_crest = zz[i_crest]\n", + "\n", + " # # Then take the left base of that peak\n", + " # x_crest = xx[props_crest['left_bases'][i_biggest_crest]]\n", + " # z_crest= zz[props_crest['left_bases'][i_biggest_crest]]\n", + "\n", + "\n", + " # except:\n", + " # # Take crest as location with maximum elevation\n", + " # i_crest = np.argmax(zz)\n", + " # x_crest = xx[i_crest]\n", + " # z_crest = zz[i_crest]\n", + " # print('No crest found, taking maximum elevation as crest')\n", + "\n", + " # try:\n", + " # # Find due toe\n", + " # peaks_toe, props_toe = find_peaks(zz_d2, height=0, prominence=0.02)\n", + " # # peaks_toe, props_toe = find_peaks(zz_d2, height=-3, prominence=0.00)\n", + "\n", + " # # Save potential crests\n", + " # x_potential_toes = xx[peaks_toe]\n", + " # z_potential_toes = zz[peaks_toe]\n", + "\n", + " # # Remove toes which are behind dune crest\n", + " # heights_toe = props_toe['peak_heights'][peaks_toe > i_crest]\n", + " # peaks_toe = peaks_toe[peaks_toe > i_crest]\n", + "\n", + " # # Remove toes that are less than 0.5m in height from dune crest\n", + "\n", + " # mask = z_crest-0.5>zz[peaks_toe]\n", + " # heights_toe = heights_toe[mask]\n", + " # peaks_toe = peaks_toe[mask]\n", + "\n", + " # # Take toe with biggest 2nd derivative height\n", + " # i_toe = peaks_toe[np.argmax(heights_toe)]\n", + " # x_toe = xx[i_toe]\n", + " # z_toe = zz[i_toe]\n", + " # print('Found {} potential dune toes from 2nd derivitive'.format(len(peaks_toe)))\n", + "\n", + " # # # The above method usually results in a toe value which is slightly too high up.\n", + " # # # We can improve this by moving the toe seaward to the next negative peak in the third\n", + " # # # derivative of elevation\n", + " # # peaks_toe_d3, props_toe_d3 = find_peaks(zz_d3*-1, height=0.03, prominence=0.02)\n", + "\n", + " # # # Filter everything landward of the previous toe position, then take the first (most landward) peak\n", + " # # mask = xx[peaks_toe_d3] > x_toe\n", + " # # peaks_toe_d3 = peaks_toe_d3[mask]\n", + "\n", + " # # if peaks_toe_d3.size != 0:\n", + " # # print('Adjusting toe position based on d3')\n", + " # # x_toe = xx[peaks_toe_d3[0]]\n", + " # # z_toe = zz[peaks_toe_d3[0]]\n", + "\n", + " # # # Move toe forward to based on the third derivative of elevation\n", + " # # print('Adjusting toe position based on d3')\n", + " # # mask = (zz_d3 > -0.0008) & (xx > x_toe) & (np.r_[np.diff(zz_d3)[0],np.diff(zz_d3)] > 0)\n", + " # # x_toe = xx[mask][1]\n", + " # # z_toe = zz[mask][1]\n", + "\n", + " # except:\n", + " # x_toe = np.nan\n", + " # z_toe = np.nan\n", + " # print('No toe found')\n", + "\n", + " \n", + " else:\n", + " # Take crest as location with maximum elevation\n", + " i_crest = np.argmax(zz)\n", + " x_crest = xx[i_crest]\n", + " z_crest = zz[i_crest]\n", + " x_potential_crests = np.nan\n", + " z_potential_crests = np.nan\n", + " print('No crest found, taking maximum elevation as crest')\n", + " \n", + " x_toe = np.nan\n", + " z_toe = np.nan\n", + " x_potential_toes=np.nan\n", + " z_potential_toes=np.nan\n", + " \n", + " # Check if toe is necessary. If slope of the dune face is similar to the median slope seaward of the dune\n", + " # toe, let's remove the dune toe\n", + " try:\n", + " if np.isnan(x_toe) == False:\n", + " dune_slope =(z_crest - z_toe) / (x_crest - x_toe)\n", + " median_foreshore_slope = np.median(zz_d1[xx > x_toe])\n", + " std_foreshore_slope = np.std(zz_d1[xx>x_toe])\n", + " print('foreshore_std:{:.4f}'.format(std_foreshore_slope))\n", + " print('foreshore_med_slope:{:.4f}'.format(median_foreshore_slope))\n", + " print('dune_slope:{:.4f}'.format(dune_slope))\n", + " if abs(dune_slope - median_foreshore_slope) < 0.075 and std_foreshore_slope <0.025:\n", + " x_toe = np.nan\n", + " z_toe = np.nan\n", + " except:\n", + " pass\n", + " \n", + " return xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes \n", + " \n", + " \n", + "def plot_profile(site, x,z, xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes):\n", + " plt.figure(figsize=(10,12))\n", + " plt.subplot(411)\n", + " plt.plot(x, z, label='raw')\n", + " plt.plot(xx,zz,label='interpolated')\n", + " plt.plot(x_potential_crests,z_potential_crests,'rv',markersize=10,fillstyle='none',label='Dune crest (potential)')\n", + " plt.plot(x_potential_toes,z_potential_toes,'r^',markersize=10,fillstyle='none',label='Dune toe (potential)')\n", + " plt.plot(x_crest,z_crest,'rv',markersize=10,label='Dune crest (x={:.2f} m, z={:.2f} m)'.format(x_crest,z_crest))\n", + " plt.plot(x_toe,z_toe,'r^',markersize=10,label='Dune toe (x={:.2f} m, z={:.2f} m)'.format(x_toe,z_toe))\n", + " plt.title('{}: elevations'.format(site))\n", + " plt.legend(loc='best')\n", + "\n", + " plt.subplot(412)\n", + " d1 = np.diff(zz) / np.diff(xx)\n", + " plt.plot(xx, zz_d1)\n", + " plt.axvline(x_crest,color='red', label='Dune crest')\n", + " plt.axvline(x_toe,color='red', label='Dune toe')\n", + " # plt.ylim([-0.2,0.2])\n", + " plt.title('first derivative - slope')\n", + "\n", + " plt.subplot(413)\n", + " d2 = np.diff(d1) / np.diff(xx[1:])\n", + " plt.plot(xx, zz_d2)\n", + " plt.axvline(x_crest,color='red', label='Dune crest')\n", + " plt.axvline(x_toe,color='red', label='Dune toe')\n", + " plt.title('second derivative - change in slope')\n", + "# plt.ylim([-0.025,0.025])\n", + "\n", + " plt.subplot(414)\n", + " plt.plot(xx, zz_d3)\n", + " plt.axvline(x_crest,color='red', label='Dune crest')\n", + " plt.axvline(x_toe,color='red', label='Dune toe')\n", + " plt.title('third derivative')\n", + "# plt.ylim([-0.0025,0.0025])\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n", + "\n", + "# xx,zz, x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes = find_crest_and_toes(x,z,x_knots)\n", + "\n", + "# plot_profile(site, x,z, xx,zz, x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [] + }, + "outputs": [], + "source": [ + "def get_crests_and_toes(site, profile_type, df_profiles):\n", + " x, z = get_profile_data(site, profile_type, df_profiles)\n", + " x_knots, z_knots, best_s = simplify_profile(x, z, site)\n", + " x_knots, z_knots = simplify_segments_with_same_slopes(x_knots, x, z, site, best_s)\n", + "# xs, zs = get_x_z_from_knots(x_knots, z_knots)\n", + " xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes = find_crest_and_toes(x,z,x_knots,best_s)\n", + "\n", + " plot_profile(site, x,z, xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes)\n", + "\n", + " \n", + "# site = 'WAMBE0010'\n", + "# site = 'NINEMs0048'\n", + "# site = 'NAMB0013'\n", + "# site = 'AVOCAn0009'\n", + "# site = 'GRANTSs0014'\n", + "site = 'NARRA0001'\n", + "\n", + "import random\n", + "site = random.sample(df_profiles.index.get_level_values('site_id').unique().tolist(), 1)[0]\n", + "\n", + "print(site)\n", + "# site='NSHORE_s0014'\n", + "get_crests_and_toes(site,'prestorm', df_profiles)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find berms \n", + "for x_knot1, x_knot2, in zip(x_knots[0:-2], x_knots[1:-1]):\n", + " \n", + " # Berms are only located seaward of the dune toe (but sometimes dune toe is undefined, so use dune crest)\n", + " if x_knot1 < x_crest:\n", + " continue\n", + " \n", + " # Get slope of segment\n", + " x_seg, z_seg = xz(x_knot1, x_knot2, x, z)\n", + " slope = get_slope(x_seg, z_seg)\n", + " z_mean = np.mean(z_seg)\n", + " width = x_knot2 - x_knot1\n", + " \n", + " if slope > -0.01:\n", + " print('{:.1f}m wide berm at x={:.1f}m to x={:.1f}m at z={:.1f}m'.format(width, x_knot1, x_knot2, z_mean))\n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hide_input": true + }, + "outputs": [], + "source": [ + "# def reject_outliers(data, m = 5):\n", + "# d = np.abs(data - np.median(data))\n", + "# mdev = np.median(d)\n", + "# s = d/mdev if mdev else 0.\n", + "# return ma.masked_where(s > m, data)\n", + "\n", + "# def hampel(vals_orig, k=7, t0=3):\n", + "# '''\n", + "# vals: pandas series of values from which to remove outliers\n", + "# k: size of window (including the sample; 7 is equal to 3 on either side of value)\n", + "# '''\n", + "# #Make copy so original not edited\n", + "# vals_orig = pd.Series(vals_orig)\n", + "# vals=vals_orig.copy() \n", + "# #Hampel Filter\n", + "# L= 1.4826\n", + "# rolling_median=vals.rolling(k).median()\n", + "# difference=np.abs(rolling_median-vals)\n", + "# median_abs_deviation=difference.rolling(k).median()\n", + "# threshold= t0 *L * median_abs_deviation\n", + "# outlier_idx=difference>threshold\n", + "# vals[outlier_idx]=np.nan\n", + "# return(vals.tolist())\n", + "\n", + "# # Remove segements where slopes are too high, indicative of a structure\n", + "# segments = [xz(x1, x2,x,z) for (x1, x2) in pairwise(x_knots_simplified)]\n", + "# slopes = np.array([get_slope(x1,x2) for x1,x2 in segments])\n", + "# mask = reject_outliers(np.array(slopes))\n", + "# x_knots_filtered = x_knots_simplified[np.append(True,~mask.mask)]\n", + "# z_knots_filtered = [z_val for x_val, z_val in zip(x,z) if x_val in x_knots_filtered]\n", + "\n", + "\n", + "\n", + "# # Hampel filter based on z values\n", + "# mask = ma.masked_invalid(hampel(z_knots_filtered))\n", + "# x_knots_filtered = x_knots_filtered[~mask.mask]\n", + "# z_knots_filtered = [z_val for x_val, z_val in zip(x,z) if x_val in x_knots_filtered]\n", + "\n", + "\n", + "\n", + "# # plt.figure(figsize=(10,5))\n", + "# # plt.plot(x_knots_simplified, np.append(slopes[0],slopes), '.-')\n", + "# # plt.plot(x_knots_simplified[np.append(True,~mask.mask)], np.append(slopes[~mask.mask][0], slopes[~mask.mask]), '.-')\n", + "# # # plt.plot(xs, zs)\n", + "# # # plt.plot(x_knots_filtered, z_knots_filtered,'.',markersize=15)\n", + "# # plt.show()\n", + "\n", + "\n", + "\n", + "\n", + "# plt.figure(figsize=(10,5))\n", + "# plt.plot(x, z, '.-')\n", + "# plt.plot(xs, zs)\n", + "# plt.plot(x_knots_filtered, z_knots_filtered,'.',markersize=15)\n", + "# plt.show()\n", + "# z_knots_filtered" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Find dune crests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 18 + ] + }, + "outputs": [], + "source": [ + "from scipy.signal import find_peaks\n", + "\n", + "\n", + "def crest_by_deriv_2(z):\n", + " \"\"\"\n", + " Try get crest elevation by taking the difference in 2nd derivative of elevation.\n", + " \"\"\"\n", + "\n", + " z = savgol_filter(z, 15, 3, mode='nearest')\n", + " deriv_2_diff = np.diff(np.gradient(z)) * -1\n", + "\n", + " # Ensure same size arrays for plotting\n", + " deriv_2_diff = np.insert(deriv_2_diff, 0, deriv_2_diff[0])\n", + " med = np.median(deriv_2_diff)\n", + " std = np.std(deriv_2_diff)\n", + " peaks, peak_props = find_peaks(deriv_2_diff, height=med+std*2, distance=10)\n", + " return peaks, peak_props, deriv_2_diff\n", + "\n", + "\n", + "def crest_by_elevation_peaks(z, min_dune_prominence=1.0):\n", + " # Try find peaks in elevation\n", + " peaks, peak_props = find_peaks(z, distance=10, prominence=min_dune_prominence)\n", + " return peaks, peak_props\n", + "\n", + "\n", + "def get_dune_crest(x, z, xs,zs, x_knots, z_knots, site):\n", + " \n", + " peaks, peak_props = crest_by_elevation_peaks(z)\n", + " \n", + " if peaks.size != 0:\n", + " # Choose crest by most landward peak\n", + " x_crest = x[peaks[-1]]\n", + " z_crest = z[peaks[-1]]\n", + " \n", + " # If no peaks in elevation are found, find by maximum second derivative\n", + " else:\n", + " peaks, peak_props, deriv_2_diff = crest_by_deriv_2(z)\n", + " \n", + " plt.title(\n", + " '{}: Difference of 2nd derivative in elevation'.format(site))\n", + " plt.xlabel('Distance (m)')\n", + " plt.ylabel('Diff(2nd Derivative Elevation)')\n", + " plt.plot(x, deriv_2_diff)\n", + "\n", + " \n", + " if peaks.size!= 0:\n", + "\n", + " # Choose crest by highest peak in diff of 2nd derivative\n", + " i_best = np.argmax(peak_props['peak_heights'])\n", + " x_crest = x[peaks[i_best]]\n", + " z_crest = z[peaks[i_best]]\n", + " plt.plot(\n", + " x[peaks],\n", + " deriv_2_diff[peaks],\n", + " 'o',\n", + " markersize=20,\n", + " fillstyle='none')\n", + " plt.show() \n", + " \n", + " else:\n", + " plt.show() \n", + " # If no peaks in diff of 2nd derivative are found, take landward most point\n", + " if peaks.size == 0:\n", + " x_crest = x_knots[0]\n", + " z_crest = z_knots[0]\n", + "\n", + " # Move crest to closest knot\n", + " idx_crest = min(range(len(x_knots)), key=lambda i: abs(x_knots[i]-x_crest))\n", + " x_crest = x_knots[idx_crest]\n", + " z_crest = z_knots[idx_crest]\n", + " \n", + " # Plot for checking\n", + " plt.title('{}: Find dune crest by peak in diff 2nd deriv'.format(site))\n", + " plt.xlabel('Distance (m)')\n", + " plt.ylabel('Elevation (m AHD)')\n", + " plt.plot(x, z, 'k.-', label='Raw profile')\n", + " plt.plot(\n", + " xs,\n", + " zs,\n", + " 'r-',\n", + " label='Interpolated profile (s={:.2f})'.format(best_s))\n", + " plt.plot(\n", + " x_knots,\n", + " z_knots,\n", + " 'ro',\n", + " markersize=8,\n", + " markeredgewidth=1.5,\n", + " markeredgecolor='orange',\n", + " label='Interpolated knots (n={})'.format(len(x_knots)))\n", + " plt.plot(\n", + " x_crest,\n", + " z_crest,\n", + " 'bv',\n", + " markersize=10,\n", + " label='Dune crest (selected) (x={:.1f}m,z={:.1f}m)'.format(x_crest,z_crest))\n", + " plt.plot(\n", + " x[peaks],\n", + " z[peaks],\n", + " 'o',\n", + " markersize=10,\n", + " fillstyle='none',\n", + " label='Dune crest (potential)')\n", + "\n", + " plt.legend(loc='best')\n", + " plt.show()\n", + " \n", + " return x_crest, z_crest\n", + " \n", + "x_crest, z_crest = get_dune_crest(x, z, xs,zs, x_knots, z_knots, site)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get toe by second derivative\n", + "def toe_by_deriv_2(z):\n", + " \"\"\"\n", + " Try get crest elevation by taking the difference in 2nd derivative of elevation.\n", + " \"\"\"\n", + "\n", + " z = savgol_filter(z,11, 3, mode='nearest')\n", + " deriv_2_diff = np.diff(np.gradient(z))\n", + "\n", + " # Ensure same size arrays for plotting\n", + " deriv_2_diff = np.insert(deriv_2_diff, 0, deriv_2_diff[0])\n", + " med = np.median(deriv_2_diff)\n", + " std = np.std(deriv_2_diff)\n", + " peaks, peak_props = find_peaks(deriv_2_diff, height=max(0.01,med+std*3), distance=10)\n", + " return peaks, peak_props, deriv_2_diff\n", + "\n", + "def get_dune_toe(x, z, xs,zs, x_crest,z_crest,x_knots, z_knots, site):\n", + " peaks, peak_props, deriv_2_diff = toe_by_deriv_2(z)\n", + "\n", + " plt.title('{}: Difference of 2nd derivative in elevation'.format(site))\n", + " plt.xlabel('Distance (m)')\n", + " plt.ylabel('Diff(2nd Derivative Elevation)')\n", + " plt.plot(x, deriv_2_diff)\n", + " \n", + " if peaks.size!= 0:\n", + " \n", + " # Remove peaks which are behind the crest\n", + " # TODO What happens if all peaks are removed?\n", + " mask = ma.masked_where(x[peaks] <= x_crest, x[peaks])\n", + " peaks = peaks[~mask.mask]\n", + "\n", + " if peaks.size!= 0:\n", + " # Choose toe by highest peak in diff of 2nd derivative\n", + " i_best = np.argmax(peak_props['peak_heights'])\n", + " x_toe = x[peaks[i_best]][0]\n", + " z_toe = z[peaks[i_best]][0]\n", + "# # Move crest to closest knot\n", + "# idx_toe = min(range(len(x_knots)), key=lambda i: abs(x_knots[i]-x_toe))\n", + "# x_toe = x_knots[idx_toe]\n", + "# z_toe = z_knots[idx_toe]\n", + "\n", + " plt.plot(\n", + " x[peaks],\n", + " deriv_2_diff[peaks],\n", + " 'o',\n", + " markersize=20,\n", + " fillstyle='none')\n", + " plt.show()\n", + " else:\n", + " x_toe = np.nan\n", + " z_toe = np.nan\n", + " plt.show()\n", + "\n", + " # Plot for checking\n", + " plt.title('{}: Find dune crest by peak in diff 2nd deriv'.format(site))\n", + " plt.xlabel('Distance (m)')\n", + " plt.ylabel('Elevation (m AHD)')\n", + " plt.plot(x, z, 'k.-', label='Raw profile')\n", + " plt.plot(\n", + " xs,\n", + " zs,\n", + " 'r-',\n", + " label='Interpolated profile (s={:.2f})'.format(best_s))\n", + " plt.plot(\n", + " x_knots,\n", + " z_knots,\n", + " 'ro',\n", + " markersize=8,\n", + " markeredgewidth=1.5,\n", + " markeredgecolor='orange',\n", + " label='Interpolated knots (n={})'.format(len(x_knots)))\n", + " plt.plot(\n", + " x_crest,\n", + " z_crest,\n", + " 'bv',\n", + " markersize=10,\n", + " label='Dune crest(x={:.1f} m,z={:.1f} m)'.format(x_crest,z_crest))\n", + " plt.plot(\n", + " x_toe,\n", + " z_toe,\n", + " 'b^',\n", + " markersize=10,\n", + " label='Dune toe (x={:.1f} m,z={:.1f} m)'.format(x_toe,z_toe))\n", + "\n", + " plt.legend(loc='best')\n", + " plt.show()\n", + " return x_toe, z_toe\n", + "x_toe, z_toe = get_dune_toe(x, z, xs,zs, x_crest,z_crest,x_knots, z_knots, site)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def find_dune_crest_and_toe(site, profile_type, df_profiles):\n", + " x, z = get_profile_data(site, profile_type, df_profiles)\n", + " x_knots, z_knots = simplify_profile(x, z, site)\n", + " x_knots, z_knots = simplify_segments_with_same_slopes(x_knots, x, z, site)\n", + " xs, zs = get_x_z_from_knots(x_knots, z_knots)\n", + " x_crest, z_crest = get_dune_crest(x, z, xs,zs, x_knots, z_knots, site)\n", + " x_toe, z_toe = get_dune_toe(x, z, xs,zs, x_crest,z_crest,x_knots, z_knots, site)\n", + " \n", + "find_dune_crest_and_toe('ENTRA0004', 'prestorm', df_profiles)\n" + ] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/05_twl_exceedence.ipynb b/notebooks/05_twl_exceedence.ipynb new file mode 100644 index 0000000..848a527 --- /dev/null +++ b/notebooks/05_twl_exceedence.ipynb @@ -0,0 +1,362 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TWL Exceedance" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup notebook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Enable autoreloading of our modules. \n", + "# Most of the code will be located in the /src/ folder, \n", + "# and then called from the notebook.\n", + "%matplotlib inline\n", + "%reload_ext autoreload\n", + "%autoreload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.core.debugger import set_trace\n", + "\n", + "import pandas as pd\n", + "import numpy as np\n", + "import os\n", + "import decimal\n", + "import plotly\n", + "import plotly.graph_objs as go\n", + "import plotly.plotly as py\n", + "import plotly.tools as tls\n", + "import plotly.figure_factory as ff\n", + "from plotly import tools\n", + "import plotly.io as pio\n", + "from scipy import stats\n", + "import math\n", + "import matplotlib\n", + "from matplotlib import cm\n", + "import colorlover as cl\n", + "\n", + "from ipywidgets import widgets, Output\n", + "from IPython.display import display, clear_output, Image, HTML\n", + "\n", + "from sklearn.metrics import confusion_matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def df_from_csv(csv, index_col, data_folder='../data/interim'):\n", + " print('Importing {}'.format(csv))\n", + " return pd.read_csv(os.path.join(data_folder,csv), index_col=index_col)\n", + "\n", + "df_waves = df_from_csv('waves.csv', index_col=[0, 1])\n", + "df_tides = df_from_csv('tides.csv', index_col=[0, 1])\n", + "df_profiles = df_from_csv('profiles.csv', index_col=[0, 1, 2])\n", + "df_sites = df_from_csv('sites.csv', index_col=[0])\n", + "df_profile_features_crest_toes = df_from_csv('profile_features_crest_toes.csv', index_col=[0,1])\n", + "\n", + "# Note that the forecasted data sets should be in the same order for impacts and twls\n", + "impacts = {\n", + " 'forecasted': {\n", + " 'foreshore_slope_sto06': df_from_csv('impacts_forecasted_foreshore_slope_sto06.csv', index_col=[0]),\n", + " 'mean_slope_sto06': df_from_csv('impacts_forecasted_mean_slope_sto06.csv', index_col=[0]),\n", + " },\n", + " 'observed': df_from_csv('impacts_observed.csv', index_col=[0])\n", + " }\n", + "\n", + "\n", + "twls = {\n", + " 'forecasted': {\n", + " 'foreshore_slope_sto06': df_from_csv('twl_foreshore_slope_sto06.csv', index_col=[0, 1]),\n", + " 'mean_slope_sto06':df_from_csv('twl_mean_slope_sto06.csv', index_col=[0, 1]),\n", + " }\n", + "}\n", + "print('Done!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Calculate vertical distribution of wave count\n", + "For each site, calculate how many waves reached a certain elevation (store as a binned histogram)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Helper functions\n", + "def find_nearest(array, value):\n", + " array = np.asarray(array)\n", + " idx = np.nanargmin((np.abs(array - value)))\n", + " return array[idx], idx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_profile_features_crest_toes.loc[(site_id,'prestorm'),'dune_toe_z']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data = []\n", + "for site_id, df_site_twl in twls['forecasted']['mean_slope_sto06'].groupby('site_id'):\n", + " \n", + " twl_eles_per_wave = []\n", + " \n", + " # Iterate through each timestamp and calculate the number of waves at each interavl.\n", + " # THIS LOOP IS SLOW\n", + " for row in df_site_twl.itertuples():\n", + " \n", + " distribution = stats.norm(loc=row.tide+row.setup, scale=row.S_total/4) # CHECK\n", + "\n", + " # Total number of waves we expect in this period\n", + " n_waves = int(3600 / row.Tp) # Check that we have 1 hour\n", + " \n", + " # Get z elevation of each wave twl in this hour and append to list\n", + " twl_eles_per_wave.extend([distribution.ppf(1-x/n_waves) for x in range(1,n_waves+1)])\n", + " \n", + " # Remove nans and infs # CHECK WHY INF\n", + " twl_eles_per_wave = list(np.asarray(twl_eles_per_wave)[np.isfinite(twl_eles_per_wave)])\n", + " \n", + " # Sort wave twl z elevations in descending list\n", + " twl_eles_per_wave.sort(reverse=True) \n", + " \n", + " # Get index of closest value of dune toe. This is the number of waves that exceeded the the dune toe\n", + " try:\n", + " _, idx = find_nearest(twl_eles_per_wave, dune_toe_z)\n", + " except:\n", + " continue\n", + " \n", + " # Get forecasted and observed impacts\n", + " forecasted_regime = impacts['forecasted']['mean_slope_sto06'].loc[site_id,'storm_regime']\n", + " observed_regime = impacts['observed'].loc[site_id,'storm_regime']\n", + " \n", + " counts, bin_edges = np.histogram(twl_eles_per_wave, bins=100) \n", + " \n", + " data.append({\n", + " 'site_id': site_id,\n", + " 'forecasted_regime': forecasted_regime,\n", + " 'observed_regime': observed_regime,\n", + " 'n_waves_exceeding_dune_toe': idx,\n", + " 'n_waves': [x for x in range(0,500,1)],\n", + " 'truncated_twl_levels': [twl_eles_per_wave[x] for x in range(0,500,1)],\n", + " 'truncated_dune_toe_z': df_profile_features_crest_toes.loc[(site_id,'prestorm'),'dune_toe_z'],\n", + " 'full_counts': counts,\n", + " 'full_bin_edges': bin_edges,\n", + " })\n", + " \n", + " print('Done {}'.format(site_id))\n", + "\n", + "data_twl = data\n", + "# df = pd.DataFrame(data)\n", + "# df = df.set_index('site_id')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "counts, bin_edges = np.histogram (data_twl[0]['twl_levels'], bins=50) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(np.asarray(twl_eles_per_wave)[~np.isfinite(twl_eles_per_wave)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = tools.make_subplots(\n", + " rows=2,\n", + " cols=2,\n", + " specs=[[{}, {}], [{}, {}]],\n", + " subplot_titles=('Swash/Swash', 'Swash/Collision', \n", + " 'Collision/Swash', 'Collision/Collision'),\n", + " shared_xaxes=True, shared_yaxes=True,)\n", + "\n", + "data = []\n", + "for site in data_twl:\n", + " if site['forecasted_regime'] == 'swash' and site[\n", + " 'observed_regime'] == 'swash':\n", + " x_col = 1\n", + " y_col = 1\n", + " elif site['forecasted_regime'] == 'collision' and site[\n", + " 'observed_regime'] == 'collision':\n", + " x_col = 2\n", + " y_col = 2\n", + " elif site['forecasted_regime'] == 'swash' and site[\n", + " 'observed_regime'] == 'collision':\n", + " x_col = 2\n", + " y_col = 1\n", + " elif site['forecasted_regime'] == 'collision' and site[\n", + " 'observed_regime'] == 'swash':\n", + " x_col = 1\n", + " y_col = 2\n", + " else:\n", + " continue\n", + "\n", + " fig.append_trace(\n", + " go.Scattergl(\n", + " x=[x - site['dune_toe_z'] for x in site['twl_levels']],\n", + " y=site['n_waves'],\n", + " name=site['site_id'],\n", + " line = dict(\n", + " color = ('rgba(22, 22, 22, 0.2)'),\n", + " width = 0.5,)),\n", + " x_col,\n", + " y_col)\n", + "\n", + "# layout = go.Layout(\n", + "# xaxis=dict(domain=[0, 0.45]),\n", + "# yaxis=dict(\n", + "# domain=[0, 0.45],\n", + "# type='log',\n", + "# ),\n", + "# xaxis2=dict(domain=[0.55, 1]),\n", + "# xaxis4=dict(domain=[0.55, 1], anchor='y4'),\n", + "# yaxis3=dict(\n", + "# domain=[0.55, 1],\n", + "# type='log',\n", + "# ),\n", + "# yaxis4=dict(\n", + "# domain=[0.55, 1],\n", + "# anchor='x4',\n", + "# type='log',\n", + "# ))\n", + "\n", + "fig['layout'].update(showlegend=False, title='Specs with Subplot Title',height=800,)\n", + "\n", + "for ax in ['yaxis','yaxis2']:\n", + "# fig['layout'][ax]['type']='log'\n", + " fig['layout'][ax]['range']= [0,100]\n", + "\n", + "for ax in ['xaxis', 'xaxis2']:\n", + " fig['layout'][ax]['range']= [-1,1]\n", + "\n", + "go.FigureWidget(fig)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig['layout']['yaxis']" + ] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/06_change_in_slope.ipynb b/notebooks/06_change_in_slope.ipynb new file mode 100644 index 0000000..2caa57d --- /dev/null +++ b/notebooks/06_change_in_slope.ipynb @@ -0,0 +1,356 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Check change in mean slope\n", + "- Check the effect of changes in prestorm and poststorm mean slope.\n", + "- If there is a large berm, the prestorm mean slope (between dune toe and MHW) could be too small, and underpredict wave runup and TWL.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup notebook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Enable autoreloading of our modules. \n", + "# Most of the code will be located in the /src/ folder, \n", + "# and then called from the notebook.\n", + "%matplotlib inline\n", + "%reload_ext autoreload\n", + "%autoreload" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.core.debugger import set_trace\n", + "\n", + "import pandas as pd\n", + "import numpy as np\n", + "import os\n", + "\n", + "import plotly\n", + "import plotly.graph_objs as go\n", + "import plotly.plotly as py\n", + "import plotly.tools as tools\n", + "import plotly.figure_factory as ff\n", + "import plotly.io as pio\n", + "\n", + "import itertools\n", + "\n", + "import matplotlib\n", + "from matplotlib import cm\n", + "import colorlover as cl\n", + "\n", + "from ipywidgets import widgets, Output\n", + "from IPython.display import display, clear_output, Image, HTML\n", + "\n", + "from sklearn.metrics import confusion_matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import data\n", + "Import our data into pandas Dataframes for the analysis. Data files are `.csv` files which are stored in the `./data/interim/` folder." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def df_from_csv(csv, index_col, data_folder='../data/interim'):\n", + " print('Importing {}'.format(csv))\n", + " return pd.read_csv(os.path.join(data_folder,csv), index_col=index_col)\n", + "\n", + "df_waves = df_from_csv('waves.csv', index_col=[0, 1])\n", + "df_tides = df_from_csv('tides.csv', index_col=[0, 1])\n", + "df_profiles = df_from_csv('profiles.csv', index_col=[0, 1, 2])\n", + "df_sites = df_from_csv('sites.csv', index_col=[0])\n", + "df_profile_features_crest_toes = df_from_csv('profile_features_crest_toes.csv', index_col=[0,1])\n", + "\n", + "# Note that the forecasted data sets should be in the same order for impacts and twls\n", + "impacts = {\n", + " 'forecasted': {\n", + " 'foreshore_slope_sto06': df_from_csv('impacts_forecasted_foreshore_slope_sto06.csv', index_col=[0]),\n", + " 'mean_slope_sto06': df_from_csv('impacts_forecasted_mean_slope_sto06.csv', index_col=[0]),\n", + " },\n", + " 'observed': df_from_csv('impacts_observed.csv', index_col=[0])\n", + " }\n", + "\n", + "\n", + "twls = {\n", + " 'forecasted': {\n", + " 'foreshore_slope_sto06': df_from_csv('twl_foreshore_slope_sto06.csv', index_col=[0, 1]),\n", + " 'mean_slope_sto06':df_from_csv('twl_mean_slope_sto06.csv', index_col=[0, 1]),\n", + " }\n", + "}\n", + "print('Done!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot prestorm vs poststorm mean slopes\n", + "Prestorm slopes have already been calculated as part of the TWL forecasting, however we'll need to extract the poststorm mean slopes from our profiles at each site." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Prestorm slopes are easy as we have already calculated this as part of the \n", + "df_slopes_prestorm = twls['forecasted']['mean_slope_sto06'].groupby('site_id').head(1).reset_index().set_index(['site_id']).beta.to_frame()\n", + "\n", + "# Get x and z at mhw (z=0.7m) for each site\n", + "z_mhw = 0.7\n", + "mhw_poststorm = []\n", + "for site, df in df_profiles.xs('poststorm', level='profile_type').groupby('site_id'):\n", + " df = df.dropna(subset=['z'])\n", + " df = df.iloc[(df['z']-z_mhw).abs().argsort().head(1)].reset_index()\n", + " df = df.iloc[0]\n", + " mhw_poststorm.append({\n", + " 'site_id': df.site_id,\n", + " 'x_mhw': df.x,\n", + " 'z_mhw': df.z\n", + " })\n", + "# break\n", + "df_mhw_poststorm = pd.DataFrame(mhw_poststorm)\n", + "df_mhw_poststorm = df_mhw_poststorm.set_index('site_id')\n", + "\n", + "# Get x and z at poststorm dune toe for each site\n", + "df_dune_toe_poststorm = df_profile_features_crest_toes.xs('poststorm', level='profile_type')[['dune_toe_x','dune_toe_z']]\n", + "\n", + "# Join df for mhw and dune toe\n", + "df = df_mhw_poststorm.join(df_dune_toe_poststorm)\n", + "df['beta'] = -(df['dune_toe_z'] - df['z_mhw']) / (df['dune_toe_x'] -df['x_mhw'])\n", + "df_slopes_poststorm = df['beta'].to_frame()\n", + "\n", + "# Count how many nans\n", + "print('Number of nans: {}'.format(df_slopes_poststorm.beta.isna().sum()))\n", + "\n", + "# Display dataframe\n", + "print('df_slopes_poststorm:')\n", + "df_slopes_poststorm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's join our post storm slopes, prestorm slopes, observed and forecasted impacts into one data frame to make it easier to plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dfs = [df_slopes_poststorm.rename(columns={'beta':'poststorm_beta'}),\n", + " df_slopes_prestorm.rename(columns={'beta':'prestorm_beta'}),\n", + " impacts['observed']['storm_regime'].to_frame().rename(columns={'storm_regime': 'observed_regime'}),\n", + " impacts['forecasted']['mean_slope_sto06']['storm_regime'].to_frame().rename(columns={'storm_regime': 'forecasted_regime'})\n", + " ]\n", + "\n", + "df = pd.concat(dfs, axis='columns')\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_data.index" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot our data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = tools.make_subplots(\n", + " rows=2,\n", + " cols=2,\n", + " specs=[[{}, {}], [{}, {}]],\n", + " subplot_titles=('Swash/Swash', 'Swash/Collision', \n", + " 'Collision/Swash', 'Collision/Collision'),\n", + " shared_xaxes=True, shared_yaxes=True,)\n", + "\n", + "\n", + "# Loop through combinations of observed/forecasted swash/collision\n", + "data = []\n", + "for forecasted_regime, observed_regime in itertools.product(['swash','collision'],repeat=2):\n", + " \n", + " # Get data for this combination \n", + " query = 'forecasted_regime==\"{}\" & observed_regime==\"{}\"'.format(forecasted_regime, observed_regime)\n", + " df_data = df.query(query)\n", + " print(query)\n", + " \n", + " \n", + " # Determine which subplot to plot results in\n", + " if forecasted_regime == 'swash' and observed_regime == 'swash':\n", + " x_col = 1\n", + " y_col = 1\n", + " elif forecasted_regime == 'collision' and observed_regime == 'collision':\n", + " x_col = 2\n", + " y_col = 2\n", + " elif forecasted_regime == 'swash' and observed_regime == 'collision':\n", + " x_col = 2\n", + " y_col = 1\n", + " elif forecasted_regime == 'collision' and observed_regime == 'swash':\n", + " x_col = 1\n", + " y_col = 2\n", + " else:\n", + " print('something went wrong')\n", + " continue\n", + "\n", + " fig.append_trace(\n", + " go.Scatter(\n", + " x=df_data.prestorm_beta,\n", + " y=df_data.poststorm_beta,\n", + " text = df_data.index.tolist(),\n", + " hoverinfo = 'text',\n", + " mode = 'markers',\n", + " line = dict(\n", + " color = ('rgba(22, 22, 22, 0.2)'),\n", + " width = 0.5,)),\n", + " x_col,\n", + " y_col)\n", + "\n", + "# layout = go.Layout(\n", + "# xaxis=dict(domain=[0, 0.45]),\n", + "# yaxis=dict(\n", + "# domain=[0, 0.45],\n", + "# type='log',\n", + "# ),\n", + "# xaxis2=dict(domain=[0.55, 1]),\n", + "# xaxis4=dict(domain=[0.55, 1], anchor='y4'),\n", + "# yaxis3=dict(\n", + "# domain=[0.55, 1],\n", + "# type='log',\n", + "# ),\n", + "# yaxis4=dict(\n", + "# domain=[0.55, 1],\n", + "# anchor='x4',\n", + "# type='log',\n", + "# ))\n", + "\n", + "fig['layout'].update(showlegend=False, title='Specs with Subplot Title',height=800,)\n", + "\n", + "for ax in ['yaxis','yaxis2']:\n", + "# fig['layout'][ax]['type']='log'\n", + " fig['layout'][ax]['range']= [0,0.2]\n", + "\n", + "for ax in ['xaxis', 'xaxis2']:\n", + " fig['layout'][ax]['range']= [0,0.2]\n", + "\n", + "go.FigureWidget(fig)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looking at the above plot:\n", + "- In general, we can see that the prestorm mean slope is flatter than the poststorm mean slope. This can be explained by the presence of prestorm berms, which increase the prestorm mean slope. During the storm, these berms get eroded and decrease the slope.\n", + "- **Collision/Collision**: Where we observe and predict collision, we see steeper prestorm slopes. This is to be expected since larger slopes will generate more runup and higher TWLs.\n", + "- **Swash/Collision**: Where we predict collision but observe swash, we can see that the prestorm mean slopes >0.1 generate high TWLs. \n", + "\n" + ] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}