Skip to content

Commit 9c80d98

Browse files
committed
add new notebooks in demo/ folder, basic cleanup
1 parent 5145c47 commit 9c80d98

File tree

4 files changed

+843
-7
lines changed

4 files changed

+843
-7
lines changed

demo/notebook_cyclicvoltammetry.ipynb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@
3030
"import matplotlib.pyplot as plt\n",
3131
"\n",
3232
"try:\n",
33-
" import gamry_parser as parser\n",
33+
" import gamry_parser\n",
3434
"except:\n",
35-
" !pip install -q --upgrade gamry-parser\n",
36-
" import gamry_parser as parser\n",
35+
" subprocess.run(\n",
36+
" [\"pip\", \"install\", \"gamry-parser\"], \n",
37+
" encoding=\"utf-8\", \n",
38+
" shell=False)\n",
39+
"finally:\n",
40+
" import gamry_parser\n",
3741
"\n",
3842
"p = parser.CyclicVoltammetry()\n",
3943
" \n",
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
{
2+
"nbformat": 4,
3+
"nbformat_minor": 0,
4+
"metadata": {
5+
"colab": {
6+
"name": "Gamry-Parser CyclicVoltammetry Peak Detection Example",
7+
"provenance": [],
8+
"collapsed_sections": []
9+
},
10+
"kernelspec": {
11+
"name": "python3",
12+
"display_name": "Python 3"
13+
}
14+
},
15+
"cells": [
16+
{
17+
"cell_type": "code",
18+
"metadata": {
19+
"id": "YyeVlSYkhahF",
20+
"cellView": "form"
21+
},
22+
"source": [
23+
"#@title Imports, initial setup (Ctrl+F9 to run all)\n",
24+
"import os\n",
25+
"import re\n",
26+
"import pandas as pd\n",
27+
"import matplotlib.pyplot as plt\n",
28+
"from scipy.signal import find_peaks\n",
29+
"import copy\n",
30+
"\n",
31+
"try:\n",
32+
" import gamry_parser\n",
33+
"except:\n",
34+
" subprocess.run(\n",
35+
" [\"pip\", \"install\", \"gamry-parser\"], \n",
36+
" encoding=\"utf-8\", \n",
37+
" shell=False)\n",
38+
"finally:\n",
39+
" import gamry_parser\n",
40+
"\n",
41+
"gp = gamry_parser.CyclicVoltammetry()\n",
42+
"\n",
43+
"print('Done.')"
44+
],
45+
"execution_count": null,
46+
"outputs": []
47+
},
48+
{
49+
"cell_type": "code",
50+
"metadata": {
51+
"id": "ZGoqracvk9q2",
52+
"cellView": "form"
53+
},
54+
"source": [
55+
"\"\"\"\n",
56+
"### SCRIPT CONFIGURATION SETTINGS ###\n",
57+
"\"\"\"\n",
58+
"#@markdown **Experimental Setup**\n",
59+
"\n",
60+
"#@markdown Where should the notebook search for DTA files? Examples (using google colab):\n",
61+
"#@markdown - Mounted google drive folder: `/content/drive/`\n",
62+
"#@markdown - If uploading files manually, : `/content/`).\n",
63+
"\n",
64+
"data_path = \"/content/\" #@param {type:\"string\"}\n",
65+
"\n",
66+
"#@markdown Filter which files we want to analyze\n",
67+
"file_pattern = \"Search-For-Text\" #@param {type:\"string\"}\n",
68+
"\n",
69+
"#@markdown Extract trace labels from file name (e.g. `[17:].lower()` => drop the first 17 characters from the filename and convert to lowercase). The trace labels are used for category labeling (and plot legends)\n",
70+
"file_label_xform = \"[51:]\" #@param {type:\"string\"}\n",
71+
"\n",
72+
"# create a \"results\" dataframe to contain the values we care about\n",
73+
"data_df = pandas.DataFrame()\n",
74+
"settings_df = pandas.DataFrame()\n",
75+
"peaks_df = pandas.DataFrame()\n",
76+
"\n",
77+
"# identify files to process\n",
78+
"files = [f for f in os.listdir(data_path) if \n",
79+
" os.path.splitext(f)[1].lower() == \".dta\" and\n",
80+
" len(re.findall(file_pattern.upper(), f.upper())) > 0\n",
81+
" ]\n"
82+
],
83+
"execution_count": null,
84+
"outputs": []
85+
},
86+
{
87+
"cell_type": "code",
88+
"metadata": {
89+
"cellView": "form",
90+
"id": "8MFNF2Qz6lef"
91+
},
92+
"source": [
93+
"#@markdown **Process Data and Detect Peaks**\n",
94+
"\n",
95+
"#@markdown Which CV curves (cycle number) should be sampled? (`0` would select the first CV curve from each file)\n",
96+
"curves_to_sample = \"0\" #@param {type:\"string\"}\n",
97+
"curves_to_sample = [int(item.strip()) for item in curves_to_sample.split(\",\")]\n",
98+
"\n",
99+
"#@markdown Peak Detection: specify the peak detection parameters\n",
100+
"peak_width_mV = 75 #@param {type:\"integer\"}\n",
101+
"peak_height_nA = 25 #@param {type:\"integer\"}\n",
102+
"peak_thresh_max_mV = 800 #@param {type:\"integer\"}\n",
103+
"peak_thresh_min_mV = -100 #@param {type:\"integer\"}\n",
104+
"\n",
105+
"# this method finds the row that has an index value closest to the desired time elapsed\n",
106+
"def duration_lookup(df, elapsed):\n",
107+
" return df.index.get_loc(elapsed, method='nearest')\n",
108+
"\n",
109+
"# iterate through each DTA file\n",
110+
"for index, file in enumerate(files):\n",
111+
" print(\"Checking File {}\".format(file))\n",
112+
"\n",
113+
" label, ext = os.path.splitext(file)\n",
114+
" my_label = \"-\".join(eval(\"label{}\".format(file_label_xform)).strip().split())\n",
115+
"\n",
116+
" # load the dta file using gamry parser\n",
117+
" gp.load(filename=os.path.join(data_path, file))\n",
118+
"\n",
119+
" is_cv = gp.get_header().get(\"TAG\") == \"CV\"\n",
120+
" if not is_cv:\n",
121+
" # if the DTA file is a different experiment type, skip it and move to the next file.\n",
122+
" print(\"File `{}` is not a CV experiment. Skipping\".format(file))\n",
123+
" del files[index] # remove invalid file from list\n",
124+
" continue\n",
125+
" \n",
126+
" # for each CV file, let's extract the relevant information\n",
127+
" cv = gamry_parser.CyclicVoltammetry(filename=os.path.join(data_path, file))\n",
128+
" cv.load()\n",
129+
" for curve_num in curves_to_sample:\n",
130+
" print(\"\\tProcessing Curve #{}\".format(curve_num))\n",
131+
" v1, v2 = cv.get_v_range()\n",
132+
" settings = pandas.DataFrame({\n",
133+
" \"label\": my_label,\n",
134+
" \"curves\": cv.get_curve_count(),\n",
135+
" \"v1_mV\": v1*1000,\n",
136+
" \"v2_mV\": v2*1000,\n",
137+
" \"rate_mV\": cv.get_scan_rate(),\n",
138+
" }, index=[0])\n",
139+
" settings_df = settings_df.append(settings)\n",
140+
"\n",
141+
" data = copy.deepcopy(cv.get_curve_data(curve=curve_num))\n",
142+
" data.Im = data.Im*1e9\n",
143+
" data.Vf = data.Vf*1e3\n",
144+
" data[\"label\"] = my_label #\"{:03d}-{}\".format(index, curve_num)\n",
145+
"\n",
146+
" data_df = data_df.append(data)\n",
147+
"\n",
148+
" # find peaks in the data\n",
149+
" dV = cv.get_scan_rate() # in mV\n",
150+
" peak_width = int(peak_width_mV/dV)\n",
151+
" peaks_pos, props_pos = find_peaks(\n",
152+
" data.Im, \n",
153+
" width=peak_width, \n",
154+
" distance=2*peak_width, \n",
155+
" height=peak_height_nA\n",
156+
" )\n",
157+
" peaks_neg, props_neg = find_peaks(\n",
158+
" -data.Im, \n",
159+
" width=peak_width, \n",
160+
" distance=2*peak_width, \n",
161+
" height=peak_height_nA\n",
162+
" )\n",
163+
" peaks = list(peaks_pos) + list(peaks_neg)\n",
164+
" # remove peaks that are out of min/max range\n",
165+
" peaks = [peak \n",
166+
" for peak in peaks \n",
167+
" if data.Vf.iloc[peak] >= peak_thresh_min_mV and data.Vf.iloc[peak] <= peak_thresh_max_mV]\n",
168+
"\n",
169+
" # add detected peaks to aggregated peak dataframe\n",
170+
" peaks = data.iloc[peaks].sort_values(by=\"Vf\")\n",
171+
" peaks[\"index\"] = peaks.index\n",
172+
" peaks.reset_index(level=0, inplace=True)\n",
173+
" peaks_df = peaks_df.append(peaks)\n",
174+
" peaks_df = peaks_df[[\"label\", \"index\", \"Vf\", \"Im\"]]\n",
175+
" # print(\"\\tdetected peaks (mV)\", [int(peak) for peak in data.iloc[peaks].Vf.sort_values().tolist()])\n",
176+
"\n",
177+
"print(\"\\nFile Metadata\")\n",
178+
"print(settings_df.to_string(index=False))\n",
179+
"\n",
180+
"print(\"\\nPeaks Detected\")\n",
181+
"print(peaks_df.to_string(index=False))"
182+
],
183+
"execution_count": null,
184+
"outputs": []
185+
},
186+
{
187+
"cell_type": "code",
188+
"metadata": {
189+
"id": "Ulne80RrpBrW",
190+
"cellView": "form"
191+
},
192+
"source": [
193+
"#@markdown **I-V plot**: Overlay the loaded CyclicVoltammetry Curves\n",
194+
"\n",
195+
"from plotly.subplots import make_subplots\n",
196+
"import plotly.graph_objects as go\n",
197+
"from plotly.colors import DEFAULT_PLOTLY_COLORS\n",
198+
"\n",
199+
"fig = make_subplots(rows=1, cols=1, shared_xaxes=True, vertical_spacing=0.02)\n",
200+
"\n",
201+
"for (index, exp_id) in enumerate(data_df.label.unique()):\n",
202+
" data = data_df.loc[data_df.label == exp_id]\n",
203+
" newTrace = go.Scatter(\n",
204+
" x=data.Vf,\n",
205+
" y=data.Im,\n",
206+
" mode='lines',\n",
207+
" name=exp_id,\n",
208+
" legendgroup=files[index],\n",
209+
" line=dict(color=DEFAULT_PLOTLY_COLORS[index]),\n",
210+
" )\n",
211+
" fig.add_trace(newTrace, row=1, col=1)\n",
212+
" peak = peaks_df.loc[peaks_df.label == exp_id]\n",
213+
" newTrace = go.Scatter(\n",
214+
" x=peak.Vf, y=peak.Im, \n",
215+
" mode=\"markers\", \n",
216+
" showlegend=False, \n",
217+
" marker=dict(size=12,\n",
218+
" color=DEFAULT_PLOTLY_COLORS[index],\n",
219+
" )\n",
220+
" )\n",
221+
" fig.add_trace(newTrace, row=1, col=1)\n",
222+
"\n",
223+
"layout = {\n",
224+
" 'title': {'text': 'Cyclic Voltammetry Overlay',\n",
225+
" 'yanchor': 'top',\n",
226+
" 'y': 0.95,\n",
227+
" 'x': 0.5 },\n",
228+
" 'xaxis': {\n",
229+
" 'anchor': 'x',\n",
230+
" 'title': 'voltage, mV'\n",
231+
" },\n",
232+
" 'yaxis': {\n",
233+
" 'title': 'current, nA',\n",
234+
" 'type': 'linear'\n",
235+
" ''\n",
236+
" },\n",
237+
" 'width': 1200,\n",
238+
" 'height': 500,\n",
239+
" 'margin': dict(l=30, r=20, t=60, b=20),\n",
240+
"}\n",
241+
"fig.update_layout(layout)\n",
242+
"\n",
243+
"config={\n",
244+
" 'displaylogo': False,\n",
245+
" 'modeBarButtonsToRemove': ['select2d', 'lasso2d', 'hoverClosestCartesian', 'toggleSpikelines','hoverCompareCartesian']\n",
246+
"}\n",
247+
"fig.show(config=config)"
248+
],
249+
"execution_count": null,
250+
"outputs": []
251+
}
252+
]
253+
}

demo/notebook_gamry_parser.ipynb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@
3030
"import matplotlib.pyplot as plt\n",
3131
"\n",
3232
"try:\n",
33-
" import gamry_parser as parser\n",
33+
" import gamry_parser\n",
3434
"except:\n",
35-
" !pip install -q --upgrade gamry-parser\n",
36-
" import gamry_parser as parser\n",
35+
" subprocess.run(\n",
36+
" [\"pip\", \"install\", \"gamry-parser\"], \n",
37+
" encoding=\"utf-8\", \n",
38+
" shell=False)\n",
39+
"finally:\n",
40+
" import gamry_parser\n",
3741
"\n",
3842
"p = parser.GamryParser()\n",
3943
" \n",
@@ -163,4 +167,4 @@
163167
"outputs": []
164168
}
165169
]
166-
}
170+
}

0 commit comments

Comments
 (0)