AutoStatLib 0.2.6__tar.gz → 0.2.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of AutoStatLib might be problematic. Click here for more details.

Files changed (21) hide show
  1. {autostatlib-0.2.6/src/AutoStatLib.egg-info → autostatlib-0.2.8}/PKG-INFO +3 -2
  2. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/AutoStatLib.py +1 -1
  3. autostatlib-0.2.8/src/AutoStatLib/StatPlots.py +626 -0
  4. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/__init__.py +1 -0
  5. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/__main__.py +1 -0
  6. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/_version.py +1 -1
  7. {autostatlib-0.2.6 → autostatlib-0.2.8/src/AutoStatLib.egg-info}/PKG-INFO +3 -2
  8. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/SOURCES.txt +1 -0
  9. {autostatlib-0.2.6 → autostatlib-0.2.8}/LICENSE +0 -0
  10. {autostatlib-0.2.6 → autostatlib-0.2.8}/MANIFEST.in +0 -0
  11. {autostatlib-0.2.6 → autostatlib-0.2.8}/README.md +0 -0
  12. {autostatlib-0.2.6 → autostatlib-0.2.8}/pyproject.toml +0 -0
  13. {autostatlib-0.2.6 → autostatlib-0.2.8}/requirements.txt +0 -0
  14. {autostatlib-0.2.6 → autostatlib-0.2.8}/setup.cfg +0 -0
  15. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/helpers.py +0 -0
  16. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/normality_tests.py +0 -0
  17. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/statistical_tests.py +0 -0
  18. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/text_formatting.py +0 -0
  19. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/dependency_links.txt +0 -0
  20. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/requires.txt +0 -0
  21. {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: AutoStatLib
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: AutoStatLib - a simple statistical analysis tool
5
5
  Author: Stemonitis, SciWare LLC
6
6
  Author-email: konung-yaropolk <yaropolk1995@gmail.com>
@@ -534,6 +534,7 @@ Requires-Dist: scipy
534
534
  Requires-Dist: statsmodels
535
535
  Requires-Dist: scikit-posthocs
536
536
  Requires-Dist: pandas
537
+ Dynamic: license-file
537
538
 
538
539
  # AutoStatLib - python library for automated statistical analysis
539
540
 
@@ -95,7 +95,7 @@ class StatisticalAnalysis(StatisticalTests, NormalityTests, TextFormatting, Help
95
95
  self.posthoc_name = None
96
96
 
97
97
  self.log('\n' + '-'*67)
98
- self.log('Statistical analysis __init__iated for data in {} groups\n'.format(
98
+ self.log('Statistical analysis initiated for data in {} groups\n'.format(
99
99
  len(self.groups_list)))
100
100
 
101
101
  # adjusting input data type
@@ -0,0 +1,626 @@
1
+ import random
2
+ # from math import comb
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import matplotlib.colors as mcolors
6
+ import matplotlib.colors as color
7
+
8
+
9
+ class Helpers():
10
+
11
+ def colors_to_rgba(self, colors, alpha=0.35):
12
+ rgba_colors = []
13
+ for col in colors:
14
+ rgba = list(mcolors.to_rgba(col))
15
+ rgba[3] = alpha
16
+ rgba_colors.append(tuple(rgba))
17
+ return rgba_colors
18
+
19
+ def get_colors(self, colormap):
20
+ # If a colormap is provided, use it;
21
+ # else generate default one with n_colors colors
22
+ # (the best color combination is 9 imho)
23
+ # but we can change it later
24
+ if colormap:
25
+ colors_edge = [c if color.is_color_like(
26
+ c) else 'k' for c in colormap]
27
+ colors_fill = self.colors_to_rgba(colors_edge)
28
+ else:
29
+ n_colors = 9 # len(self.data_groups)
30
+ cmap = plt.get_cmap('Set1')
31
+ colors_edge = [cmap(i / n_colors) for i in range(n_colors)]
32
+ colors_edge.insert(0, 'k')
33
+ colors_fill = self.colors_to_rgba(colors_edge)
34
+ return colors_edge, colors_fill
35
+
36
+ def make_p_value_printed(self, p) -> str:
37
+ if p is not None:
38
+ if p > 0.99:
39
+ return 'p>0.99'
40
+ elif p >= 0.01:
41
+ return f'p={p:.2g}'
42
+ elif p >= 0.001:
43
+ return f'p={p:.2g}'
44
+ elif p >= 0.0001:
45
+ return f'p={p:.1g}'
46
+ elif p < 0.0001:
47
+ return 'p<0.0001'
48
+ else:
49
+ return 'N/A'
50
+ return 'N/A'
51
+
52
+ def make_stars(self, p) -> int:
53
+ if p is not None:
54
+ if p < 0.0001:
55
+ return 4
56
+ if p < 0.001:
57
+ return 3
58
+ elif p < 0.01:
59
+ return 2
60
+ elif p < 0.05:
61
+ return 1
62
+ else:
63
+ return 0
64
+ return 0
65
+
66
+ def make_stars_printed(self, n) -> str:
67
+ return '*' * n if n else 'ns'
68
+
69
+ def transpose(self, data):
70
+ return list(map(list, zip(*data)))
71
+
72
+
73
+ class BaseStatPlot(Helpers):
74
+
75
+ def __init__(self,
76
+ data_groups,
77
+ p=None,
78
+ testname='',
79
+ dependent=False,
80
+ plot_title='',
81
+ x_label='',
82
+ y_label='',
83
+ print_x_labels=True,
84
+ x_manual_tick_labels=None,
85
+ posthoc_matrix=[],
86
+ colormap=None,
87
+ **kwargs):
88
+ self.data_groups = data_groups
89
+ self.n_groups = len(self.data_groups)
90
+ self.p = p
91
+ self.testname = testname
92
+ self.posthoc_matrix = posthoc_matrix
93
+ self.n_significance_bars = 1
94
+ self.dependent = dependent
95
+ self.plot_title = plot_title
96
+ self.x_label = x_label
97
+ self.y_label = y_label
98
+ self.print_x_labels = print_x_labels
99
+
100
+ # sd sem mean and median calculation if they are not provided
101
+ self.mean = [
102
+ np.mean(self.data_groups[i]).item() for i in range(self.n_groups)]
103
+ self.median = [
104
+ np.median(self.data_groups[i]).item() for i in range(self.n_groups)]
105
+ self.sd = [
106
+ np.std(self.data_groups[i]).item() for i in range(self.n_groups)]
107
+ self.sem = [np.std(self.data_groups[i]).item() / np.sqrt(len(self.data_groups[i])).item()
108
+ for i in range(self.n_groups)]
109
+
110
+ self.n = [len(i) for i in self.data_groups]
111
+ self.p_printed = self.make_p_value_printed(self.p)
112
+ self.stars_printed = self.make_stars_printed(self.make_stars(self.p))
113
+
114
+ self.x_manual_tick_labels = x_manual_tick_labels if x_manual_tick_labels is not None else [
115
+ '']
116
+
117
+ if colormap is not None and colormap != ['']:
118
+ colormap = colormap
119
+ self.colormap_default = False
120
+ else:
121
+ colormap = []
122
+ self.colormap_default = True
123
+ self.colors_edge, self.colors_fill = self.get_colors(colormap)
124
+
125
+ self.y_max = max([max(data) for data in self.data_groups])
126
+
127
+ def setup_figure(self, ):
128
+ fig, ax = plt.subplots(figsize=(0.5 + 0.9 * self.n_groups, 4))
129
+ return fig, ax
130
+
131
+ def add_scatter(self, ax,
132
+ color='k',
133
+ alpha=0.5,
134
+ marker='o',
135
+ linewidth=1,
136
+ zorder=1):
137
+ # Generate x jitter pool.
138
+ spread_pool = [] # storing x positions of data points
139
+ for i, data in enumerate(self.data_groups):
140
+ spread = tuple(random.uniform(-.10, .10) for _ in data)
141
+ spread_pool.append(tuple(i + s for s in spread))
142
+
143
+ for i, data in enumerate(self.transpose(self.data_groups)):
144
+ # Plot individual data points with x jitter.
145
+ ax.plot(self.transpose(spread_pool)[i], data,
146
+ color=color,
147
+ alpha=alpha,
148
+ marker=marker,
149
+ linewidth=linewidth,
150
+ # Connect the data points if desired.
151
+ linestyle='-' if self.dependent else '',
152
+ zorder=zorder)
153
+
154
+ def add_barplot(self, ax, x,
155
+ fill=True,
156
+ linewidth=2,
157
+ zorder=1):
158
+
159
+ # Plot bar for mean
160
+ ax.bar(x, self.mean[x],
161
+ width=0.75,
162
+ facecolor=self.colors_fill[x % len(self.colors_fill)],
163
+ edgecolor=self.colors_edge[x % len(self.colors_edge)],
164
+ fill=fill,
165
+ linewidth=linewidth,
166
+ zorder=zorder)
167
+
168
+ def add_violinplot(self, ax, x,
169
+ linewidth=2,
170
+ widths=0.85,
171
+ vert=True,
172
+ showmeans=True,
173
+ showmedians=True,
174
+ showextrema=True,
175
+ points=200,
176
+ bw_method=0.5):
177
+
178
+ vp = ax.violinplot(self.data_groups[x], positions=[x], widths=widths, vert=vert,
179
+ showmeans=showmeans, showmedians=showmedians, showextrema=showextrema,
180
+ points=points, bw_method=bw_method)
181
+
182
+ for pc in vp['bodies']:
183
+ pc.set_facecolor(self.colors_fill[x % len(self.colors_fill)])
184
+ pc.set_edgecolor(self.colors_edge[x % len(self.colors_edge)])
185
+ pc.set_linewidth(linewidth)
186
+
187
+ def add_boxplot(self, ax,
188
+ # positions of boxes, defaults to range(1,n+1)
189
+ positions=None,
190
+ widths=0.6,
191
+ tickLabels=None,
192
+ notch=False,
193
+ confidences=None,
194
+ fliers=False,
195
+ fliersMarker='',
196
+ flierFillColor=None,
197
+ flierEdgeColor=None,
198
+ flierLineWidth=2,
199
+ flierLineStyle=None,
200
+ vertical=True,
201
+ # whiskers when one float is tukeys parameter, when a pair of percentages,
202
+ # defines the percentiles where the whiskers should be If a float,
203
+ # the lower whisker is at the lowest datum above Q1 - whis*(Q3-Q1),
204
+ # and the upper whisker at the highest datum below Q3 + whis*(Q3-Q1),
205
+ # where Q1 and Q3 are the first and third quartiles. The default value of whis = 1.5
206
+ # corresponds to Tukey's original definition of boxplots.
207
+ whiskers=1.5,
208
+ bootstrap=None,
209
+ whiskersColor=None,
210
+ whiskersLineWidth=2,
211
+ whiskersLineStyle=None,
212
+ showWhiskersCaps=True,
213
+ whiskersCapsWidths=None,
214
+ whiskersCapsColor=None,
215
+ whiskersCapsLineWidth=2,
216
+ whiskersCapsLineStyle=None,
217
+ boxFill=None,
218
+ boxBorderColor=None,
219
+ boxBorderWidth=2,
220
+ userMedians=None,
221
+ medianColor=None,
222
+ medianLineStyle=None,
223
+ medianLineWidth=2,
224
+ showMeans=False,
225
+ meanMarker=None,
226
+ meanFillColor=None,
227
+ meanEdgeColor=None,
228
+ meanLine=False,
229
+ meanLineColor=None,
230
+ meanLineStyle=None,
231
+ meanLineWidth=2,
232
+ autorange=False
233
+ ):
234
+
235
+ positions = list(range(self.n_groups))
236
+ # if (not hasattr(positions, "__len__") or
237
+ # len(positions) != self.length or
238
+ # any(not isinstance(x, (int, float)) for x in positions)):
239
+ # positions = None
240
+ if fliers == False:
241
+ fliersMarker = ""
242
+ else:
243
+ if fliersMarker == "":
244
+ fliersMarker = 'b+'
245
+ # write a function to make a dictionary
246
+ whiskersCapsStyles = dict()
247
+ if whiskersCapsColor != None:
248
+ whiskersCapsStyles["color"] = whiskersCapsColor
249
+ if whiskersCapsLineWidth != None:
250
+ whiskersCapsStyles["linewidth"] = whiskersCapsLineWidth
251
+ if whiskersCapsLineStyle != None:
252
+ whiskersCapsStyles['linestyle'] = whiskersCapsLineStyle
253
+
254
+ boxProps = {"facecolor": (0, 0, 0, 0),
255
+ "edgecolor": "black", "linewidth": 1}
256
+ if boxFill != None:
257
+ boxProps["facecolor"] = boxFill
258
+ if boxBorderColor != None:
259
+ boxProps["edgecolor"] = boxBorderColor
260
+ if boxBorderWidth != None:
261
+ boxProps['linewidth'] = boxBorderWidth
262
+ # if boxBorderStyle != None:
263
+ # boxProps['linestyle'] = boxBorderStyle !!!this feature is not working with patch_artist that is needed for facecolor to work
264
+
265
+ whiskersProps = {"color": 'black',
266
+ "linestyle": "solid", "linewidth": 1}
267
+ if whiskersColor != None:
268
+ whiskersProps["color"] = whiskersColor
269
+ if whiskersLineStyle != None:
270
+ whiskersProps["linestyle"] = whiskersLineStyle
271
+ if whiskersLineWidth != None:
272
+ whiskersProps['linewidth'] = whiskersLineWidth
273
+
274
+ flierProps = {"markerfacecolor": [
275
+ 0, 0, 0, 0], "markeredgecolor": "black", "linestyle": "solid", "markeredgewidth": 1}
276
+ if flierFillColor != None:
277
+ flierProps["markerfacecolor"] = flierFillColor
278
+ if flierEdgeColor != None:
279
+ flierProps["markeredgecolor"] = flierEdgeColor
280
+ if flierLineWidth != None:
281
+ flierProps['markeredgewidth'] = flierLineWidth
282
+ if flierLineStyle != None:
283
+ flierProps['linestyle'] = flierLineStyle
284
+ medianProps = {"linestyle": 'solid', "linewidth": 1, "color": 'red'}
285
+ if medianColor != None:
286
+ medianProps["color"] = medianColor
287
+ if medianLineStyle != None:
288
+ medianProps["linestyle"] = medianLineStyle
289
+ if medianLineWidth != None:
290
+ medianProps['linewidth'] = medianLineWidth
291
+
292
+ meanProps = {"color": "black", "marker": 'o', "markerfacecolor": "black",
293
+ "markeredgecolor": "black", "linestyle": "solid", "linewidth": 1}
294
+
295
+ if meanMarker != None:
296
+ meanProps['marker'] = meanMarker
297
+ if meanFillColor != None:
298
+ meanProps["markerfacecolor"] = meanFillColor
299
+ if meanEdgeColor != None:
300
+ meanProps['markeredgecolor'] = meanEdgeColor
301
+ if meanLineColor != None:
302
+ meanProps["color"] = meanLineColor
303
+ if meanLineStyle != None:
304
+ meanProps['linestyle'] = meanLineStyle
305
+ if meanLineWidth != None:
306
+ meanProps['linewidth'] = meanLineWidth
307
+
308
+ bplot = ax.boxplot(self.data_groups,
309
+ positions=positions,
310
+ widths=widths,
311
+ # tick_labels=tickLabels,
312
+ notch=notch,
313
+ conf_intervals=confidences,
314
+ sym=fliersMarker,
315
+ flierprops=flierProps,
316
+ vert=vertical,
317
+ whis=whiskers,
318
+ whiskerprops=whiskersProps,
319
+ showcaps=showWhiskersCaps,
320
+ capwidths=whiskersCapsWidths,
321
+ capprops=whiskersCapsStyles,
322
+ boxprops=boxProps,
323
+ usermedians=userMedians,
324
+ medianprops=medianProps,
325
+ bootstrap=bootstrap,
326
+ showmeans=showMeans,
327
+ meanline=meanLine,
328
+ meanprops=meanProps,
329
+ autorange=autorange,
330
+ patch_artist=True)
331
+
332
+ # apply use r colormap if provided
333
+ # else left white face with black border
334
+ if not self.colormap_default:
335
+ for x, patch in enumerate(bplot['boxes']):
336
+ patch.set_facecolor(
337
+ self.colors_fill[x % len(self.colors_fill)])
338
+
339
+ def add_errorbar_sd(self, ax, x,
340
+ capsize=8,
341
+ ecolor='r',
342
+ linewidth=2,
343
+ zorder=3):
344
+ # Add error bars
345
+ ax.errorbar(x, self.mean[x],
346
+ yerr=self.sd[x],
347
+ fmt='none',
348
+ capsize=capsize,
349
+ ecolor=ecolor,
350
+ linewidth=linewidth,
351
+ zorder=zorder)
352
+
353
+ def add_errorbar_sem(self, ax, x,
354
+ capsize=8,
355
+ ecolor='r',
356
+ linewidth=2,
357
+ zorder=3):
358
+ # Add error bars
359
+ ax.errorbar(x, self.mean[x],
360
+ yerr=self.sem[x],
361
+ fmt='none',
362
+ capsize=capsize,
363
+ ecolor=ecolor,
364
+ linewidth=linewidth,
365
+ zorder=zorder)
366
+
367
+ def add_mean_marker(self, ax, x,
368
+ marker='_',
369
+ markerfacecolor='#00000000',
370
+ markeredgecolor='r',
371
+ markersize=16,
372
+ markeredgewidth=1):
373
+ # Overlay mean marker
374
+ ax.plot(x, self.mean[x],
375
+ marker=marker,
376
+ markerfacecolor=markerfacecolor,
377
+ markeredgecolor=markeredgecolor,
378
+ markersize=markersize,
379
+ markeredgewidth=markeredgewidth)
380
+
381
+ def add_median_marker(self, ax, x,
382
+ marker='x',
383
+ markerfacecolor='#00000000',
384
+ markeredgecolor='r',
385
+ markersize=10,
386
+ markeredgewidth=1):
387
+ # Overlay median marker
388
+ ax.plot(x, self.median[x],
389
+ marker=marker,
390
+ markerfacecolor=markerfacecolor,
391
+ markeredgecolor=markeredgecolor,
392
+ markersize=markersize,
393
+ markeredgewidth=markeredgewidth)
394
+
395
+ def add_significance_bars(self, ax,
396
+ linewidth=2,
397
+ capsize=0.01,
398
+ col='k',
399
+ label=''):
400
+ '''label can be "p", "s", "both"'''
401
+
402
+ # # Estimate how many bars needed
403
+ # self.n_significance_bars = comb(
404
+ # self.n_groups, 2) if self.n_groups > 2 else 1
405
+
406
+ posthoc_matrix_printed = [[self.make_p_value_printed(element) for element in row]
407
+ for row in self.posthoc_matrix] if self.posthoc_matrix else []
408
+ posthoc_matrix_stars = [[self.make_stars_printed(self.make_stars(element)) for element in row]
409
+ for row in self.posthoc_matrix] if self.posthoc_matrix else []
410
+
411
+ def draw_bar(p, stars, order=0, x1=0, x2=self.n_groups-1, capsize=capsize, linewidth=linewidth, col=col, label=label):
412
+ if label == 'p':
413
+ vspace = capsize+0.03
414
+ label = '{}'.format(p)
415
+ elif label == 's':
416
+ vspace = capsize+0.03
417
+ label = '{}'.format(stars)
418
+ else:
419
+ vspace = capsize+0.06
420
+ label = '{}\n{}'.format(p, stars)
421
+
422
+ # Draw significance bar connecting x1 and x2 coords
423
+ y, h = ((1.05 + (order*vspace)) *
424
+ self.y_max), capsize * self.y_max
425
+ ax.plot([x1, x1, x2, x2], [y, y + h, y + h, y],
426
+ lw=linewidth, c=col)
427
+
428
+ ax.text((x1 + x2) * 0.5, y + h, label,
429
+ ha='center', va='bottom', color=col, fontweight='bold', fontsize=8)
430
+
431
+ def draw_bar_from_posthoc_matrix(x1, x2, o):
432
+ draw_bar(
433
+ posthoc_matrix_printed[x1][x2], posthoc_matrix_stars[x1][x2], order=o, x1=x1, x2=x2)
434
+
435
+ # bars_args= []
436
+ # vshift=[0 for _ in self.data_groups]
437
+
438
+ # for i in range(len(self.posthoc_matrix)):
439
+ # for j in range(i+1, len(self.posthoc_matrix[i])):
440
+ # bars_args.append((i, j, j*3-i*3))
441
+ # for i in bars_args:
442
+ # draw_bar(i[0], i[1], i[2])
443
+
444
+ if (self.p is not None) or (self.posthoc_matrix != []):
445
+ if not self.posthoc_matrix:
446
+ draw_bar(
447
+ self.p_printed, self.stars_printed)
448
+ elif len(self.posthoc_matrix) == 3:
449
+ draw_bar_from_posthoc_matrix(0, 1, 0)
450
+ draw_bar_from_posthoc_matrix(1, 2, 1)
451
+ draw_bar_from_posthoc_matrix(0, 2, 3)
452
+ elif len(self.posthoc_matrix) == 4:
453
+ draw_bar_from_posthoc_matrix(0, 1, 0)
454
+ draw_bar_from_posthoc_matrix(2, 3, 0)
455
+ draw_bar_from_posthoc_matrix(1, 2, 1)
456
+
457
+ draw_bar_from_posthoc_matrix(0, 2, 3)
458
+ draw_bar_from_posthoc_matrix(1, 3, 5)
459
+
460
+ draw_bar_from_posthoc_matrix(0, 3, 7)
461
+
462
+ elif len(self.posthoc_matrix) == 5:
463
+
464
+ draw_bar_from_posthoc_matrix(0, 1, 0)
465
+ draw_bar_from_posthoc_matrix(2, 3, 0)
466
+ draw_bar_from_posthoc_matrix(1, 2, 1)
467
+ draw_bar_from_posthoc_matrix(3, 4, 1)
468
+
469
+ draw_bar_from_posthoc_matrix(0, 2, 4)
470
+ draw_bar_from_posthoc_matrix(2, 4, 5)
471
+ draw_bar_from_posthoc_matrix(1, 3, 8)
472
+
473
+ draw_bar_from_posthoc_matrix(0, 3, 11)
474
+ draw_bar_from_posthoc_matrix(1, 4, 14)
475
+
476
+ draw_bar_from_posthoc_matrix(0, 4, 17)
477
+
478
+ else:
479
+ draw_bar(
480
+ self.p_printed, self.stars_printed)
481
+
482
+ def axes_formatting(self, ax,
483
+ linewidth=2):
484
+ # Remove all spines except left
485
+ for spine in ax.spines.values():
486
+ spine.set_visible(False)
487
+ ax.spines['left'].set_visible(True)
488
+ ax.xaxis.set_visible(bool(self.x_label or self.print_x_labels))
489
+ plt.tight_layout()
490
+
491
+ # Set x ticks and labels
492
+ if self.print_x_labels:
493
+ plt.subplots_adjust(bottom=0.11)
494
+ if self.x_manual_tick_labels != ['']:
495
+ ax.set_xticks(range(self.n_groups))
496
+ ax.set_xticklabels([self.x_manual_tick_labels[i % len(self.x_manual_tick_labels)]
497
+ for i in range(self.n_groups)])
498
+ else:
499
+ ax.set_xticks(range(self.n_groups))
500
+ ax.set_xticklabels(['Group {}'.format(i + 1)
501
+ for i in range(self.n_groups)], fontweight='regular', fontsize=8)
502
+ else:
503
+ plt.subplots_adjust(bottom=0.08)
504
+ ax.tick_params(axis='x', which='both',
505
+ labeltop=False, labelbottom=False)
506
+
507
+ # Additional formatting
508
+ for ytick in ax.get_yticklabels():
509
+ ytick.set_fontweight('bold')
510
+ ax.tick_params(width=linewidth)
511
+ ax.xaxis.set_tick_params(labelsize=10)
512
+ ax.yaxis.set_tick_params(labelsize=12)
513
+ ax.spines['left'].set_linewidth(linewidth)
514
+ ax.tick_params(axis='y', which='both',
515
+ length=linewidth * 2, width=linewidth)
516
+ ax.tick_params(axis='x', which='both', length=0)
517
+
518
+ def add_titles_and_labels(self, fig, ax):
519
+ if self.plot_title:
520
+ ax.set_title(self.plot_title, fontsize=12, fontweight='bold')
521
+ if self.x_label:
522
+ ax.set_xlabel(self.x_label, fontsize=10, fontweight='bold')
523
+ if self.y_label:
524
+ ax.set_ylabel(self.y_label, fontsize=10, fontweight='bold')
525
+ fig.text(0.95, 0.0,
526
+ '{}\nn={}'.format(self.testname,
527
+ str(self.n)[1:-1] if not self.dependent else str(self.n[0])),
528
+ ha='right', va='bottom', fontsize=8, fontweight='regular')
529
+
530
+ def show(self):
531
+ plt.show()
532
+
533
+ def save(self, path):
534
+ plt.savefig(path)
535
+
536
+ def plot(self):
537
+ # Abstract method—each subclass must implement its own plot method
538
+ raise NotImplementedError(
539
+ "Implement the plot() method in the subclass")
540
+
541
+
542
+ class BarStatPlot(BaseStatPlot):
543
+
544
+ def plot(self):
545
+ fig, ax = self.setup_figure()
546
+ linewidth = 2
547
+
548
+ for x in range(len(self.data_groups)):
549
+
550
+ # Create a bar for given group.
551
+ self.add_barplot(ax, x)
552
+
553
+ # Overlay errbars, and markers.
554
+ self.add_median_marker(ax, x)
555
+ self.add_mean_marker(ax, x)
556
+ self.add_errorbar_sd(ax, x)
557
+
558
+ self.add_scatter(ax)
559
+ self.add_significance_bars(ax, linewidth)
560
+ self.add_titles_and_labels(fig, ax)
561
+ self.axes_formatting(ax, linewidth)
562
+
563
+
564
+ class ViolinStatPlot(BaseStatPlot):
565
+ '''
566
+ Violin plot, for adjusting see
567
+ https://matplotlib.org/stable/gallery/statistics/customized_violin.html#sphx-glr-gallery-statistics-customized-violin-py
568
+ https://medium.com/@mohammadaryayi/anything-about-violin-plots-in-matplotlib-ffd58a62bbb5
569
+
570
+ Kernel Density Estimation (violin shape prediction approach)
571
+ https://scikit-learn.org/stable/modules/density.html
572
+
573
+ SeaBorn violins:
574
+ https://seaborn.pydata.org/archive/0.11/generated/seaborn.violinplot.html
575
+ '''
576
+
577
+ def plot(self):
578
+ fig, ax = self.setup_figure()
579
+ linewidth = 2
580
+
581
+ for x in range(len(self.data_groups)):
582
+
583
+ # Create a violin for given group.
584
+ self.add_violinplot(ax, x)
585
+
586
+ # Overlay errbars and markers.
587
+ self.add_median_marker(ax, x)
588
+ self.add_mean_marker(ax, x)
589
+ # self.add_errorbar_sd(ax, x)
590
+
591
+ self.add_scatter(ax)
592
+ self.add_significance_bars(ax, linewidth)
593
+ self.add_titles_and_labels(fig, ax)
594
+ self.axes_formatting(ax, linewidth)
595
+
596
+
597
+ class BoxStatPlot(BaseStatPlot):
598
+
599
+ def plot(self):
600
+ fig, ax = self.setup_figure()
601
+ linewidth = 2
602
+
603
+ self.add_boxplot(ax)
604
+ self.add_scatter(ax)
605
+ self.add_significance_bars(ax, linewidth)
606
+ self.add_titles_and_labels(fig, ax)
607
+ self.axes_formatting(ax, linewidth)
608
+
609
+
610
+ class ScatterStatPlot(BaseStatPlot):
611
+
612
+ def plot(self):
613
+ fig, ax = self.setup_figure()
614
+ linewidth = 2
615
+
616
+ for x in range(len(self.data_groups)):
617
+
618
+ # Overlay errbars, and markers.
619
+ self.add_median_marker(ax, x)
620
+ self.add_mean_marker(ax, x)
621
+ self.add_errorbar_sd(ax, x)
622
+
623
+ self.add_scatter(ax)
624
+ self.add_significance_bars(ax, linewidth)
625
+ self.add_titles_and_labels(fig, ax)
626
+ self.axes_formatting(ax, linewidth)
@@ -1,2 +1,3 @@
1
1
  from AutoStatLib.AutoStatLib import StatisticalAnalysis
2
+ from AutoStatLib.StatPlots import *
2
3
  from AutoStatLib._version import __version__
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  from AutoStatLib.AutoStatLib import StatisticalAnalysis
3
+ from AutoStatLib.StatPlots import *
3
4
  from AutoStatLib._version import __version__
4
5
 
5
6
  if __name__ == '__main__':
@@ -1,2 +1,2 @@
1
1
  # AutoStatLib package version:
2
- __version__ = "0.2.6"
2
+ __version__ = "0.2.8"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: AutoStatLib
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: AutoStatLib - a simple statistical analysis tool
5
5
  Author: Stemonitis, SciWare LLC
6
6
  Author-email: konung-yaropolk <yaropolk1995@gmail.com>
@@ -534,6 +534,7 @@ Requires-Dist: scipy
534
534
  Requires-Dist: statsmodels
535
535
  Requires-Dist: scikit-posthocs
536
536
  Requires-Dist: pandas
537
+ Dynamic: license-file
537
538
 
538
539
  # AutoStatLib - python library for automated statistical analysis
539
540
 
@@ -4,6 +4,7 @@ README.md
4
4
  pyproject.toml
5
5
  requirements.txt
6
6
  src/AutoStatLib/AutoStatLib.py
7
+ src/AutoStatLib/StatPlots.py
7
8
  src/AutoStatLib/__init__.py
8
9
  src/AutoStatLib/__main__.py
9
10
  src/AutoStatLib/_version.py
File without changes
File without changes
File without changes
File without changes
File without changes