GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Failed
Push — master ( 69efa7...3bf7f5 )
by Sabiha
02:48 queued 11s
created

voltcycle.core.peak_detection_fxn()   A

Complexity

Conditions 1

Size

Total Lines 55
Code Lines 14

Duplication

Lines 55
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nop 1
dl 55
loc 55
rs 9.7
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
"""This module consists of all the functions utilized."""
2
# This is a tool to automate cyclic voltametry analysis.
3
# Current Version = 1
4
5
import copy
6
import pandas as pd
7
import numpy as np
8
import matplotlib.pyplot as plt
9
import peakutils
10
11
12
def read_cycle(data):
13
    """This function reads a segment of datafile (corresponding a cycle)
14
    and generates a dataframe with columns 'Potential' and 'Current'
15
16
    Parameters
17
    __________
18
    data: segment of data file
19
    Returns
20
    _______
21
    A dataframe with potential and current columns
22
    """
23
24
    current = []
25
    potential = []
26
    for i in data[3:]:
27
        current.append(float(i.split("\t")[4]))
28
        potential.append(float(i.split("\t")[3]))
29
<<<<<<< HEAD
30
    zippedList = list(zip(potential, current))
31
    df = pd.DataFrame(zippedList, columns=['Potential', 'Current'])
32
    return df
33
=======
34
    zipped_list = list(zip(potential, current))
35
    dataframe = pd.DataFrame(zipped_list, columns=['Potential', 'Current'])
36
    return dataframe
37
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
38
39
40
def read_file_dash(lines):
41
    """This function is exactly similar to read_file, but it is for dash
42
43
    Parameters
44
    __________
45
    file: lines from dash input file
46
47
    Returns:
48
    ________
49
    dict_of_df: dictionary of dataframes with keys = cycle numbers and
50
    values = dataframes for each cycle
51
    n_cycle: number of cycles in the raw file
52
    """
53
    dict_of_df = {}
54
<<<<<<< HEAD
55
    h = 0
56
    j = 0
57
=======
58
    h_val = 0
59
    l_val = 0
60
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
61
    n_cycle = 0
62
    number = 0
63
    # a = []
64
    # with open(file, 'rt') as f:
65
    #    print(file + ' Opened')
66
    for line in lines:
67
        record = 0
68
<<<<<<< HEAD
69
        if not (h and j):
70
=======
71
        if not (h_val and l_val):
72
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
73
            if line.startswith('SCANRATE'):
74
                scan_rate = float(line.split()[2])
75
                h_val = 1
76
            if line.startswith('STEPSIZE'):
77
                step_size = float(line.split()[2])
78
<<<<<<< HEAD
79
                j = 1
80
=======
81
                l_val = 1
82
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
83
        if line.startswith('CURVE'):
84
            n_cycle += 1
85
            if n_cycle > 1:
86
                number = n_cycle - 1
87
                data = read_cycle(a_val)
88
                key_name = 'cycle_' + str(number)
89
<<<<<<< HEAD
90
                # key_name = number
91
                dict_of_df[key_name] = copy.deepcopy(df)
92
            a = []
93
=======
94
                #key_name = number
95
                dict_of_df[key_name] = copy.deepcopy(data)
96
            a_val = []
97
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
98
        if n_cycle:
99
            a_val.append(line)
100
    return dict_of_df, number
101
102
103
def read_file(file):
104
    """This function reads the raw data file, gets the scanrate and stepsize
105
    and then reads the lines according to cycle number. Once it reads the data
106
<<<<<<< HEAD
107
    for one cycle, it calls read_cycle function to generate a dataframe. It
108
=======
109
    for one cycle, it calls read_cycle function to denerate a dataframe. It
110
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
111
    does the same thing for all the cycles and finally returns a dictionary,
112
    the keys of which are the cycle numbers and the values are the
113
    corresponding dataframes.
114
115
    Parameters
116
    __________
117
    file: raw data file
118
119
    Returns:
120
    ________
121
    dict_of_df: dictionary of dataframes with keys = cycle numbers and
122
    values = dataframes for each cycle
123
    n_cycle: number of cycles in the raw file
124
    """
125
    dict_of_df = {}
126
<<<<<<< HEAD
127
    h = 0
128
    j = 0
129
    n_cycle = 0
130
    # a = []
131
    with open(file, 'rt') as f:
132
=======
133
    h_val = 0
134
    l_val = 0
135
    n_cycle = 0
136
    #a = []
137
    with open(file, 'rt') as f_val:
138
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
139
        print(file + ' Opened')
140
        for line in f_val:
141
            record = 0
142
            if not (h_val and l_val):
143
                if line.startswith('SCANRATE'):
144
                    scan_rate = float(line.split()[2])
145
                    h_val = 1
146
                if line.startswith('STEPSIZE'):
147
                    step_size = float(line.split()[2])
148
<<<<<<< HEAD
149
                    j = 1
150
=======
151
                    l_val = 1
152
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
153
            if line.startswith('CURVE'):
154
                n_cycle += 1
155
                if n_cycle > 1:
156
                    number = n_cycle - 1
157
                    data = read_cycle(a_val)
158
                    key_name = 'cycle_' + str(number)
159
<<<<<<< HEAD
160
                    # key_name = number
161
                    dict_of_df[key_name] = copy.deepcopy(df)
162
                a = []
163
=======
164
                    #key_name = number
165
                    dict_of_df[key_name] = copy.deepcopy(data)
166
                a_val = []
167
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
168
            if n_cycle:
169
                a_val.append(line)
170
    return dict_of_df, number
171
# df = pd.DataFrame(list(dict1['df_1'].items()))
172
# list1, list2 = list(dict1['df_1'].items())
173
# list1, list2 = list(dict1.get('df_'+str(1)))
174
175
<<<<<<< HEAD
176
177
def data_frame(dict_cycle, n):
178
=======
179
#df = pd.DataFrame(list(dict1['df_1'].items()))
180
#list1, list2 = list(dict1['df_1'].items())
181
#list1, list2 = list(dict1.get('df_'+str(1)))
182
183
184
def data_frame(dict_cycle, number):
185
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
186
    """Reads the dictionary of dataframes and returns dataframes for each cycle
187
188
    Parameters
189
    __________
190
    dict_cycle: Dictionary of dataframes
191
    n: cycle number
192
193
    Returns:
194
    _______
195
    Dataframe correcponding to the cycle number
196
    """
197
<<<<<<< HEAD
198
    list1, list2 = (list(dict_cycle.get('cycle_'+str(n)).items()))
199
    zippedList = list(zip(list1[1], list2[1]))
200
    data = pd.DataFrame(zippedList, columns=['Potential', 'Current'])
201
=======
202
    list1, list2 = (list(dict_cycle.get('cycle_'+str(number)).items()))
203
    zipped_list = list(zip(list1[1], list2[1]))
204
    data = pd.DataFrame(zipped_list, columns=['Potential', 'Current'])
205
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
206
    return data
207
208
209
def plot_fig(dict_cycle, number):
210
    """For basic plotting of the cycle data
211
212
    Parameters
213
    __________
214
    dict: dictionary of dataframes for all the cycles
215
    n: number of cycles
216
217
    Saves the plot in a file called cycle.png
218
    """
219
220
    for i in range(number):
221
        print(i+1)
222
<<<<<<< HEAD
223
        df = data_frame(dict_cycle, i+1)
224
        plt.plot(df.Potential, df.Current, label="Cycle{}".format(i+1))
225
226
    # print(df.head())
227
=======
228
        data = data_frame(dict_cycle, i+1)
229
        plt.plot(data.Potential, data.Current, label="Cycle{}".format(i+1))
230
231
    print(data.head())
232
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
233
    plt.xlabel('Voltage')
234
    plt.ylabel('Current')
235
    plt.legend()
236
    plt.savefig('cycle.png')
237
    print('executed')
238
239
240
# split forward and backward sweping data, to make it easier for processing.
241
def split(vector):
242
    """
243
    This function takes an array and splits it into equal two half.
244
    ----------
245
    Parameters
246
    ----------
247
    vector : Can be in any form of that can be turned into numpy array.
248
    Normally, for the use of this function, it expects pandas DataFrame column.
249
    For example, df['potentials'] could be input as the column of x data.
250
    -------
251
    Returns
252
    -------
253
    This function returns two equally splited vector.
254
<<<<<<< HEAD
255
    The output then can be used to ease the implementation
256
    of peak detection and baseline finding.
257
    """
258
    (assert type(vector) == pd.core.series.Series,
259
        "Input of the function should be pandas series")
260
    split = int(len(vector)/2)
261
=======
262
    The output then can be used to ease the implementation of peak detection and baseline finding.
263
    """
264
    assert isinstance(vector, pd.core.series.Series), "Input should be pandas series"
265
    split_top = int(len(vector)/2)
266
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
267
    end = int(len(vector))
268
    vector1 = np.array(vector)[0:split]
269
    vector2 = np.array(vector)[split_top:end]
270
    return vector1, vector2
271
272
273
<<<<<<< HEAD
274
def critical_idx(x, y):  # Finds index where data set is no longer linear
275
    """
276
    This function takes x and y values callculate the derrivative
277
    of x and y, and calculate moving average of 5 and 15 points.
278
    Finds intercepts of different moving average curves and
279
    return the indexs of the first intercepts.
280
=======
281
def critical_idx(arr_x, arr_y): ## Finds index where data set is no longer linear
282
    """
283
    This function takes x and y values callculate the derrivative of x and y,
284
    and calculate moving average of 5 and 15 points. Finds intercepts of different
285
    moving average curves and return the indexs of the first intercepts.
286
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
287
    ----------
288
    Parameters
289
    ----------
290
    x : Numpy array.
291
    y : Numpy array.
292
<<<<<<< HEAD
293
    Normally, for the use of this function, it expects
294
    numpy array that came out from split function.
295
    For example, output of split.df['potentials']
296
    could be input for this function as x.
297
    -------
298
    Returns
299
    -------
300
    This function returns 5th index of the intercepts
301
    of different moving average curves.
302
    User can change this function according to
303
    baseline branch method 2 to get various indexes.
304
    """
305
    (assert type(x) == np.ndarray,
306
     "Input of the function should be numpy array")
307
    (assert type(y) == np.ndarray,
308
     "Input of the function should be numpy array")
309
    if x.shape[0] != y.shape[0]:
310
        raise ValueError("x and y must have same first dimension, but "
311
                         "have shapes {} and {}".format(x.shape, y.shape))
312
    k = np.diff(y)/(np.diff(x))  # calculated slops of x and y
313
    # Calculate moving average for 10 and 15 points.
314
    # This two arbitrary number can be tuned to get better fitting.
315
    ave10 = []
316
    ave15 = []
317
    for i in range(len(k)-10):
318
        # The reason to minus 10 is to prevent j from running out of index.
319
        a = 0
320
        for j in range(0, 5):
321
            a = a + k[i+j]
322
        ave10.append(round(a/10, 5))
323
    # keeping 5 desimal points for more accuracy
324
    # This numbers affect how sensitive to noise.
325
    for i in range(len(k)-15):
326
        b = 0
327
        for j in range(0, 15):
328
            b = b + k[i+j]
329
        ave15.append(round(b/15, 5))
330
    ave10i = np.asarray(ave10)
331
    ave15i = np.asarray(ave15)
332
    # Find intercepts of different moving average curves
333
    # reshape into one row.
334
    idx = {np.argwhere(np.diff(np.sign(ave15i -
335
                               ave10i[:len(ave15i)]) != 0)).reshape(-1) + 0}
336
=======
337
    Normally, for the use of this function, it expects numpy array
338
    that came out from split function. For example, output of
339
    split.df['potentials'] could be input for this function as x.
340
    -------
341
    Returns
342
    -------
343
    This function returns 5th index of the intercepts of different moving average curves.
344
    User can change this function according to baseline
345
    branch method 2 to get various indexes..
346
    """
347
    assert isinstance(arr_x, np.ndarray), "Input should be numpy array"
348
    assert isinstance(arr_y == np.ndarray), "Input should be numpy array"
349
    if arr_x.shape[0] != arr_y.shape[0]:
350
        raise ValueError("x and y must have same first dimension, but "
351
                         "have shapes {} and {}".format(arr_x.shape, arr_y.shape))
352
    k_val = np.diff(arr_y)/(np.diff(arr_x)) #calculated slops of x and y
353
    ## Calculate moving average for 10 and 15 points.
354
    ## This two arbitrary number can be tuned to get better fitting.
355
    ave10 = []
356
    ave15 = []
357
    for i in range(len(k_val)-10):
358
        # The reason to minus 10 is to prevent j from running out of index.
359
        a_val = 0
360
        for j in range(0, 5):
361
            a_val = a_val + k_val[i+j]
362
        ave10.append(round(a_val/10, 5))
363
        # keeping 5 desimal points for more accuracy
364
        # This numbers affect how sensitive to noise.
365
    for i in range(len(k_val)-15):
366
        b_val = 0
367
        for j in range(0, 15):
368
            b_val = b_val + k_val[i+j]
369
        ave15.append(round(b_val/15, 5))
370
    ave10i = np.asarray(ave10)
371
    ave15i = np.asarray(ave15)
372
    ## Find intercepts of different moving average curves
373
    #reshape into one row.
374
    idx = np.argwhere(np.diff(np.sign(ave15i - ave10i[:len(ave15i)]) != 0)).reshape(-1)+0
375
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
376
    return idx[5]
377
# This is based on the method 1 where user can't choose the baseline.
378
# If wanted to add that, choose method2.
379
380
381
def sum_mean(vector):
382
    """
383
    This function returns the mean and sum of the given vector.
384
    ----------
385
    Parameters
386
    ----------
387
    vector : Can be in any form of that can be turned into numpy array.
388
    Normally, for the use of this function, it expects pandas DataFrame column.
389
    For example, df['potentials'] could be input as the column of x data.
390
    """
391
<<<<<<< HEAD
392
    (assert type(vector) == np.ndarray,
393
     "Input of the function should be numpy array")
394
    a = 0
395
    for i in vector:
396
        a = a + i
397
    return [a, a/len(vector)]
398
=======
399
    assert isinstance(vector == np.ndarray), "Input should be numpy array"
400
    a_val = 0
401
    for i in vector:
402
        a_val = a_val + i
403
    return [a_val, a_val/len(vector)]
404
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
405
406
407
def multiplica(vector_x, vector_y):
408
    """
409
    This function returns the sum of the multilica of two given vector.
410
    ----------
411
    Parameters
412
    ----------
413
    vector_x, vector_y : Output of the split vector function.
414
    Two inputs can be the same vector or different vector with same length.
415
    -------
416
    Returns
417
    -------
418
    This function returns a number that is the sum
419
    of multiplicity of given two vector.
420
    """
421
<<<<<<< HEAD
422
    (assert type(vector_x) == np.ndarray,
423
     "Input of the function should be numpy array")
424
    (assert type(vector_y) == np.ndarray,
425
     "Input of the function should be numpy array")
426
    a = 0
427
    for x, y in zip(vector_x, vector_y):
428
        a = a + (x * y)
429
    return a
430
431
432
def linear_coeff(x, y):
433
    """
434
    This function returns the inclination coeffecient and
435
    y axis interception coeffecient m and b.
436
=======
437
    assert isinstance(vector_x == np.ndarray), "Input should be numpy array"
438
    assert isinstance(vector_y == np.ndarray), "Input should be numpy array"
439
    a_val = 0
440
    for vec_x, vec_y in zip(vector_x, vector_y):
441
        a_val = a_val + (vec_x * vec_y)
442
    return a_val
443
444
445
def linear_coeff(vec_x, vec_y):
446
    """
447
    This function returns the inclination coeffecient and y axis interception coeffecient m and b.
448
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
449
    ----------
450
    Parameters
451
    ----------
452
    x : Output of the split vector function.
453
    y : Output of the split vector function.
454
    -------
455
    Returns
456
    -------
457
    float number of m and b.
458
    """
459
<<<<<<< HEAD
460
    m = {(multiplica(x, y) - sum_mean(x)[0] * sum_mean(y)[1]) /
461
         (multiplica(x, x) - sum_mean(x)[0] * sum_mean(x)[1])}
462
    b = sum_mean(y)[1] - m * sum_mean(x)[1]
463
    return m, b
464
=======
465
    m_val = ((multiplica(vec_x, vec_y) - sum_mean(vec_x)[0] * sum_mean(vec_y)[1])/
466
             (multiplica(vec_x, vec_x) - sum_mean(vec_x)[0] * sum_mean(vec_x)[1]))
467
    b_val = sum_mean(vec_y)[1] - m_val * sum_mean(vec_x)[1]
468
    return m_val, b_val
469
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
470
471
472
def y_fitted_line(m_val, b_val, vec_x):
473
    """
474
<<<<<<< HEAD
475
    This function returns the fitted baseline constructed
476
    by coeffecient m and b and x values.
477
=======
478
    This function returns the fitted baseline constructed by coeffecient m and b and x values.
479
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
480
    ----------
481
    Parameters
482
    ----------
483
    x : Output of the split vector function. x value of the input.
484
    m : inclination of the baseline.
485
    b : y intercept of the baseline.
486
    -------
487
    Returns
488
    -------
489
    List of constructed y_labels.
490
    """
491
    y_base = []
492
    for i in vec_x:
493
        y_val = m_val * i + b_val
494
        y_base.append(y_val)
495
    return y_base
496
497
498
def linear_background(vec_x, vec_y):
499
    """
500
    This function is wrapping function for calculating linear fitted line.
501
    It takes x and y values of the cv data, returns the fitted baseline.
502
    ----------
503
    Parameters
504
    ----------
505
<<<<<<< HEAD
506
    x : Output of the split vector function. x value
507
    of the cyclic voltammetry data.
508
    y : Output of the split vector function. y value
509
    of the cyclic voltammetry data.
510
=======
511
    x : Output of the split vector function. x value of the cyclic voltammetry data.
512
    y : Output of the split vector function. y value of the cyclic voltammetry data.
513
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
514
    -------
515
    Returns
516
    -------
517
    List of constructed y_labels.
518
    """
519
<<<<<<< HEAD
520
    assert type(x) == np.ndarray, "Input of the function should be numpy array"
521
    assert type(y) == np.ndarray, "Input of the function should be numpy array"
522
    idx = critical_idx(x, y) + 5
523
    # this is also arbitrary number we can play with.
524
    m, b = {linear_coeff(x[(idx - int(0.5 * idx)): (idx + int(0.5 * idx))],
525
                         y[(idx - int(0.5 * idx)): (idx + int(0.5 * idx))])}
526
    y_base = y_fitted_line(m, b, x)
527
=======
528
    assert isinstance(vec_x, np.ndarray), "Input of the function should be numpy array"
529
    assert isinstance(vec_y, np.ndarray), "Input of the function should be numpy array"
530
    idx = critical_idx(vec_x, vec_y) + 5 #this is also arbitrary number we can play with.
531
    m_val, b_val = (linear_coeff(vec_x[(idx - int(0.5 * idx)) : (idx + int(0.5 * idx))],
532
                                 vec_y[(idx - int(0.5 * idx)) : (idx + int(0.5 * idx))]))
533
    y_base = y_fitted_line(m_val, b_val, vec_x)
534
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
535
    return y_base
536
537
538
def peak_detection_fxn(data_y):
539
    """The function takes an input of the column containing the y variables in
540
    the dataframe, associated with the current. The function calls the split
541
    function, which splits the column into two arrays, one of the positive and
542
    one of the negative values. This is because cyclic voltammetry delivers
543
    negative peaks,but the peakutils function works better with positive peaks.
544
    The function also runs on the middle 80% of data to eliminate unnecessary
545
    noise and messy values associated with pseudo-peaks.The vectors are then
546
    imported into the peakutils. Indexes function to determine the significant
547
    peak for each array. The values are stored in a list, with the first index
548
    corresponding to the top peak and the second corresponding to the bottom
549
    peak.
550
    Parameters
551
    ______________
552
    y column: must be a column from a pandas dataframe
553
554
    Returns
555
    _____________
556
    A list with the index of the peaks from the top curve and bottom curve.
557
    """
558
559
    # initialize storage list
560
    index_list = []
561
562
    # split data into above and below the baseline
563
    col_y1, col_y2 = split(data_y)  # removed main. head.
564
565
    # detemine length of data and what 10% of the data is
566
    len_y = len(col_y1)
567
    ten_percent = int(np.around(0.1*len_y))
568
569
    # adjust both input columns to be the middle 80% of data
570
    # (take of the first and last 10% of data)
571
    # this avoid detecting peaks from electrolysis
572
    # (from water splitting and not the molecule itself,
573
    # which can form random "peaks")
574
    mod_col_y2 = col_y2[ten_percent:len_y-ten_percent]
575
    mod_col_y1 = col_y1[ten_percent:len_y-ten_percent]
576
577
    # run peakutils package to detect the peaks for both top and bottom
578
    peak_top = peakutils.indexes(mod_col_y2, thres=0.99, min_dist=20)
579
    peak_bottom = peakutils.indexes(abs(mod_col_y1), thres=0.99, min_dist=20)
580
581
    # detemine length of both halves of data
582
    len_top = len(peak_top)
583
    len_bot = len(peak_bottom)
584
585
    # append the values to the storage list
586
    # manipulate values by adding the ten_percent value back
587
    # (as the indecies have moved)
588
    # to detect the actual peaks and not the modified values
589
    index_list.append(peak_top[int(len_top/2)]+ten_percent)
590
    index_list.append(peak_bottom[int(len_bot/2)]+ten_percent)
591
592
    # return storage list
593
    # first value is the top, second value is the bottom
594
    return index_list
595
596
def peak_values(dataframe_x, dataframe_y):
597
    """Outputs x (potentials) and y (currents) values from data indices
598
        given by peak_detection function.
599
600
       ----------
601
       Parameters
602
       ----------
603
       DataFrame_x : should be in the form of a pandas DataFrame column.
604
         For example, df['potentials'] could be input as the column of x
605
         data.
606
607
        DataFrame_y : should be in the form of a pandas DataFrame column.
608
          For example, df['currents'] could be input as the column of y
609
          data.
610
611
       Returns
612
       -------
613
       Result : numpy array of coordinates at peaks in the following order:
614
         potential of peak on top curve, current of peak on top curve,
615
         potential of peak on bottom curve, current of peak on bottom curve"""
616
    index = peak_detection_fxn(dataframe_y)
617
    potential1, potential2 = split(dataframe_x)
618
    current1, current2 = split(dataframe_y)
619
    peak_values = []
620
    peak_values.append(potential2[(index[0])])  # TOPX (bottom part of curve is
621
    # the first part of DataFrame)
622
    peak_values.append(current2[(index[0])])  # TOPY
623
    peak_values.append(potential1[(index[1])])  # BOTTOMX
624
    peak_values.append(current1[(index[1])])  # BOTTOMY
625
    peak_array = np.array(peak_values)
626
    return peak_array
627
628
629
def del_potential(dataframe_x, dataframe_y):
630
    """Outputs the difference in potentials between anoidc and
631
       cathodic peaks in cyclic voltammetry data.
632
633
       Parameters
634
       ----------
635
       DataFrame_x : should be in the form of a pandas DataFrame column.
636
         For example, df['potentials'] could be input as the column of x
637
         data.
638
639
        DataFrame_y : should be in the form of a pandas DataFrame column.
640
          For example, df['currents'] could be input as the column of y
641
          data.
642
643
        Returns
644
        -------
645
        Results: difference in peak potentials."""
646
    del_potentials = (peak_values(dataframe_x, dataframe_y)[0] -
647
                      peak_values(dataframe_x, dataframe_y)[2])
648
    return del_potentials
649
650
651
def half_wave_potential(dataframe_x, dataframe_y):
652
    """Outputs the half wave potential(redox potential) from cyclic
653
       voltammetry data.
654
655
       Parameters
656
       ----------
657
       DataFrame_x : should be in the form of a pandas DataFrame column.
658
         For example, df['potentials'] could be input as the column of x
659
         data.
660
661
        DataFrame_y : should be in the form of a pandas DataFrame column.
662
          For example, df['currents'] could be input as the column of y
663
          data.
664
665
       Returns
666
       -------
667
       Results : the half wave potential."""
668
    half_wave_pot = (del_potential(dataframe_x, dataframe_y))/2
669
    return half_wave_pot
670
671
672
def peak_heights(dataframe_x, dataframe_y):
673
    """Outputs heights of minimum peak and maximum
674
         peak from cyclic voltammetry data.
675
676
       Parameters
677
       ----------
678
       DataFrame_x : should be in the form of a pandas DataFrame column.
679
         For example, df['potentials'] could be input as the column of x
680
         data.
681
682
        DataFrame_y : should be in the form of a pandas DataFrame column.
683
          For example, df['currents'] could be input as the column of y
684
          data.
685
686
        Returns
687
        -------
688
        Results: height of maximum peak, height of minimum peak
689
          in that order in the form of a list."""
690
    current_max = peak_values(dataframe_x, dataframe_y)[1]
691
    current_min = peak_values(dataframe_x, dataframe_y)[3]
692
    col_x1, col_x2 = split(dataframe_x)
693
    col_y1, col_y2 = split(dataframe_y)
694
    line_at_min = linear_background(col_x1, col_y1)[peak_detection_fxn(dataframe_y)[1]]
695
    line_at_max = linear_background(col_x2, col_y2)[peak_detection_fxn(dataframe_y)[0]]
696
    height_of_max = current_max - line_at_max
697
    height_of_min = abs(current_min - line_at_min)
698
    return [height_of_max, height_of_min]
699
700
701
def peak_ratio(dataframe_x, dataframe_y):
702
    """Outputs the peak ratios from cyclic voltammetry data.
703
704
       Parameters
705
       ----------
706
       DataFrame_x : should be in the form of a pandas DataFrame column.
707
         For example, df['potentials'] could be input as the column of x
708
         data.
709
710
        DataFrame_y : should be in the form of a pandas DataFrame column.
711
          For example, df['currents'] could be input as the column of y
712
          data.
713
714
       Returns
715
       -------
716
       Result : returns a the peak ratio."""
717
    ratio = (peak_heights(dataframe_x, dataframe_y)[0] /
718
             peak_heights(dataframe_x, dataframe_y)[1])
719
    return ratio
720
721
722
def data_analysis(data):
723
    """This function returns a dictionary consisting of
724
    the relevant values. This can be seen in the user
725
    interface (Dash) as well."""
726
    results_dict = {}
727
728
    # df = main.data_frame(dict_1,1)
729
    x_val = data['Potential']
730
    y_val = data['Current']
731
    # Peaks are here [list]
732
    peak_index = peak_detection_fxn(y_val)
733
    # Split x,y to get baselines
734
<<<<<<< HEAD
735
    x1, x2 = core.split(x)
736
    y1, y2 = core.split(y)
737
    y_base1 = core.linear_background(x1, y1)
738
    y_base2 = core.linear_background(x2, y2)
739
    # Calculations based on baseline and peak
740
    values = core.peak_values(x, y)
741
    Et = values[0]
742
    Eb = values[2]
743
    dE = core.del_potential(x, y)
744
    half_E = min(Et, Eb) + core.half_wave_potential(x, y)
745
    ia = core.peak_heights(x, y)[0]
746
    ic = core.peak_heights(x, y)[1]
747
    ratio_i = core.peak_ratio(x, y)
748
    results_dict['Peak Current Ratio'] = ratio_i
749
    results_dict['Ipc (A)'] = ic
750
    results_dict['Ipa (A)'] = ia
751
    results_dict['Epc (V)'] = Eb
752
    results_dict['Epa (V)'] = Et
753
    results_dict['∆E (V)'] = dE
754
    results_dict['Redox Potential (V)'] = half_E
755
    if dE > 0.3:
756
=======
757
    col_x1, col_x2 = split(x_val)
758
    col_y1, col_y2 = split(y_val)
759
    y_base1 = linear_background(col_x1, col_y1)
760
    y_base2 = linear_background(col_x2, col_y2)
761
    # Calculations based on baseline and peak
762
    values = peak_values(x_val, y_val)
763
    esub_t = values[0]
764
    esub_b = values[2]
765
    dof_e = del_potential(x_val, y_val)
766
    half_e = min(esub_t, esub_b) + half_wave_potential(x_val, y_val)
767
    ipa = peak_heights(x_val, y_val)[0]
768
    ipc = peak_heights(x_val, y_val)[1]
769
    ratio_i = peak_ratio(x_val, y_val)
770
    results_dict['Peak Current Ratio'] = ratio_i
771
    results_dict['Ipc (A)'] = ipc
772
    results_dict['Ipa (A)'] = ipa
773
    results_dict['Epc (V)'] = esub_b
774
    results_dict['Epa (V)'] = esub_t
775
    results_dict['∆E (V)'] = dof_e
776
    results_dict['Redox Potential (V)'] = half_e
777
    if dof_e > 0.3:
778
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
779
        results_dict['Reversible'] = 'No'
780
    else:
781
        results_dict['Reversible'] = 'Yes'
782
783
<<<<<<< HEAD
784
    if half_E > 0 and 'Yes' in results_dict.values():
785
        results_dict['Type'] = 'Catholyte'
786
    elif 'Yes' in results_dict.values():
787
        results_dict['Type'] = 'Anolyte'
788
    return results_dict, x1, x2, y1, y2, y_base1, y_base2, peak_index
789
    # return results_dict
790
=======
791
    if half_e > 0 and  'Yes' in results_dict.values():
792
        results_dict['Type'] = 'Catholyte'
793
    elif 'Yes' in results_dict.values():
794
        results_dict['Type'] = 'Anolyte'
795
    return results_dict, col_x1, col_x2, col_y1, col_y2, y_base1, y_base2, peak_index
796
    #return results_dict
797
>>>>>>> 69efa78d566312fbfc5e8d5b130e3e2bf7cbb2be
798