CBACABLab 0.0.1__py3-none-any.whl
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.
- CBACABLab/LikertPrep.py +102 -0
- CBACABLab/LoadRawData.py +251 -0
- CBACABLab/LoadSQLData.py +311 -0
- CBACABLab/Plots.py +363 -0
- CBACABLab/Util.py +55 -0
- CBACABLab/__init__.py +5 -0
- CBACABLab/settings.json +193 -0
- cbacablab-0.0.1.dist-info/METADATA +46 -0
- cbacablab-0.0.1.dist-info/RECORD +11 -0
- cbacablab-0.0.1.dist-info/WHEEL +4 -0
- cbacablab-0.0.1.dist-info/licenses/LICENSE +21 -0
CBACABLab/Plots.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
from itertools import cycle
|
|
2
|
+
import numpy as np
|
|
3
|
+
import re
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import statsmodels.api as sm
|
|
6
|
+
import seaborn as sns
|
|
7
|
+
|
|
8
|
+
CalculateDistance = lambda x, y: np.abs(x-y)
|
|
9
|
+
EpanechnikovWeight = lambda d: (3/4)*(1-np.power(d,2))
|
|
10
|
+
Silvermans = lambda x: (np.power((4*np.power(x.std(),5))/(3*x.count()),1/5))
|
|
11
|
+
CalculateWeight = lambda d: (70/81)*np.power((1-np.power(np.abs(d),3)),3)
|
|
12
|
+
|
|
13
|
+
def escape_dollar(label):
|
|
14
|
+
return re.sub(r'\$', r'\\$', label)
|
|
15
|
+
|
|
16
|
+
def FindValue(x, df, xcolumn, ycolumn, bandwidth):
|
|
17
|
+
bwcount = int(np.ceil(bandwidth*df[xcolumn].count()))
|
|
18
|
+
df['distance'] = df[xcolumn].apply(func=CalculateDistance, args=(x,))
|
|
19
|
+
subsample = df.sort_values(by=['distance', ])[:bwcount]
|
|
20
|
+
maxsubvalue = subsample['distance'].max()
|
|
21
|
+
subsample['wdistance'] = subsample['distance']/maxsubvalue
|
|
22
|
+
weight = np.array(subsample['wdistance'].apply(func=CalculateWeight).tolist())
|
|
23
|
+
y = subsample[ycolumn].values
|
|
24
|
+
XM = np.column_stack([
|
|
25
|
+
np.ones(len(subsample)),
|
|
26
|
+
subsample[xcolumn].to_numpy(),
|
|
27
|
+
np.power(subsample[xcolumn].to_numpy(), 2)
|
|
28
|
+
])
|
|
29
|
+
r = sm.WLS(y, XM, weights=weight).fit().params
|
|
30
|
+
xvals = np.array([1, x, np.power(x,2)]).T
|
|
31
|
+
projection = np.dot(r,xvals)
|
|
32
|
+
return projection
|
|
33
|
+
|
|
34
|
+
def RunLOESS(bandwidth, data, xcolumn, ycolumn):
|
|
35
|
+
xinterval = np.linspace(data[xcolumn].min(), data[xcolumn].max(), 1000).tolist()
|
|
36
|
+
yprojections = [FindValue(x, data, xcolumn, ycolumn, bandwidth) for x in xinterval]
|
|
37
|
+
return (xinterval, yprojections)
|
|
38
|
+
|
|
39
|
+
def ManageLOESSProcess(bandwidth, data, xcolumn, ycolumn):
|
|
40
|
+
xinterval, yprojections = RunLOESS(bandwidth, data, xcolumn, ycolumn)
|
|
41
|
+
return xinterval, yprojections
|
|
42
|
+
|
|
43
|
+
def CalculateKDE(x, data, bandwidth):
|
|
44
|
+
subsample = data.loc[((x-bandwidth) <= data) & (data <= (x+bandwidth))]
|
|
45
|
+
n = data.count()
|
|
46
|
+
if subsample.count()>0:
|
|
47
|
+
normalizedsubsample = subsample.apply(func=CalculateDistance, args=(x,))/(bandwidth)
|
|
48
|
+
density = (1/(n*bandwidth))*normalizedsubsample.apply(func=EpanechnikovWeight).sum()
|
|
49
|
+
return density
|
|
50
|
+
else:
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
def RunKDE(bandwidth, data):
|
|
54
|
+
mean = data.mean()
|
|
55
|
+
minRange = mean - 6*data.std()
|
|
56
|
+
maxRange = mean + 6*data.std()
|
|
57
|
+
xinterval = np.linspace(minRange, maxRange, 1000).tolist()
|
|
58
|
+
yprojections = [CalculateKDE(x, data, bandwidth) for x in xinterval]
|
|
59
|
+
return (xinterval, yprojections)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def ManageKDEProcess(bandwidth, data):
|
|
63
|
+
xinterval, yprojections = RunKDE(bandwidth, data)
|
|
64
|
+
return xinterval, yprojections
|
|
65
|
+
|
|
66
|
+
def CreateLOESSPlots(data, xcolumn, ycolumn, bandwidths, linetypes=False, save=False, xlabel="X", ylabel="Y", title="LOESS", xStep=2, figsize=(9, 5)):
|
|
67
|
+
"""
|
|
68
|
+
Plot LOESS curves for one response column at the requested bandwidths.
|
|
69
|
+
|
|
70
|
+
The source observations are shown as a scatter plot. If ``save`` is provided,
|
|
71
|
+
the figure is saved as a PDF before being displayed.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
data (pandas.DataFrame): The observations used to calculate and plot the curves.
|
|
75
|
+
xcolumn (str): The column to use for the x-axis.
|
|
76
|
+
ycolumn (str): The column to use for the y-axis.
|
|
77
|
+
bandwidths (list): The LOESS bandwidths to plot.
|
|
78
|
+
linetypes (list or bool): Line styles for the curves, or False to use defaults.
|
|
79
|
+
save (str or bool): Filename prefix for the optional PDF output, or False to skip saving.
|
|
80
|
+
xlabel (str): The x-axis label.
|
|
81
|
+
ylabel (str): The y-axis label.
|
|
82
|
+
title (str): The plot title.
|
|
83
|
+
xStep (int or float): The interval between x-axis ticks.
|
|
84
|
+
figsize (tuple): The figure width and height in inches.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
None
|
|
88
|
+
"""
|
|
89
|
+
defaultLinestyle = ['-', '--', ':', '-.']
|
|
90
|
+
if linetypes is False:
|
|
91
|
+
linetypes = []
|
|
92
|
+
for i, item in enumerate(cycle(defaultLinestyle)):
|
|
93
|
+
if i == len(data):
|
|
94
|
+
break
|
|
95
|
+
linetypes.append(item)
|
|
96
|
+
r = [ ManageLOESSProcess(bandwidth, data, xcolumn, ycolumn) for bandwidth in bandwidths ]
|
|
97
|
+
plt.figure(figsize=figsize)
|
|
98
|
+
y_min, y_max = data[ycolumn].min(), data[ycolumn].max()
|
|
99
|
+
x_min, x_max = data[xcolumn].min(), data[xcolumn].max()
|
|
100
|
+
ax = plt.axes()
|
|
101
|
+
ax.set_xlabel(xlabel)
|
|
102
|
+
ax.set_ylabel(ylabel)
|
|
103
|
+
ax.set_xlim(x_min, x_max)
|
|
104
|
+
ax.set_ylim(y_min, y_max)
|
|
105
|
+
ax.set_xticks(np.arange(x_min, x_max + 1, xStep))
|
|
106
|
+
ax.set_title(title)
|
|
107
|
+
ax.scatter(data[xcolumn].tolist(), data[ycolumn].tolist(), color='black')
|
|
108
|
+
for index, result in enumerate(r):
|
|
109
|
+
xinterval, yprojections = result
|
|
110
|
+
ax.plot(xinterval, yprojections, label='Bandwidth = ' + str(round(bandwidths[index], 4)), color='black', linestyle=linetypes[index])
|
|
111
|
+
ax.legend()
|
|
112
|
+
if save:
|
|
113
|
+
plt.savefig(save + '.pdf', bbox_inches='tight')
|
|
114
|
+
plt.savefig(save + '.svg', bbox_inches='tight')
|
|
115
|
+
plt.show()
|
|
116
|
+
|
|
117
|
+
def CreateMultipleLOESSPlots(data, xcolumn, ycolumns, bandwidths = False, linetypes=False, save=False, xlabel="X", ylabel="Y", title="Multiple LOESS Plots", xStep=2, figsize=(9, 5)):
|
|
118
|
+
"""
|
|
119
|
+
Plot LOESS curves for multiple response columns and bandwidths.
|
|
120
|
+
|
|
121
|
+
If ``bandwidths`` is False, bandwidths 0.3, 0.4, and 0.5 are used. If
|
|
122
|
+
``save`` is provided, the figure is saved as both PDF and SVG before display.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
data (pandas.DataFrame): The observations used to calculate and plot the curves.
|
|
126
|
+
xcolumn (str): The column to use for the x-axis.
|
|
127
|
+
ycolumns (list): The response columns to plot.
|
|
128
|
+
bandwidths (list, float, or bool): LOESS bandwidths to plot, or False to use defaults.
|
|
129
|
+
linetypes (list or bool): Line styles for the curves, or False to use defaults.
|
|
130
|
+
save (str or bool): Filename prefix for the optional PDF and SVG outputs, or False to skip saving.
|
|
131
|
+
xlabel (str): The x-axis label.
|
|
132
|
+
ylabel (str): The y-axis label.
|
|
133
|
+
title (str): The plot title.
|
|
134
|
+
xStep (int or float): The interval between x-axis ticks.
|
|
135
|
+
figsize (tuple): The figure width and height in inches.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
None
|
|
139
|
+
"""
|
|
140
|
+
if bandwidths is False:
|
|
141
|
+
bandwidths = [0.3, 0.4, 0.5]
|
|
142
|
+
if not isinstance(bandwidths, list):
|
|
143
|
+
bandwidths = [bandwidths,]
|
|
144
|
+
|
|
145
|
+
defaultColors = ['black']*4 + ['gray']*4
|
|
146
|
+
colors = []
|
|
147
|
+
for i, item in enumerate(cycle(defaultColors)):
|
|
148
|
+
if i == len(ycolumns)*len(bandwidths):
|
|
149
|
+
break
|
|
150
|
+
colors.append(item)
|
|
151
|
+
|
|
152
|
+
defaultLinestyle = ['-', '--', ':', '-.']
|
|
153
|
+
if linetypes is False:
|
|
154
|
+
linetypes = []
|
|
155
|
+
for i, item in enumerate(cycle(defaultLinestyle)):
|
|
156
|
+
if i == len(ycolumns)*len(bandwidths):
|
|
157
|
+
break
|
|
158
|
+
linetypes.append(item)
|
|
159
|
+
r = [ ManageLOESSProcess(bandwidth, data, xcolumn, ycolumn) for bandwidth in bandwidths for ycolumn in ycolumns ]
|
|
160
|
+
labels = [f"{ycolumn} (Bandwidth = {round(bandwidth, 4)})" for bandwidth in bandwidths for ycolumn in ycolumns]
|
|
161
|
+
labels = [escape_dollar(label) for label in labels]
|
|
162
|
+
plt.figure(figsize=figsize)
|
|
163
|
+
y_min, y_max = data[ycolumns].min().min(), data[ycolumns].max().max()
|
|
164
|
+
x_min, x_max = data[xcolumn].min(), data[xcolumn].max()
|
|
165
|
+
ax = plt.axes()
|
|
166
|
+
ax.set_xlabel(xlabel)
|
|
167
|
+
ax.set_ylabel(ylabel)
|
|
168
|
+
ax.set_xlim(x_min, x_max)
|
|
169
|
+
ax.set_ylim(y_min, y_max)
|
|
170
|
+
ax.set_xticks(np.arange(x_min, x_max + 1, xStep))
|
|
171
|
+
ax.set_title(title)
|
|
172
|
+
for index, result in enumerate(r):
|
|
173
|
+
xinterval, yprojections = result
|
|
174
|
+
ax.plot(xinterval, yprojections, label=labels[index], color=colors[index], linestyle=linetypes[index])
|
|
175
|
+
ax.legend()
|
|
176
|
+
if save:
|
|
177
|
+
plt.savefig(save + '.pdf', bbox_inches='tight')
|
|
178
|
+
plt.savefig(save + '.svg', bbox_inches='tight')
|
|
179
|
+
plt.show()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def CreateMultipleLinePlots(data, xcolumn, ycolumns, linetypes=False, save=False, xlabel="X", ylabel="Y", title="Multiple Line Plots", xStep=2, figsize=(9, 6)):
|
|
183
|
+
"""
|
|
184
|
+
Plot multiple response columns as lines against a shared x-axis column.
|
|
185
|
+
|
|
186
|
+
If ``save`` is provided, the figure is saved as both PDF and SVG before display.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
data (pandas.DataFrame): The observations to plot.
|
|
190
|
+
xcolumn (str): The column to use for the x-axis.
|
|
191
|
+
ycolumns (list): The response columns to plot.
|
|
192
|
+
linetypes (list or bool): Line styles for the series, or False to use defaults.
|
|
193
|
+
save (str or bool): Filename prefix for the optional PDF and SVG outputs, or False to skip saving.
|
|
194
|
+
xlabel (str): The x-axis label.
|
|
195
|
+
ylabel (str): The y-axis label.
|
|
196
|
+
title (str): The plot title.
|
|
197
|
+
xStep (int or float): The interval between x-axis ticks.
|
|
198
|
+
figsize (tuple): The figure width and height in inches.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
None
|
|
202
|
+
"""
|
|
203
|
+
defaultColors = ['black']*4 + ['gray']*4
|
|
204
|
+
colors = []
|
|
205
|
+
for i, item in enumerate(cycle(defaultColors)):
|
|
206
|
+
if i == len(ycolumns):
|
|
207
|
+
break
|
|
208
|
+
colors.append(item)
|
|
209
|
+
defaultLinestyle = ['-', '--', ':', '-.']
|
|
210
|
+
if linetypes is False:
|
|
211
|
+
linetypes = []
|
|
212
|
+
for i, item in enumerate(cycle(defaultLinestyle)):
|
|
213
|
+
if i == len(ycolumns):
|
|
214
|
+
break
|
|
215
|
+
linetypes.append(item)
|
|
216
|
+
plt.figure(figsize=figsize)
|
|
217
|
+
y_min, y_max = data[ycolumns].min().min(), data[ycolumns].max().max()
|
|
218
|
+
x_min, x_max = data[xcolumn].min(), data[xcolumn].max()
|
|
219
|
+
ax = plt.axes()
|
|
220
|
+
ax.set_xlabel(xlabel)
|
|
221
|
+
ax.set_ylabel(ylabel)
|
|
222
|
+
ax.set_xlim(x_min, x_max)
|
|
223
|
+
ax.set_ylim(y_min, y_max)
|
|
224
|
+
ax.set_xticks(np.arange(x_min, x_max + 1, xStep))
|
|
225
|
+
ax.set_title(title)
|
|
226
|
+
for index, ycolumn in enumerate(ycolumns):
|
|
227
|
+
ax.plot(data[xcolumn], data[ycolumn], label=escape_dollar(ycolumn), color=colors[index], linestyle=linetypes[index])
|
|
228
|
+
ax.legend()
|
|
229
|
+
if save:
|
|
230
|
+
plt.savefig(save + '.pdf', bbox_inches='tight')
|
|
231
|
+
plt.savefig(save + '.svg', bbox_inches='tight')
|
|
232
|
+
plt.show()
|
|
233
|
+
|
|
234
|
+
def ProjectKDEPlot(data, label='Score', labels=False, linetypes=False, save=False, lower=False, upper=False, bottom=0, top=False, mean=False, bwMultiplier = 1, sameBandwidth=False):
|
|
235
|
+
"""
|
|
236
|
+
Plot kernel density estimates for a collection of data series.
|
|
237
|
+
|
|
238
|
+
Bandwidths are calculated with Silverman's rule and scaled by ``bwMultiplier``.
|
|
239
|
+
If ``sameBandwidth`` is True, the first series' bandwidth is used for all
|
|
240
|
+
series. If ``save`` is provided, the figure is saved as both PDF and SVG
|
|
241
|
+
before display; the calculated bandwidths are also printed.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
data (list): The data series for which to calculate density estimates.
|
|
245
|
+
label (str): The x-axis label.
|
|
246
|
+
labels (list or bool): Legend labels for the series, or False to hide the legend.
|
|
247
|
+
linetypes (list or bool): Line styles for the series, or False to use defaults.
|
|
248
|
+
save (str or bool): Filename prefix for the optional PDF and SVG outputs, or False to skip saving.
|
|
249
|
+
lower (float or bool): Lower x-axis limit, or False to use the computed default.
|
|
250
|
+
upper (float or bool): Upper x-axis limit, or False to use the computed default.
|
|
251
|
+
bottom (float): Lower y-axis limit.
|
|
252
|
+
top (float or bool): Upper y-axis limit, or False to use the computed default.
|
|
253
|
+
mean (bool): Whether to draw a vertical line at each series' mean.
|
|
254
|
+
bwMultiplier (float): Factor by which to scale the calculated bandwidths.
|
|
255
|
+
sameBandwidth (bool): Whether to use the first series' bandwidth for every series.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
None
|
|
259
|
+
"""
|
|
260
|
+
showlegend = True
|
|
261
|
+
if labels is False:
|
|
262
|
+
labels = ['',]*len(data)
|
|
263
|
+
showlegend = False
|
|
264
|
+
defaultLinestyle = ['-', '--', ':', '-.']
|
|
265
|
+
if linetypes is False:
|
|
266
|
+
linetypes = []
|
|
267
|
+
for i, item in enumerate(cycle(defaultLinestyle)):
|
|
268
|
+
if i == len(data):
|
|
269
|
+
break
|
|
270
|
+
linetypes.append(item)
|
|
271
|
+
dataset = []
|
|
272
|
+
bandwidths = []
|
|
273
|
+
for d in data:
|
|
274
|
+
dataset.append(d)
|
|
275
|
+
if sameBandwidth:
|
|
276
|
+
bandwidths.append(Silvermans(data[0]) * bwMultiplier)
|
|
277
|
+
else:
|
|
278
|
+
bandwidths.append(Silvermans(d) * bwMultiplier)
|
|
279
|
+
r = []
|
|
280
|
+
for index, d in enumerate(dataset):
|
|
281
|
+
r.append(ManageKDEProcess(bandwidths[index], d))
|
|
282
|
+
plt.figure()
|
|
283
|
+
ax = plt.axes()
|
|
284
|
+
ax.set_ylabel('Density')
|
|
285
|
+
ax.set_xlabel(label)
|
|
286
|
+
xintervals = []
|
|
287
|
+
yintervals = []
|
|
288
|
+
for index, result in enumerate(r):
|
|
289
|
+
xinterval, yprojections = result
|
|
290
|
+
xintervals.append(xinterval)
|
|
291
|
+
yintervals.append(yprojections)
|
|
292
|
+
xintervalsNP = np.concatenate(xintervals)
|
|
293
|
+
yintervalsNP = np.concatenate(yintervals)
|
|
294
|
+
computedLower = np.percentile(xintervalsNP, 25)
|
|
295
|
+
computedUpper = np.percentile(xintervalsNP, 75)
|
|
296
|
+
computedTop = np.percentile(yintervalsNP, 100)*1.1
|
|
297
|
+
if lower is False:
|
|
298
|
+
lower = computedLower
|
|
299
|
+
if upper is False:
|
|
300
|
+
upper = computedUpper
|
|
301
|
+
if top is False:
|
|
302
|
+
top = computedTop
|
|
303
|
+
for index, result in enumerate(r):
|
|
304
|
+
xinterval, yprojections = result
|
|
305
|
+
ax.plot(xinterval, yprojections, label=labels[index], color='black', linestyle=linetypes[index])
|
|
306
|
+
if mean:
|
|
307
|
+
ax.vlines(dataset[index].mean(), bottom, top, color='gray', linestyles=linetypes[index])
|
|
308
|
+
ax.set_xlim(lower, upper)
|
|
309
|
+
ax.set_ylim(bottom, top)
|
|
310
|
+
#plt.vlines(subdata.mean(), 0, 1, color='black', linestyles='-')
|
|
311
|
+
if showlegend:
|
|
312
|
+
ax.legend()
|
|
313
|
+
if save:
|
|
314
|
+
plt.savefig(save + '.pdf', bbox_inches='tight')
|
|
315
|
+
plt.savefig(save + '.svg', bbox_inches='tight')
|
|
316
|
+
print('Silverman Bandwidths: ', bandwidths)
|
|
317
|
+
plt.show()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def makeHeatMap(displayData, heatmapData, xlabel, ylabel, title, save=False, figsize=(11, 7), label=None, vmin=None, vmax=None, reversed=False, yticklabels=None):
|
|
321
|
+
"""
|
|
322
|
+
Display a heatmap with values from ``heatmapData`` and annotations from ``displayData``.
|
|
323
|
+
|
|
324
|
+
Rows are restricted to their common index, and columns to those present in
|
|
325
|
+
both data frames. The ``_marginal`` suffix is removed from ``displayData``'s
|
|
326
|
+
index in place. If ``save`` is provided, the figure is saved as both PDF and
|
|
327
|
+
SVG before display.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
displayData (pandas.DataFrame): Values to annotate in each heatmap cell.
|
|
331
|
+
heatmapData (pandas.DataFrame): Values that determine the heatmap colors.
|
|
332
|
+
xlabel (str): The x-axis label.
|
|
333
|
+
ylabel (str): The y-axis label.
|
|
334
|
+
title (str): The plot title.
|
|
335
|
+
save (str or bool): Filename prefix for the optional PDF and SVG outputs, or False to skip saving.
|
|
336
|
+
figsize (tuple): The figure width and height in inches.
|
|
337
|
+
label (str or None): The colorbar label.
|
|
338
|
+
vmin (float or None): Lower bound of the heatmap color scale.
|
|
339
|
+
vmax (float or None): Upper bound of the heatmap color scale.
|
|
340
|
+
reversed (bool): Whether to reverse the grayscale color map.
|
|
341
|
+
yticklabels (list or bool or None): Labels to use for the y-axis ticks.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
None
|
|
345
|
+
"""
|
|
346
|
+
displayData.index = displayData.index.str.replace('_marginal', '', regex=False)
|
|
347
|
+
common_index = displayData.index.intersection(heatmapData.index)
|
|
348
|
+
displayData = displayData.loc[common_index, :]
|
|
349
|
+
heatmapData = heatmapData.loc[common_index, :]
|
|
350
|
+
heatmapData.reindex_like(displayData)
|
|
351
|
+
heatmapData = heatmapData[[col for col in displayData.columns if col in heatmapData.columns]]
|
|
352
|
+
cmap = 'Greys_r' if reversed else 'Greys'
|
|
353
|
+
if yticklabels is None:
|
|
354
|
+
yticklabels = "auto"
|
|
355
|
+
plt.figure(figsize=figsize)
|
|
356
|
+
ax = sns.heatmap(heatmapData, annot=displayData, fmt='.4f', cmap=cmap, cbar_kws={'label': label}, vmin=vmin, vmax=vmax, yticklabels=yticklabels)
|
|
357
|
+
ax.set_xlabel(xlabel)
|
|
358
|
+
ax.set_ylabel(ylabel)
|
|
359
|
+
ax.set_title(title)
|
|
360
|
+
if save:
|
|
361
|
+
plt.savefig(save + '.pdf', bbox_inches='tight')
|
|
362
|
+
plt.savefig(save + '.svg', bbox_inches='tight')
|
|
363
|
+
plt.show()
|
CBACABLab/Util.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from IPython.display import display
|
|
2
|
+
|
|
3
|
+
def SummaryStatisticsOfCounts(dataframe, columns, save=None):
|
|
4
|
+
"""
|
|
5
|
+
Display a count and percent table for each categorical column.
|
|
6
|
+
|
|
7
|
+
Missing values are counted as their own category. Each table is shown with IPython's display.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
dataframe (pandas.DataFrame): The data to summarize.
|
|
11
|
+
columns (list): The names of the columns to summarize.
|
|
12
|
+
save (str): Optional file path prefix. If given, each table is also written to
|
|
13
|
+
<save>_<column>_distribution.csv.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
None
|
|
17
|
+
"""
|
|
18
|
+
for column in columns:
|
|
19
|
+
counts = dataframe[column].value_counts(dropna=False)
|
|
20
|
+
table = counts.rename_axis("Category").to_frame("Count")
|
|
21
|
+
table["Percent"] = table["Count"] / table["Count"].sum()
|
|
22
|
+
|
|
23
|
+
display(
|
|
24
|
+
table.style
|
|
25
|
+
.format({"Count": "{:,}", "Percent": "{:.1%}"})
|
|
26
|
+
.set_caption(f"{column.title()} distribution")
|
|
27
|
+
)
|
|
28
|
+
if save is not None:
|
|
29
|
+
table.to_csv(save + f"_{column}_distribution.csv")
|
|
30
|
+
|
|
31
|
+
def SummaryStatisticsOfContinuous(dataframe, columns, save=None):
|
|
32
|
+
"""
|
|
33
|
+
Display descriptive statistics (count, mean, std, min, quartiles, max) for each numeric column.
|
|
34
|
+
|
|
35
|
+
Each table is shown with IPython's display.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
dataframe (pandas.DataFrame): The data to summarize.
|
|
39
|
+
columns (list): The names of the columns to summarize.
|
|
40
|
+
save (str): Optional file path prefix. If given, each summary is also written to
|
|
41
|
+
<save>_<column>_distribution.csv.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
None
|
|
45
|
+
"""
|
|
46
|
+
for column in columns:
|
|
47
|
+
summary = dataframe[column].describe()
|
|
48
|
+
display(
|
|
49
|
+
summary.to_frame().style
|
|
50
|
+
.format("{:.2f}")
|
|
51
|
+
.set_caption(f"{column.title()} distribution")
|
|
52
|
+
)
|
|
53
|
+
if save is not None:
|
|
54
|
+
summary.to_csv(save + f"_{column}_distribution.csv")
|
|
55
|
+
|
CBACABLab/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from .Plots import ProjectKDEPlot, CreateLOESSPlots, CreateMultipleLinePlots, CreateMultipleLOESSPlots, makeHeatMap
|
|
2
|
+
from .Util import SummaryStatisticsOfCounts, SummaryStatisticsOfContinuous
|
|
3
|
+
from .LoadRawData import LoadSurveyData, LoadSensorData, AutoDetermineQuestionIdtoQuestionNameMapping
|
|
4
|
+
from .LoadSQLData import ListQuestions, LoadQuestionsWide, LoadSurveyWithSensorDataLong, LoadSurveyWithSensorDataWide, ListInstrumentColumns, ListLikertScales
|
|
5
|
+
from .LikertPrep import LikertToK, AddLikertColumns, CreatePanelData
|
CBACABLab/settings.json
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
{
|
|
2
|
+
"InstrumentColumns": [
|
|
3
|
+
"Anger",
|
|
4
|
+
"Contempt",
|
|
5
|
+
"Disgust",
|
|
6
|
+
"Fear",
|
|
7
|
+
"Joy",
|
|
8
|
+
"Sadness",
|
|
9
|
+
"Surprise",
|
|
10
|
+
"Engagement",
|
|
11
|
+
"Adaptive Engagement",
|
|
12
|
+
"Valence",
|
|
13
|
+
"Adaptive Valence",
|
|
14
|
+
"Sentimentality",
|
|
15
|
+
"Confusion",
|
|
16
|
+
"Neutral",
|
|
17
|
+
"Attention",
|
|
18
|
+
"Blink",
|
|
19
|
+
"BlinkRate",
|
|
20
|
+
"Speaking",
|
|
21
|
+
"Yawn",
|
|
22
|
+
"Pain",
|
|
23
|
+
"Brow Furrow",
|
|
24
|
+
"Brow Raise",
|
|
25
|
+
"Cheek Raise",
|
|
26
|
+
"Chin Raise",
|
|
27
|
+
"Dimpler",
|
|
28
|
+
"Eye Closure",
|
|
29
|
+
"Eye Widen",
|
|
30
|
+
"Inner Brow Raise",
|
|
31
|
+
"Jaw Drop",
|
|
32
|
+
"Lip Corner Depressor",
|
|
33
|
+
"Lip Press",
|
|
34
|
+
"Lip Pucker",
|
|
35
|
+
"Lip Stretch",
|
|
36
|
+
"Lip Suck",
|
|
37
|
+
"Lid Tighten",
|
|
38
|
+
"Mouth Open",
|
|
39
|
+
"Nose Wrinkle",
|
|
40
|
+
"Smile",
|
|
41
|
+
"Smirk",
|
|
42
|
+
"Upper Lip Raise",
|
|
43
|
+
"Pitch",
|
|
44
|
+
"Yaw",
|
|
45
|
+
"Roll",
|
|
46
|
+
"Interocular Distance",
|
|
47
|
+
"GSR Resistance CAL",
|
|
48
|
+
"GSR Conductance CAL",
|
|
49
|
+
"Heart Rate PPG ALG"
|
|
50
|
+
],
|
|
51
|
+
"LikertScales": {
|
|
52
|
+
"agree4": {
|
|
53
|
+
"Strongly disagree": 0,
|
|
54
|
+
"Disagree": 1,
|
|
55
|
+
"Agree": 2,
|
|
56
|
+
"Strongly agree": 3
|
|
57
|
+
},
|
|
58
|
+
"agree5": {
|
|
59
|
+
"Strongly disagree": 0,
|
|
60
|
+
"Disagree": 1,
|
|
61
|
+
"Neither agree nor disagree": 2,
|
|
62
|
+
"Neither agree or disagree": 2,
|
|
63
|
+
"Agree": 3,
|
|
64
|
+
"Strongly agree": 4
|
|
65
|
+
},
|
|
66
|
+
"agree6": {
|
|
67
|
+
"Strongly disagree": 0,
|
|
68
|
+
"Disagree": 1,
|
|
69
|
+
"Somewhat disagree": 2,
|
|
70
|
+
"Somewhat agree": 3,
|
|
71
|
+
"Agree": 4,
|
|
72
|
+
"Strongly agree": 5
|
|
73
|
+
},
|
|
74
|
+
"agree7": {
|
|
75
|
+
"Strongly disagree": 0,
|
|
76
|
+
"Disagree": 1,
|
|
77
|
+
"Somewhat disagree": 2,
|
|
78
|
+
"Neither agree nor disagree": 3,
|
|
79
|
+
"Neither agree or disagree": 3,
|
|
80
|
+
"Somewhat agree": 4,
|
|
81
|
+
"Agree": 5,
|
|
82
|
+
"Strongly agree": 6
|
|
83
|
+
},
|
|
84
|
+
"helpful4": {
|
|
85
|
+
"Very unhelpful": 0,
|
|
86
|
+
"Unhelpful": 1,
|
|
87
|
+
"Helpful": 2,
|
|
88
|
+
"Very helpful": 3
|
|
89
|
+
},
|
|
90
|
+
"helpful5": {
|
|
91
|
+
"Very unhelpful": 0,
|
|
92
|
+
"Unhelpful": 1,
|
|
93
|
+
"Neither helpful nor unhelpful": 2,
|
|
94
|
+
"Helpful": 3,
|
|
95
|
+
"Very helpful": 4
|
|
96
|
+
},
|
|
97
|
+
"helpful6": {
|
|
98
|
+
"Very unhelpful": 0,
|
|
99
|
+
"Unhelpful": 1,
|
|
100
|
+
"Somewhat unhelpful": 2,
|
|
101
|
+
"Somewhat helpful": 3,
|
|
102
|
+
"Helpful": 4,
|
|
103
|
+
"Very helpful": 5
|
|
104
|
+
},
|
|
105
|
+
"helpful7": {
|
|
106
|
+
"Very unhelpful": 0,
|
|
107
|
+
"Unhelpful": 1,
|
|
108
|
+
"Somewhat unhelpful": 2,
|
|
109
|
+
"Neither helpful nor unhelpful": 3,
|
|
110
|
+
"Somewhat helpful": 4,
|
|
111
|
+
"Helpful": 5,
|
|
112
|
+
"Very helpful": 6
|
|
113
|
+
},
|
|
114
|
+
"satisfied4": {
|
|
115
|
+
"Very dissatisfied": 0,
|
|
116
|
+
"Dissatisfied": 1,
|
|
117
|
+
"Satisfied": 2,
|
|
118
|
+
"Very satisfied": 3
|
|
119
|
+
},
|
|
120
|
+
"satisfied5": {
|
|
121
|
+
"Very dissatisfied": 0,
|
|
122
|
+
"Highly dissatisfied": 0,
|
|
123
|
+
"Dissatisfied": 1,
|
|
124
|
+
"Neither satisfied nor dissatisfied": 2,
|
|
125
|
+
"Satisfied": 3,
|
|
126
|
+
"Very satisfied": 4,
|
|
127
|
+
"Highly satisfied": 4
|
|
128
|
+
},
|
|
129
|
+
"satisfied6": {
|
|
130
|
+
"Very dissatisfied": 0,
|
|
131
|
+
"Dissatisfied": 1,
|
|
132
|
+
"Somewhat dissatisfied": 2,
|
|
133
|
+
"Somewhat satisfied": 3,
|
|
134
|
+
"Satisfied": 4,
|
|
135
|
+
"Very satisfied": 5
|
|
136
|
+
},
|
|
137
|
+
"satisfied7": {
|
|
138
|
+
"Very dissatisfied": 0,
|
|
139
|
+
"Dissatisfied": 1,
|
|
140
|
+
"Somewhat dissatisfied": 2,
|
|
141
|
+
"Neither satisfied nor dissatisfied": 3,
|
|
142
|
+
"Somewhat satisfied": 4,
|
|
143
|
+
"Satisfied": 5,
|
|
144
|
+
"Very satisfied": 6
|
|
145
|
+
},
|
|
146
|
+
"agree5_completely": {
|
|
147
|
+
"Completely disagree": 0,
|
|
148
|
+
"Somewhat disagree": 1,
|
|
149
|
+
"Neither agree or disagree": 2,
|
|
150
|
+
"Neither agree nor disagree": 2,
|
|
151
|
+
"Somewhat agree": 3,
|
|
152
|
+
"Completely agree": 4
|
|
153
|
+
},
|
|
154
|
+
"quality5": {
|
|
155
|
+
"Very poor": 0,
|
|
156
|
+
"Poor": 1,
|
|
157
|
+
"Fair": 2,
|
|
158
|
+
"Good": 3,
|
|
159
|
+
"Excellent": 4
|
|
160
|
+
},
|
|
161
|
+
"quality7": {
|
|
162
|
+
"Very poor": 0,
|
|
163
|
+
"Poor": 1,
|
|
164
|
+
"Fair": 2,
|
|
165
|
+
"Good": 3,
|
|
166
|
+
"Very good": 4,
|
|
167
|
+
"Excellent": 5,
|
|
168
|
+
"Exceptional": 6
|
|
169
|
+
},
|
|
170
|
+
"happy7": {
|
|
171
|
+
"Extremely unhappy": 0,
|
|
172
|
+
"Unhappy": 1,
|
|
173
|
+
"Somewhat unhappy": 2,
|
|
174
|
+
"Neither happy nor unhappy": 3,
|
|
175
|
+
"Somewhat happy": 4,
|
|
176
|
+
"Happy": 5,
|
|
177
|
+
"Extremely happy": 6
|
|
178
|
+
},
|
|
179
|
+
"satisfied7_completely": {
|
|
180
|
+
"Completely unsatisfied": 0,
|
|
181
|
+
"Moderately unsatisfied": 1,
|
|
182
|
+
"Unsatisfied": 2,
|
|
183
|
+
"Neither satisfied nor unsatisfied": 3,
|
|
184
|
+
"Moderately satisfied": 4,
|
|
185
|
+
"Satisfied": 5,
|
|
186
|
+
"Completely satisfied": 6
|
|
187
|
+
},
|
|
188
|
+
"numeric4": {"1": 0, "2": 1, "3": 2, "4": 3},
|
|
189
|
+
"numeric5": {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4},
|
|
190
|
+
"numeric6": {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5},
|
|
191
|
+
"numeric7": {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: CBACABLab
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Tools for loading, joining, and plotting survey and iMotions sensor data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/UNO-CAB-Lab/python-parsing
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/UNO-CAB-Lab/python-parsing/issues
|
|
7
|
+
Author-email: Ben Smith <bosmith@unomaha.edu>
|
|
8
|
+
Maintainer-email: Ben Smith <bosmith@unomaha.edu>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: Eye Tracking,Facial Expression,Qualtrics,Survey,iMotions
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: matplotlib>=3.4.0
|
|
22
|
+
Requires-Dist: numpy>=1.21.0
|
|
23
|
+
Requires-Dist: openpyxl>=3.0.0
|
|
24
|
+
Requires-Dist: pandas>=1.3.0
|
|
25
|
+
Requires-Dist: seaborn>=0.11.0
|
|
26
|
+
Requires-Dist: statsmodels>=0.13.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# CBA CAB Lab
|
|
30
|
+
|
|
31
|
+
This module is part of the UNO's CBA CAB Lab project to expand the labs usage to everyone interested in using it. It provides a set of tools that automatically map question data from Qualtrics to the data that is generated through instruments (iMotions). In this process, it generates a SQLite database that can be used directly. However, it also contains numerous helpful functions for selecting and filtering the data you want for analysis. This project is managed by Ben Smith, so please reach out to him if you have any questions.
|
|
32
|
+
|
|
33
|
+
How do I install this module?
|
|
34
|
+
|
|
35
|
+
To install this module, you can use pip:
|
|
36
|
+
```
|
|
37
|
+
pip install CBACABLab
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
To install this module with conda, you can use the following command:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
conda install -c tazzben cbacablab
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you don't yet have Python installed, we recommend installing Anaconda, which is a open-source distribution of Python that includes useful packages for data analysis. You can download Anaconda from [here](https://www.anaconda.com/products/distribution). We also recommend you install Microsoft [Visual Studio Code](https://code.visualstudio.com/) to work with notebook files.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
CBACABLab/LikertPrep.py,sha256=2_wC3ICtJrnGAx26MM8vnp-8oN36w9gFzbyzGRGBjd4,4817
|
|
2
|
+
CBACABLab/LoadRawData.py,sha256=ODjsJf6TXtMwPqMu-z25LRODa5SrgSZUwZlqHqYXMbk,11060
|
|
3
|
+
CBACABLab/LoadSQLData.py,sha256=_HcuEewz6lwXDKyEg7W5x8pAbaTzOygim1Tt-JG2Ymc,13377
|
|
4
|
+
CBACABLab/Plots.py,sha256=ZlVIjidpQUh2Mo0s2uQq8Dm_9-f2RRzPa0uCeo8K7vg,15604
|
|
5
|
+
CBACABLab/Util.py,sha256=WENsmV_-2m2CLAK-ON7Jkq2tqq5sGYubQj0BUHhgyXY,1909
|
|
6
|
+
CBACABLab/__init__.py,sha256=VFRutIQbiyO2D6L_Bbia5GbAMle4mMVQy0ymeGWg7Zg,520
|
|
7
|
+
CBACABLab/settings.json,sha256=p4pIEi5wWHPKcisCgXKrWSLfvag0pePw2Nn0_XrjcEk,5309
|
|
8
|
+
cbacablab-0.0.1.dist-info/METADATA,sha256=LgG1JvPhb7PiJ6DSt9_CtsrjEK42UQlyu7IZuYnJRLA,2299
|
|
9
|
+
cbacablab-0.0.1.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
10
|
+
cbacablab-0.0.1.dist-info/licenses/LICENSE,sha256=lGlR4xNFKXHbp7H0vE2lRGkcv52zJOMF3wKc7kOJvUs,1066
|
|
11
|
+
cbacablab-0.0.1.dist-info/RECORD,,
|