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.
- {autostatlib-0.2.6/src/AutoStatLib.egg-info → autostatlib-0.2.8}/PKG-INFO +3 -2
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/AutoStatLib.py +1 -1
- autostatlib-0.2.8/src/AutoStatLib/StatPlots.py +626 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/__init__.py +1 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/__main__.py +1 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/_version.py +1 -1
- {autostatlib-0.2.6 → autostatlib-0.2.8/src/AutoStatLib.egg-info}/PKG-INFO +3 -2
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/SOURCES.txt +1 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/LICENSE +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/MANIFEST.in +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/README.md +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/pyproject.toml +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/requirements.txt +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/setup.cfg +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/helpers.py +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/normality_tests.py +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/statistical_tests.py +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib/text_formatting.py +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/dependency_links.txt +0 -0
- {autostatlib-0.2.6 → autostatlib-0.2.8}/src/AutoStatLib.egg-info/requires.txt +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: AutoStatLib
|
|
3
|
-
Version: 0.2.
|
|
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
|
|
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,2 @@
|
|
|
1
1
|
# AutoStatLib package version:
|
|
2
|
-
__version__ = "0.2.
|
|
2
|
+
__version__ = "0.2.8"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: AutoStatLib
|
|
3
|
-
Version: 0.2.
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|