{ "cells": [ { "cell_type": "markdown", "id": "e6a4c185-3663-4b6b-8924-33ddc1a20d4a", "metadata": {}, "source": [ "# Publication Figure 5\n", "\n", "This jupyter notebook contains the Analysis code for an upcoming publication.\n", "\n", "Authors: Kevin Sawade (kevin.sawade@uni-konstanz.de), Tobias Lemke, Christine Peter (christine.peter@uni-konstanz.de)\n", "\n", "EncoderMap's featurization was inspired by the (no longer maintained) PyEMMA library. Consider citing it (https://dx.doi.org/10.1021/acs.jctc.5b00743), if you are using EncoderMap:\n", "\n", "## Imports\n", "\n", "We start with the imports." ] }, { "cell_type": "code", "execution_count": 1, "id": "ff68b7c7-26b0-4e22-8523-535b0d5fe044", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/kevin/git/encoder_map_private/encodermap/__init__.py:194: GPUsAreDisabledWarning: EncoderMap disables the GPU per default because most tensorflow code runs with a higher compatibility when the GPU is disabled. If you want to enable GPUs manually, set the environment variable 'ENCODERMAP_ENABLE_GPU' to 'True' before importing EncoderMap. To do this in python you can run:\n", "\n", "import os; os.environ['ENCODERMAP_ENABLE_GPU'] = 'True'\n", "\n", "before importing encodermap.\n", " _warnings.warn(\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "621d4a46bbef4916a902ecd23d325a61", "version_major": 2, "version_minor": 0 }, "text/plain": [] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Future imports at the top\n", "from __future__ import annotations\n", "\n", "# Import EncoderMap\n", "import encodermap as em\n", "from encodermap.plot import plotting\n", "from encodermap.plot.plotting import _plot_free_energy\n", "\n", "# Builtin packages\n", "import re\n", "import io\n", "import warnings\n", "import os\n", "import json\n", "import contextlib\n", "import time\n", "from copy import deepcopy\n", "from types import SimpleNamespace\n", "from pathlib import Path\n", "\n", "# Math\n", "import numpy as np\n", "import pandas as pd\n", "import xarray as xr\n", "\n", "# ML\n", "import tensorflow as tf\n", "\n", "# Plotting\n", "import plotly.express as px\n", "import plotly.graph_objects as go\n", "import plotly.io as pio\n", "from plotly.subplots import make_subplots\n", "import plotly.io as pio\n", "\n", "# MD\n", "import mdtraj as md\n", "import MDAnalysis as mda\n", "import nglview as nv\n", "\n", "# dates\n", "from dateutil import parser" ] }, { "cell_type": "markdown", "id": "8e119c27-fb9d-4ee7-9bd7-aae2b4aac24e", "metadata": {}, "source": [ "Using Autoreload we can make changes in the EncoderMap source code and use the new code, without needing to restart the Kernel." ] }, { "cell_type": "code", "execution_count": 3, "id": "05c70465-7506-44ec-8bca-c2e0c123e0ba", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The autoreload extension is already loaded. To reload it, use:\n", " %reload_ext autoreload\n" ] } ], "source": [ "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "markdown", "id": "9f3b16a6-69f8-4a31-a8ca-8ce2b3f82cae", "metadata": {}, "source": [ "Functions for this notebook." ] }, { "cell_type": "code", "execution_count": 4, "id": "2d1e79df-b680-4c6a-9a8c-ec7100c7de7a", "metadata": {}, "outputs": [], "source": [ "@contextlib.contextmanager\n", "def set_env(**environ):\n", " \"\"\"\n", " Temporarily set the process environment variables.\n", "\n", " >>> with set_env(PLUGINS_DIR='test/plugins'):\n", " ... \"PLUGINS_DIR\" in os.environ\n", " True\n", "\n", " >>> \"PLUGINS_DIR\" in os.environ\n", " False\n", "\n", " :type environ: dict[str, unicode]\n", " :param environ: Environment variables to set\n", " \"\"\"\n", " old_environ = dict(os.environ)\n", " os.environ.update(environ)\n", " try:\n", " yield\n", " finally:\n", " os.environ.clear()\n", " os.environ.update(old_environ)" ] }, { "cell_type": "markdown", "id": "3bc8782e-5fe6-4099-a40b-9d1f893e7379", "metadata": {}, "source": [ "## Trained network weights\n", "\n", "EncoderMap's Neural Network is initialized with random weights and biases. While the training is deterministic with pre-defined training weights, and the inferences that can be made from two trained models are qualitatively similar, the numerical exact output depends on the starting weights.\n", "\n", "For this reason, the trained network weights are available from the corresponding authors upon reasonable request or by raising an issue on GitHub: https://github.com/AG-Peter/encodermap/issues\n", "\n", "Figure 4 demonstrates how EncoderMap can deal when the training input contains different topologies. The MD simulations used for this figure were orginally conducted for:\n", "\n", "```\n", "@article{kienle2022electrostatic,\n", " title={Electrostatic and steric effects underlie acetylation-induced changes in ubiquitin structure and function},\n", " author={Kienle, Simon Maria and Schneider, Tobias and Stuber, Katrin and Globisch, Christoph and Jansen, Jasmin and Stengel, Florian and Peter, Christine and Marx, Andreas and Kovermann, Michael and Scheffner, Martin},\n", " journal={Nature communications},\n", " volume={13},\n", " number={1},\n", " pages={5435},\n", " year={2022},\n", " publisher={Nature Publishing Group UK London}\n", "}\n", "```\n", "\n", "They can be downloaded from KonDATA as well. First, let use define, where the data will be put." ] }, { "cell_type": "code", "execution_count": 5, "id": "5ce9ff32-06f6-4ef7-9624-e20a86e31751", "metadata": {}, "outputs": [], "source": [ "figure_5_data_dir = Path.cwd() / \"analysis/figure_5\"\n", "if not figure_5_data_dir.exists():\n", " figure_5_data_dir.mkdir(parents=True)" ] }, { "cell_type": "markdown", "id": "026c1e6e-3140-47e1-b5c6-6abcb7f4ad45", "metadata": {}, "source": [ "### Load MD Data from KonDATA" ] }, { "cell_type": "code", "execution_count": 6, "id": "de5e8508-3ad8-46e9-a14f-b08c77b3edc2", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Output()" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n" ], "text/plain": [] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "'/home/kevin/git/encoder_map_private/docs/source/notebooks/publication/analysis/figure_4'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "em.get_from_kondata(\n", " \"Ub_K11_mutants\",\n", " output=figure_5_data_dir,\n", " silence_overwrite_message=True,\n", ")" ] }, { "cell_type": "markdown", "id": "b10e8c63-ce77-4d97-a931-4b4947b5a10c", "metadata": {}, "source": [ "Next, we will load the `.xtc` and `.pdb` files into an EncoderMap `TrajEnsemble`. The list of common strings (`common_str`) defines, how EncoderMap should group the trajectories." ] }, { "cell_type": "code", "execution_count": 7, "id": "17a0dba7-7210-4f40-b02c-931fc3f24db0", "metadata": {}, "outputs": [], "source": [ "common_str=[\"Ac\", \"C\", \"Q\", \"R\", \"wt\"]" ] }, { "cell_type": "markdown", "id": "70e0bfd7-39f8-497d-a3c1-cfb7756c9a6e", "metadata": {}, "source": [ "Then we can use Python pathlib's glob to search for `.xtc` and `.pdb` files." ] }, { "cell_type": "code", "execution_count": 8, "id": "b1431961-3612-4e99-9467-e75a741d75d0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "analysis/figure_4/Ub_K11C_I/traj.xtc\n", "analysis/figure_4/Ub_K11Q_III/traj.xtc\n", "analysis/figure_4/Ub_K11Q_II/traj.xtc\n", "analysis/figure_4/Ub_K11Ac_III/traj.xtc\n", "analysis/figure_4/Ub_wt_II/traj.xtc\n", "analysis/figure_4/Ub_wt_III/traj.xtc\n", "analysis/figure_4/Ub_K11Q_I/traj.xtc\n", "analysis/figure_4/Ub_K11R_I/traj.xtc\n", "analysis/figure_4/Ub_wt_I/traj.xtc\n", "analysis/figure_4/Ub_K11C_II/traj.xtc\n", "analysis/figure_4/Ub_K11R_III/traj.xtc\n", "analysis/figure_4/Ub_K11Ac_II/traj.xtc\n", "analysis/figure_4/Ub_K11C_III/traj.xtc\n", "analysis/figure_4/Ub_K11Ac_I/traj.xtc\n", "analysis/figure_4/Ub_K11R_II/traj.xtc\n" ] } ], "source": [ "xtc_files = list(figure_5_data_dir.rglob(\"*.xtc\"))\n", "for file in xtc_files:\n", " print(file.relative_to(Path.cwd()))" ] }, { "cell_type": "code", "execution_count": 9, "id": "f985c6d5-bba1-4ed1-aaef-c14370060f41", "metadata": {}, "outputs": [], "source": [ "pdb_files = []\n", "for file in xtc_files:\n", " pdb_file = file.with_name(\"start.pdb\")\n", " pdb_files.append(pdb_file)" ] }, { "cell_type": "code", "execution_count": 10, "id": "1e3a68f6-8113-4ceb-92cb-f23141a8addf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\n", "\n" ], "text/plain": [ "\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
<xarray.Dataset>\n", "Dimensions: (frame_num: 4001, ATOM: 228, COORDS: 3,\n", " CENTRAL_DISTANCES: 227,\n", " CENTRAL_ANGLES: 226,\n", " CENTRAL_DIHEDRALS: 225,\n", " SIDE_DIHEDRALS: 162, ATOM_NO: 4,\n", " traj_num: 15)\n", "Coordinates:\n", " * frame_num (frame_num) int64 0 1 2 ... 3999 4000\n", " * ATOM (ATOM) <U3 '1' '2' '3' ... '227' '228'\n", " * COORDS (COORDS) <U10 'POSITION X' ... 'POSIT...\n", " * CENTRAL_DISTANCES (CENTRAL_DISTANCES) <U19 'CENTERDISTA...\n", " * CENTRAL_ANGLES (CENTRAL_ANGLES) <U19 'CENTERANGLE ...\n", " * CENTRAL_DIHEDRALS (CENTRAL_DIHEDRALS) <U19 'CENTERDIH P...\n", " * SIDE_DIHEDRALS (SIDE_DIHEDRALS) <U18 'SIDECHDIH CHI1...\n", " * ATOM_NO (ATOM_NO) int64 0 1 2 3\n", " * traj_num (traj_num) int64 0 1 2 3 ... 11 12 13 14\n", " traj_name (traj_num) <U4 'traj' 'traj' ... 'traj'\n", " time (traj_num, frame_num) float32 0.0 ......\n", "Data variables:\n", " central_cartesians (traj_num, frame_num, ATOM, COORDS) float32 ...\n", " central_distances (traj_num, frame_num, CENTRAL_DISTANCES) float32 ...\n", " central_angles (traj_num, frame_num, CENTRAL_ANGLES) float32 ...\n", " central_dihedrals (traj_num, frame_num, CENTRAL_DIHEDRALS) float32 ...\n", " side_dihedrals (traj_num, frame_num, SIDE_DIHEDRALS) float32 ...\n", " central_cartesians_feature_indices (traj_num, ATOM) int32 0 4 9 ... 762 763\n", " central_distances_feature_indices (traj_num, CENTRAL_DISTANCES, ATOM_NO) float64 ...\n", " central_angles_feature_indices (traj_num, CENTRAL_ANGLES, ATOM_NO) float64 ...\n", " central_dihedrals_feature_indices (traj_num, CENTRAL_DIHEDRALS, ATOM_NO) int32 ...\n", " side_dihedrals_feature_indices (traj_num, SIDE_DIHEDRALS, ATOM_NO) float64 ...\n", "Attributes:\n", " length_units: nm\n", " time_units: ps\n", " feature_axes: ['ATOM', 'CENTRAL_ANGLES', 'CENTRAL_DIHEDRALS', 'CENTRAL...\n", " angle_units: rad\n", " full_paths: ['/home/kevin/git/encoder_map_private/docs/source/notebo...\n", " topology_files: ['/home/kevin/git/encoder_map_private/docs/source/notebo...
0?[0]:[]);if(o.enter().append(\"g\").classed(f.containerClassName,!0).style(\"cursor\",\"pointer\"),o.exit().each((function(){n.select(this).selectAll(\"g.\"+f.headerGroupClassName).each(a)})).remove(),0!==r.length){var l=o.selectAll(\"g.\"+f.headerGroupClassName).data(r,p);l.enter().append(\"g\").classed(f.headerGroupClassName,!0);for(var u=s.ensureSingle(o,\"g\",f.dropdownButtonGroupClassName,(function(t){t.style(\"pointer-events\",\"all\")})),c=0;c 90&&i.log(\"Long binary search...\"),h-1},e.sorterAsc=function(t,e){return t-e},e.sorterDes=function(t,e){return e-t},e.distinctVals=function(t){var r,n=t.slice();for(n.sort(e.sorterAsc),r=n.length-1;r>-1&&n[r]===o;r--);for(var i,a=n[r]-n[0]||1,s=a/(r||1)/1e4,l=[],u=0;u<=r;u++){var c=n[u],f=c-i;void 0===i?(l.push(c),i=c):f>s&&(a=Math.min(a,f),l.push(c),i=c)}return{vals:l,minDiff:a}},e.roundUp=function(t,e,r){for(var n,i=0,a=e.length-1,o=0,s=r?0:1,l=r?1:0,u=r?Math.ceil:Math.floor;i0&&(n=1),r&&n)return t.sort(e)}return n?t:t.reverse()},e.findIndexOfMin=function(t,e){e=e||a;for(var r,n=1/0,i=0;il?r.y-l:0;return Math.sqrt(u*u+f*f)}for(var p=h(u);p;){if((u+=p+r)>f)return;p=h(u)}for(p=h(f);p;){if(u>(f-=p+r))return;p=h(f)}return{min:u,max:f,len:f-u,total:c,isClosed:0===u&&f===c&&Math.abs(n.x-i.x)<.1&&Math.abs(n.y-i.y)<.1}},e.findPointOnPath=function(t,e,r,n){for(var i,a,o,s=(n=n||{}).pathLength||t.getTotalLength(),l=n.tolerance||.001,u=n.iterationLimit||30,c=t.getPointAtLength(0)[r]>t.getPointAtLength(s)[r]?-1:1,f=0,h=0,p=s;f0?p=i:h=i,f++}return a}},33040:function(t,e,r){\"use strict\";var n=r(38248),i=r(49760),a=r(72160),o=r(8932),s=r(22548).defaultLine,l=r(38116).isArrayOrTypedArray,u=a(s);function c(t,e){var r=t;return r[3]*=e,r}function f(t){if(n(t))return u;var e=a(t);return e.length?e:u}function h(t){return n(t)?t:1}t.exports={formatColor:function(t,e,r){var n=t.color;n&&n._inputArray&&(n=n._inputArray);var i,s,p,d,v,g=l(n),y=l(e),m=o.extractOpts(t),x=[];if(i=void 0!==m.colorscale?o.makeColorScaleFuncFromTrace(t):f,s=g?function(t,e){return void 0===t[e]?u:a(i(t[e]))}:f,p=y?function(t,e){return void 0===t[e]?1:h(t[e])}:h,g||y)for(var b=0;b
/i;e.BR_TAG_ALL=/
/gi;var _=/(^|[\\s\"'])style\\s*=\\s*(\"([^\"]*);?\"|'([^']*);?')/i,w=/(^|[\\s\"'])href\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/i,T=/(^|[\\s\"'])target\\s*=\\s*(\"([^\"\\s]*)\"|'([^'\\s]*)')/i,k=/(^|[\\s\"'])popup\\s*=\\s*(\"([\\w=,]*)\"|'([\\w=,]*)')/i;function A(t,e){if(!t)return null;var r=t.match(e),n=r&&(r[3]||r[4]);return n&&L(n)}var M=/(^|;)\\s*color:/;e.plainText=function(t,e){for(var r=void 0!==(e=e||{}).len&&-1!==e.len?e.len:1/0,n=void 0!==e.allowedTags?e.allowedTags:[\"br\"],i=t.split(m),a=[],o=\"\",s=0,l=0;l
\"+l;e.text=u}(t,o,r,u):\"log\"===c?function(t,e,r,n,a){var o=t.dtick,l=e.x,u=t.tickformat,c=\"string\"==typeof o&&o.charAt(0);if(\"never\"===a&&(a=\"\"),n&&\"L\"!==c&&(o=\"L3\",c=\"L\"),u||\"L\"===c)e.text=bt(Math.pow(10,l),t,a,n);else if(i(o)||\"D\"===c&&s.mod(l+.01,1)<.1){var f=Math.round(l),h=Math.abs(f),p=t.exponentformat;\"power\"===p||mt(p)&&xt(f)?(e.text=0===f?1:1===f?\"10\":\"10\"+(f>1?\"\":P)+h+\"\",e.fontSize*=1.25):(\"e\"===p||\"E\"===p)&&h>2?e.text=\"1\"+p+(f>0?\"+\":P)+h:(e.text=bt(Math.pow(10,l),t,\"\",\"fakehover\"),\"D1\"===o&&\"y\"===t._id.charAt(0)&&(e.dy-=e.fontSize/6))}else{if(\"D\"!==c)throw\"unrecognized dtick \"+String(o);e.text=String(Math.round(Math.pow(10,s.mod(l,1)))),e.fontSize*=.75}if(\"D1\"===t.dtick){var d=String(e.text).charAt(0);\"0\"!==d&&\"1\"!==d||(\"y\"===t._id.charAt(0)?e.dx-=e.fontSize/4:(e.dy+=e.fontSize/2,e.dx+=(t.range[1]>t.range[0]?1:-1)*e.fontSize*(l<0?.5:.25)))}}(t,o,0,u,v):\"category\"===c?function(t,e){var r=t._categories[Math.round(e.x)];void 0===r&&(r=\"\"),e.text=String(r)}(t,o):\"multicategory\"===c?function(t,e,r){var n=Math.round(e.x),i=t._categories[n]||[],a=void 0===i[1]?\"\":String(i[1]),o=void 0===i[0]?\"\":String(i[0]);r?e.text=o+\" - \"+a:(e.text=a,e.text2=o)}(t,o,r):Dt(t)?function(t,e,r,n,i){if(\"radians\"!==t.thetaunit||r)e.text=bt(e.x,t,i,n);else{var a=e.x/180;if(0===a)e.text=\"0\";else{var o=function(t){function e(t,e){return Math.abs(t-e)<=1e-6}var r=function(t){for(var r=1;!e(Math.round(t*r)/r,t);)r*=10;return r}(t),n=t*r,i=Math.abs(function t(r,n){return e(n,0)?r:t(n,r%n)}(n,r));return[Math.round(n/i),Math.round(r/i)]}(a);if(o[1]>=100)e.text=bt(s.deg2rad(e.x),t,i,n);else{var l=e.x<0;1===o[1]?1===o[0]?e.text=\"π\":e.text=o[0]+\"π\":e.text=[\"\",o[0],\"\",\"⁄\",\"\",o[1],\"\",\"π\"].join(\"\"),l&&(e.text=P+e.text)}}}}(t,o,r,u,v):function(t,e,r,n,i){\"never\"===i?i=\"\":\"all\"===t.showexponent&&Math.abs(e.x/t.dtick)<1e-6&&(i=\"hide\"),e.text=bt(e.x,t,i,n)}(t,o,0,u,v),n||(t.tickprefix&&!d(t.showtickprefix)&&(o.text=t.tickprefix+o.text),t.ticksuffix&&!d(t.showticksuffix)&&(o.text+=t.ticksuffix)),t.labelalias&&t.labelalias.hasOwnProperty(o.text)){var g=t.labelalias[o.text];\"string\"==typeof g&&(o.text=g)}if(\"boundaries\"===t.tickson||t.showdividers){var y=function(e){var r=t.l2p(e);return r>=0&&r<=t._length?e:null};o.xbnd=[y(o.x-.5),y(o.x+t.dtick-.5)]}return o},H.hoverLabelText=function(t,e,r){r&&(t=s.extendFlat({},t,{hoverformat:r}));var n=s.isArrayOrTypedArray(e)?e[0]:e,i=s.isArrayOrTypedArray(e)?e[1]:void 0;if(void 0!==i&&i!==n)return H.hoverLabelText(t,n,r)+\" - \"+H.hoverLabelText(t,i,r);var a=\"log\"===t.type&&n<=0,o=H.tickText(t,t.c2l(a?-n:n),\"hover\").text;return a?0===n?\"0\":P+o:o};var yt=[\"f\",\"p\",\"n\",\"μ\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\"];function mt(t){return\"SI\"===t||\"B\"===t}function xt(t){return t>14||t<-15}function bt(t,e,r,n){var a=t<0,o=e._tickround,l=r||e.exponentformat||\"B\",u=e._tickexponent,c=H.getTickFormat(e),f=e.separatethousands;if(n){var h={exponentformat:l,minexponent:e.minexponent,dtick:\"none\"===e.showexponent?e.dtick:i(t)&&Math.abs(t)||1,range:\"none\"===e.showexponent?e.range.map(e.r2d):[0,t||1]};vt(h),o=(Number(h._tickround)||0)+4,u=h._tickexponent,e.hoverformat&&(c=e.hoverformat)}if(c)return e._numFormat(c)(t).replace(/-/g,P);var p,d=Math.pow(10,-o)/2;if(\"none\"===l&&(u=0),(t=Math.abs(t))
\")):x=h.textLabel;var L={x:h.traceCoordinate[0],y:h.traceCoordinate[1],z:h.traceCoordinate[2],data:_._input,fullData:_,curveNumber:_.index,pointNumber:T};d.appendArrayPointValue(L,_,T),t._module.eventData&&(L=_._module.eventData(L,h,_,{},T));var C={points:[L]};if(e.fullSceneLayout.hovermode){var P=[];d.loneHover({trace:_,x:(.5+.5*m[0]/m[3])*s,y:(.5-.5*m[1]/m[3])*l,xLabel:k.xLabel,yLabel:k.yLabel,zLabel:k.zLabel,text:x,name:c.name,color:d.castHoverOption(_,T,\"bgcolor\")||c.color,borderColor:d.castHoverOption(_,T,\"bordercolor\"),fontFamily:d.castHoverOption(_,T,\"font.family\"),fontSize:d.castHoverOption(_,T,\"font.size\"),fontColor:d.castHoverOption(_,T,\"font.color\"),nameLength:d.castHoverOption(_,T,\"namelength\"),textAlign:d.castHoverOption(_,T,\"align\"),hovertemplate:f.castOption(_,T,\"hovertemplate\"),hovertemplateLabels:f.extendFlat({},L,k),eventData:[L]},{container:n,gd:r,inOut_bbox:P}),L.bbox=P[0]}h.distance<5&&(h.buttons||w)?r.emit(\"plotly_click\",C):r.emit(\"plotly_hover\",C),this.oldEventData=C}else d.loneUnhover(n),this.oldEventData&&r.emit(\"plotly_unhover\",this.oldEventData),this.oldEventData=void 0;e.drawAnnotations(e)},k.recoverContext=function(){var t=this;t.glplot.dispose();var e=function(){t.glplot.gl.isContextLost()?requestAnimationFrame(e):t.initializeGLPlot()?t.plot.apply(t,t.plotArgs):f.error(\"Catastrophic and unrecoverable WebGL error. Context lost.\")};requestAnimationFrame(e)};var M=[\"xaxis\",\"yaxis\",\"zaxis\"];function S(t,e,r){for(var n=t.fullSceneLayout,i=0;i<3;i++){var a=M[i],o=a.charAt(0),s=n[a],l=e[o],u=e[o+\"calendar\"],c=e[\"_\"+o+\"length\"];if(f.isArrayOrTypedArray(l))for(var h,p=0;p<(c||l.length);p++)if(f.isArrayOrTypedArray(l[p]))for(var d=0;d
\");b.text(T).attr(\"data-unformatted\",T).call(f.convertToTspans,t),_=c.bBox(b.node())}b.attr(\"transform\",a(-3,8-_.height)),x.insert(\"rect\",\".static-attribution\").attr({x:-_.width-6,y:-_.height-3,width:_.width+6,height:_.height+3,fill:\"rgba(255, 255, 255, 0.75)\"});var k=1;_.width+6>w&&(k=w/(_.width+6));var A=[n.l+n.w*h.x[1],n.t+n.h*(1-h.y[0])];x.attr(\"transform\",a(A[0],A[1])+o(k))}},e.updateFx=function(t){for(var e=t._fullLayout,r=e._subplots[p],n=0;n