csdid 0.2.7__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.
- csdid/__init__.py +0 -0
- csdid/_version.py +1 -0
- csdid/aggte_fnc/__init__.py +0 -0
- csdid/aggte_fnc/aggte.py +113 -0
- csdid/aggte_fnc/compute_aggte.py +495 -0
- csdid/aggte_fnc/utils.py +196 -0
- csdid/att_gt.py +245 -0
- csdid/attgt_fnc/__init__.py +0 -0
- csdid/attgt_fnc/compute_att_gt.py +507 -0
- csdid/attgt_fnc/preprocess_did.py +429 -0
- csdid/plots/__init__.py +0 -0
- csdid/plots/gplot.py +106 -0
- csdid/utils/__init__.py +0 -0
- csdid/utils/bmisc.py +55 -0
- csdid/utils/mboot.py +115 -0
- csdid-0.2.7.dist-info/METADATA +29 -0
- csdid-0.2.7.dist-info/RECORD +20 -0
- csdid-0.2.7.dist-info/WHEEL +5 -0
- csdid-0.2.7.dist-info/licenses/LICENSE +21 -0
- csdid-0.2.7.dist-info/top_level.txt +1 -0
csdid/aggte_fnc/utils.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from csdid.utils.bmisc import TorF
|
|
2
|
+
from csdid.utils.mboot import mboot
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import scipy.stats as stats
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
def wif(keepers, pg, weights_ind, G, group):
|
|
9
|
+
# note: weights are all of the form P(G=g|cond)/sum_cond(P(G=g|cond))
|
|
10
|
+
# this is equal to P(G=g)/sum_cond(P(G=g)) which simplifies things here
|
|
11
|
+
pg = np.array(pg)
|
|
12
|
+
group = np.array(group)
|
|
13
|
+
|
|
14
|
+
# effect of estimating weights in the numerator
|
|
15
|
+
if1 = np.empty((len(weights_ind), len(keepers)))
|
|
16
|
+
for i, k in enumerate(keepers):
|
|
17
|
+
numerator = (weights_ind * 1 * TorF(G == group[k])) - pg[k]
|
|
18
|
+
# denominator = sum(np.array(pg)[keepers]) )[:, None]
|
|
19
|
+
denominator = np.sum(pg[keepers])
|
|
20
|
+
|
|
21
|
+
result = numerator[:, None] / denominator
|
|
22
|
+
if1[:, i] = result.squeeze()
|
|
23
|
+
|
|
24
|
+
# effect of estimating weights in the denominator
|
|
25
|
+
if2 = np.empty((len(weights_ind), len(keepers)))
|
|
26
|
+
for i, k in enumerate(keepers):
|
|
27
|
+
numerator = ( weights_ind * 1 * TorF(G == group[k]) ) - pg[k]
|
|
28
|
+
# result = numerator.to_numpy()[:, None] @ multipler[:, None].T
|
|
29
|
+
if2[:, i] = numerator.squeeze()
|
|
30
|
+
if2 = np.sum(if2, axis=1)
|
|
31
|
+
multiplier = ( pg[keepers] / sum( pg[keepers] ) ** 2 )
|
|
32
|
+
if2 = np.outer( if2 , multiplier)
|
|
33
|
+
|
|
34
|
+
# if1 = [((weights_ind * 1*TorF(G==group[k])) - pg[k]) / sum(pg[keepers]) for k in keepers]
|
|
35
|
+
# if2 = np.dot(np.array([weights_ind*1*TorF(G==group[k]) - pg[k] for k in keepers]).T, pg[keepers]/(sum(pg[keepers])**2))
|
|
36
|
+
wif_factor = if1 - if2
|
|
37
|
+
return wif_factor
|
|
38
|
+
|
|
39
|
+
def get_agg_inf_func(att, inffunc, whichones, weights_agg, wif=None):
|
|
40
|
+
# enforce weights are in matrix form
|
|
41
|
+
weights_agg = np.asarray(weights_agg)
|
|
42
|
+
|
|
43
|
+
# multiplies influence function times weights and sums to get vector of weighted IF (of length n)
|
|
44
|
+
thisinffunc = np.dot(inffunc[:, whichones], weights_agg)
|
|
45
|
+
|
|
46
|
+
# Incorporate influence function of the weights
|
|
47
|
+
if wif is not None:
|
|
48
|
+
thisinffunc = thisinffunc + np.dot(wif, np.array(att[whichones]))
|
|
49
|
+
|
|
50
|
+
# return influence function
|
|
51
|
+
return thisinffunc
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_se(thisinffunc, DIDparams=None):
|
|
55
|
+
alpha = 0.05
|
|
56
|
+
bstrap = False
|
|
57
|
+
if DIDparams is not None:
|
|
58
|
+
bstrap = DIDparams['bstrap']
|
|
59
|
+
alpha = DIDparams['alp']
|
|
60
|
+
cband = DIDparams['cband']
|
|
61
|
+
n = len(thisinffunc)
|
|
62
|
+
|
|
63
|
+
if bstrap:
|
|
64
|
+
bout = mboot(thisinffunc, DIDparams)
|
|
65
|
+
return bout['se']
|
|
66
|
+
else:
|
|
67
|
+
return np.sqrt(np.mean((thisinffunc)**2) / n)
|
|
68
|
+
|
|
69
|
+
def AGGTEobj(overall_att=None,
|
|
70
|
+
overall_se=None,
|
|
71
|
+
typec="simple",
|
|
72
|
+
egt=None,
|
|
73
|
+
att_egt=None,
|
|
74
|
+
se_egt=None,
|
|
75
|
+
crit_val_egt=None,
|
|
76
|
+
inf_function=None,
|
|
77
|
+
min_e=None,
|
|
78
|
+
max_e=None,
|
|
79
|
+
balance_e=None,
|
|
80
|
+
call=None,
|
|
81
|
+
DIDparams=None):
|
|
82
|
+
|
|
83
|
+
out = {
|
|
84
|
+
"overall_att": overall_att,
|
|
85
|
+
"overall_se": overall_se,
|
|
86
|
+
"type": typec,
|
|
87
|
+
"egt": egt,
|
|
88
|
+
"att_egt": att_egt,
|
|
89
|
+
"se_egt": se_egt,
|
|
90
|
+
"crit_val_egt": crit_val_egt,
|
|
91
|
+
"inf_function": inf_function,
|
|
92
|
+
"min_e": min_e,
|
|
93
|
+
"max_e": max_e,
|
|
94
|
+
"balance_e": balance_e,
|
|
95
|
+
"call": call,
|
|
96
|
+
"DIDparams": DIDparams
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Overall estimates
|
|
101
|
+
alp = out["DIDparams"]["alp"]
|
|
102
|
+
pointwise_cval = stats.norm.ppf(1 - alp / 2)
|
|
103
|
+
overall_cband_upper = out["overall_att"] + pointwise_cval * out["overall_se"]
|
|
104
|
+
overall_cband_lower = out["overall_att"] - pointwise_cval * out["overall_se"]
|
|
105
|
+
out1 = np.column_stack((out["overall_att"], out["overall_se"], overall_cband_lower, overall_cband_upper))
|
|
106
|
+
out1 = np.round(out1, 4)
|
|
107
|
+
overall_sig = (overall_cband_upper < 0) | (overall_cband_lower > 0)
|
|
108
|
+
overall_sig[np.isnan(overall_sig)] = False
|
|
109
|
+
overall_sig_text = np.where(overall_sig, "*", "")
|
|
110
|
+
out1 = np.column_stack((out1, overall_sig_text))
|
|
111
|
+
|
|
112
|
+
print("\n")
|
|
113
|
+
if out["type"] == "dynamic":
|
|
114
|
+
print("Overall summary of ATT's based on event-study/dynamic aggregation:")
|
|
115
|
+
elif out["type"] == "group":
|
|
116
|
+
print("Overall summary of ATT's based on group/cohort aggregation:")
|
|
117
|
+
elif out["type"] == "calendar":
|
|
118
|
+
print("Overall summary of ATT's based on calendar time aggregation:")
|
|
119
|
+
colnames = ["ATT", "Std. Error", f"[{100 * (1 - out['DIDparams']['alp'])}%"," Conf. Int.]", ""]
|
|
120
|
+
print(pd.DataFrame(out1, columns=colnames).to_string(index=False))
|
|
121
|
+
print("\n")
|
|
122
|
+
|
|
123
|
+
# Handle cases depending on type
|
|
124
|
+
if out["type"] in ["group", "dynamic", "calendar"]:
|
|
125
|
+
if out["type"] == "dynamic":
|
|
126
|
+
c1name = "Event time"
|
|
127
|
+
print("Dynamic Effects:")
|
|
128
|
+
elif out["type"] == "group":
|
|
129
|
+
c1name = "Group"
|
|
130
|
+
print("Group Effects:")
|
|
131
|
+
elif out["type"] == "calendar":
|
|
132
|
+
c1name = "Time"
|
|
133
|
+
print("Time Effects (calendar):")
|
|
134
|
+
|
|
135
|
+
cband_text1a = f"{100 * (1 - out['DIDparams']['alp'])}% "
|
|
136
|
+
cband_text1b = "Simult. " if out["DIDparams"]["bstrap"] else "Pointwise "
|
|
137
|
+
cband_text1 = f"[{cband_text1a}{cband_text1b}"
|
|
138
|
+
|
|
139
|
+
cband_lower = out["att_egt"] - out["crit_val_egt"] * out["se_egt"]
|
|
140
|
+
cband_upper = out["att_egt"] + out["crit_val_egt"] * out["se_egt"]
|
|
141
|
+
|
|
142
|
+
sig = (cband_upper < 0) | (cband_lower > 0)
|
|
143
|
+
sig[np.isnan(sig)] = False
|
|
144
|
+
sig_text = np.where(sig, "*", "").T
|
|
145
|
+
|
|
146
|
+
out2 = pd.DataFrame([out["egt"],
|
|
147
|
+
out["att_egt"],
|
|
148
|
+
out["se_egt"].flatten(),
|
|
149
|
+
np.hstack(cband_lower),
|
|
150
|
+
np.hstack(cband_upper)]).T
|
|
151
|
+
|
|
152
|
+
out2 = out2.round(4)
|
|
153
|
+
out2[0] = out2[0].astype(int)
|
|
154
|
+
out2 = pd.concat([out2, pd.DataFrame(sig_text, columns=['sig_text']) ], axis=1)
|
|
155
|
+
|
|
156
|
+
out2.columns = [c1name, "Estimate", "Std. Error", cband_text1, "Conf. Band", ""]
|
|
157
|
+
print(out2)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
print("---")
|
|
164
|
+
print("Signif. codes: `*' confidence band does not cover 0")
|
|
165
|
+
|
|
166
|
+
# Set control group text
|
|
167
|
+
control_group = out["DIDparams"]["control_group"]
|
|
168
|
+
control_group_text = None
|
|
169
|
+
if control_group == "nevertreated":
|
|
170
|
+
control_group_text = "Never Treated"
|
|
171
|
+
elif control_group == "notyettreated":
|
|
172
|
+
control_group_text = "Not Yet Treated"
|
|
173
|
+
|
|
174
|
+
if control_group:
|
|
175
|
+
print("Control Group: ", control_group_text, ", ")
|
|
176
|
+
|
|
177
|
+
# Anticipation periods
|
|
178
|
+
print("Anticipation Periods: ", out["DIDparams"]["anticipation"])
|
|
179
|
+
|
|
180
|
+
# Estimation method text
|
|
181
|
+
est_method = out["DIDparams"]["est_method"]
|
|
182
|
+
if isinstance(est_method, str):
|
|
183
|
+
est_method_text = est_method
|
|
184
|
+
if est_method == "dr":
|
|
185
|
+
est_method_text = "Doubly Robust"
|
|
186
|
+
elif est_method == "ipw":
|
|
187
|
+
est_method_text = "Inverse Probability Weighting"
|
|
188
|
+
elif est_method == "reg":
|
|
189
|
+
est_method_text = "Outcome Regression"
|
|
190
|
+
|
|
191
|
+
print("Estimation Method: ", est_method_text)
|
|
192
|
+
print("\n")
|
|
193
|
+
|
|
194
|
+
return out
|
|
195
|
+
|
|
196
|
+
|
csdid/att_gt.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# from aggte import AGGte
|
|
2
|
+
from csdid.aggte_fnc.aggte import aggte as agg_te
|
|
3
|
+
|
|
4
|
+
from csdid.attgt_fnc.preprocess_did import pre_process_did
|
|
5
|
+
from csdid.attgt_fnc.compute_att_gt import compute_att_gt
|
|
6
|
+
|
|
7
|
+
from csdid.utils.mboot import mboot
|
|
8
|
+
|
|
9
|
+
from csdid.plots.gplot import gplot, splot
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
|
|
14
|
+
import warnings
|
|
15
|
+
|
|
16
|
+
import numpy as np, pandas as pd
|
|
17
|
+
|
|
18
|
+
# class ATTgt(AGGte):
|
|
19
|
+
class ATTgt:
|
|
20
|
+
def __init__(self, yname, tname, idname, gname, data, control_group = ['nevertreated', 'notyettreated'],
|
|
21
|
+
xformla: str = None, panel = True, allow_unbalanced_panel = True,
|
|
22
|
+
clustervar = None, weights_name = None, anticipation = 0,
|
|
23
|
+
cband = False, biters = 1000, alp = 0.05
|
|
24
|
+
):
|
|
25
|
+
dp = pre_process_did(
|
|
26
|
+
yname=yname, tname = tname, idname=idname, gname = gname,
|
|
27
|
+
data = data, control_group=control_group, anticipation=anticipation,
|
|
28
|
+
xformla=xformla, panel=panel, allow_unbalanced_panel=allow_unbalanced_panel, cband=cband, clustervar=None, weights_name=None
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
dp['biters'] = biters
|
|
32
|
+
dp['alp'] = alp
|
|
33
|
+
dp['true_repeated_cross_sections'] = dp['true_rep_cross_section']
|
|
34
|
+
dp['cband'] = cband
|
|
35
|
+
dp['panel'] = panel
|
|
36
|
+
self.dp = dp
|
|
37
|
+
|
|
38
|
+
def fit(self, est_method = 'dr', base_period = 'varying', bstrap = True):
|
|
39
|
+
# print(self.dp)
|
|
40
|
+
dp = self.dp
|
|
41
|
+
result, inffunc = compute_att_gt(dp, est_method = est_method, base_period = base_period)
|
|
42
|
+
att = result['att']
|
|
43
|
+
n_len = list(map(len, inffunc))
|
|
44
|
+
crit_val, se, V = (
|
|
45
|
+
1.96,
|
|
46
|
+
np.std(inffunc, axis=1, ddof = 1) / np.sqrt(n_len),
|
|
47
|
+
np.zeros(len(att)),
|
|
48
|
+
)
|
|
49
|
+
if bstrap:
|
|
50
|
+
ref_se = mboot(inffunc.T, dp)
|
|
51
|
+
crit_val, se = ref_se['crit_val'], ref_se['se']
|
|
52
|
+
V = ref_se['V']
|
|
53
|
+
|
|
54
|
+
############# aggte input
|
|
55
|
+
group = result['group']
|
|
56
|
+
att = result['att']
|
|
57
|
+
tt = result['year']
|
|
58
|
+
inf_fnc = {'inffunc': inffunc.T}
|
|
59
|
+
|
|
60
|
+
dp['bstrap'] = bstrap
|
|
61
|
+
dp['est_method'] = est_method
|
|
62
|
+
dp['base_period'] = base_period
|
|
63
|
+
self.dp = dp
|
|
64
|
+
n = dp['n']
|
|
65
|
+
|
|
66
|
+
mp = {
|
|
67
|
+
'group': group, 'att': att, 't': tt,
|
|
68
|
+
'DIDparams': dp, 'inffunc': inf_fnc,
|
|
69
|
+
'n': n
|
|
70
|
+
}
|
|
71
|
+
self.MP = mp
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
cband_lower = att - crit_val * se
|
|
75
|
+
cband_upper = att + crit_val * se
|
|
76
|
+
sig = (cband_upper < 0) | (cband_lower > 0)
|
|
77
|
+
sig[np.isnan(sig)] = False
|
|
78
|
+
sig_text = np.where(sig, "*", "")
|
|
79
|
+
|
|
80
|
+
result.update(
|
|
81
|
+
{
|
|
82
|
+
'se': se, 'l_se': cband_lower,
|
|
83
|
+
'c': crit_val,
|
|
84
|
+
'u_se': cband_upper, 'sig': sig_text
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
self.results = result
|
|
88
|
+
|
|
89
|
+
rst = result
|
|
90
|
+
did_object = {
|
|
91
|
+
'group': mp['group'],
|
|
92
|
+
't': mp['t'],
|
|
93
|
+
'att': rst['att'],
|
|
94
|
+
'se': rst['se'],
|
|
95
|
+
'c': rst['c'],
|
|
96
|
+
}
|
|
97
|
+
self.did_object = did_object
|
|
98
|
+
return self
|
|
99
|
+
def summ_attgt(self, n = 4):
|
|
100
|
+
result = self.results
|
|
101
|
+
att_gt = pd.DataFrame(result)
|
|
102
|
+
att_gt = att_gt.drop('c', axis=1)
|
|
103
|
+
name_attgt_df = ['Group', 'Time', 'ATT(g, t)', 'Post', "Std. Error", "[95% Pointwise", 'Conf. Band]', '']
|
|
104
|
+
att_gt.columns = name_attgt_df
|
|
105
|
+
att_gt = att_gt.round(n)
|
|
106
|
+
self.summary2 = att_gt
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
def aggte(
|
|
110
|
+
self,
|
|
111
|
+
typec = "group",
|
|
112
|
+
balance_e = None,
|
|
113
|
+
min_e = float('-inf'),
|
|
114
|
+
max_e = float('inf'),
|
|
115
|
+
na_rm = False,
|
|
116
|
+
bstrap = None,
|
|
117
|
+
biters = None,
|
|
118
|
+
cband = None,
|
|
119
|
+
alp = None,
|
|
120
|
+
clustervars = None,
|
|
121
|
+
):
|
|
122
|
+
mp = self.MP
|
|
123
|
+
did_object = self.did_object
|
|
124
|
+
|
|
125
|
+
did_object.update({
|
|
126
|
+
'type': typec
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
atte = agg_te(
|
|
132
|
+
mp, typec=typec, balance_e=balance_e,
|
|
133
|
+
min_e=min_e, max_e=max_e, na_rm=na_rm, bstrap=bstrap,
|
|
134
|
+
biters=biters, cband=cband, alp=alp, clustervars=clustervars
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
self.atte = atte
|
|
138
|
+
return self
|
|
139
|
+
def plot_attgt(self, ylim=None,
|
|
140
|
+
xlab=None,
|
|
141
|
+
ylab=None,
|
|
142
|
+
title="Group",
|
|
143
|
+
xgap=1,
|
|
144
|
+
ncol=1,
|
|
145
|
+
legend=True,
|
|
146
|
+
group=None,
|
|
147
|
+
ref_line=0,
|
|
148
|
+
theming=True,
|
|
149
|
+
grtitle="Group"
|
|
150
|
+
):
|
|
151
|
+
|
|
152
|
+
did_object = self.did_object
|
|
153
|
+
|
|
154
|
+
grp = did_object['group']
|
|
155
|
+
t_i = did_object['t']
|
|
156
|
+
|
|
157
|
+
G = len(np.unique(grp))
|
|
158
|
+
Y = len(np.unique(t_i))
|
|
159
|
+
g = np.unique(grp)[np.argsort(np.unique(grp))].astype(int)
|
|
160
|
+
y = np.unique(t_i)
|
|
161
|
+
|
|
162
|
+
results = pd.DataFrame({'year': np.tile(y, G)})
|
|
163
|
+
results['group'] = np.repeat(g, Y)
|
|
164
|
+
results['grtitle'] = grtitle + ' ' + results['group'].astype(str)
|
|
165
|
+
results['att'] = did_object['att']
|
|
166
|
+
results['att_se'] = did_object['se']
|
|
167
|
+
results['post'] = np.where(results['year'] >= grp, 1, 0)
|
|
168
|
+
results['year'] = results['year']
|
|
169
|
+
results['c'] = did_object['c']
|
|
170
|
+
|
|
171
|
+
self.results_plot_df_attgt = results
|
|
172
|
+
|
|
173
|
+
if group is None:
|
|
174
|
+
group = g
|
|
175
|
+
if any(group not in g for group in group):
|
|
176
|
+
raise ValueError("Some of the specified groups do not exist in the data. Reporting all available groups.")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
legend_1 = False # for multiple subplots, legend outside
|
|
180
|
+
fig, axes = plt.subplots(nrows=len(group), ncols=1, figsize=(10, 5)) # Adjust the figsize as needed
|
|
181
|
+
handles = []
|
|
182
|
+
labels = []
|
|
183
|
+
for i, group_cat in enumerate(group):
|
|
184
|
+
group_data = results.loc[results['group'] == group_cat]
|
|
185
|
+
title = group_data['grtitle'].unique()[0]
|
|
186
|
+
ax = axes[i]
|
|
187
|
+
ax = gplot(group_data, ax, ylim, xlab, ylab, title, xgap, legend_1, ref_line, theming)
|
|
188
|
+
plt.tight_layout()
|
|
189
|
+
if legend is True:
|
|
190
|
+
handles_ax, labels_ax = ax.get_legend_handles_labels()
|
|
191
|
+
handles.extend(handles_ax)
|
|
192
|
+
labels.extend(labels_ax)
|
|
193
|
+
fig.legend(handles, labels, loc='lower center', fontsize='small', bbox_to_anchor=(0.545, -0.075), ncol=2)
|
|
194
|
+
|
|
195
|
+
plt.show()
|
|
196
|
+
return fig
|
|
197
|
+
|
|
198
|
+
def plot_aggte(self, ylim=None,
|
|
199
|
+
xlab=None,
|
|
200
|
+
ylab=None,
|
|
201
|
+
title="",
|
|
202
|
+
xgap=1,
|
|
203
|
+
legend=True,
|
|
204
|
+
ref_line=0,
|
|
205
|
+
theming=True,
|
|
206
|
+
**kwargs):
|
|
207
|
+
|
|
208
|
+
did_object = self.atte
|
|
209
|
+
|
|
210
|
+
post_treat = 1 * (np.asarray(did_object["egt"]).astype(int) >= 0)
|
|
211
|
+
|
|
212
|
+
results = {
|
|
213
|
+
"year": list(map(int, did_object["egt"])),
|
|
214
|
+
"att": did_object["att_egt"],
|
|
215
|
+
"att_se": did_object["se_egt"][0],
|
|
216
|
+
"post": post_treat
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
results = pd.DataFrame(results)
|
|
220
|
+
self.results_plot_df_aggte = results
|
|
221
|
+
|
|
222
|
+
if did_object['crit_val_egt'] is None:
|
|
223
|
+
results['c'] = abs(norm.ppf(0.025))
|
|
224
|
+
else:
|
|
225
|
+
results['c'] = did_object['crit_val_egt']
|
|
226
|
+
|
|
227
|
+
if title == "":
|
|
228
|
+
title = "Average Effect by Group" if\
|
|
229
|
+
did_object["type"] == "group" else\
|
|
230
|
+
"Average Effect by Length of Exposure"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if did_object["type"] == "group":
|
|
234
|
+
fig, ax = plt.subplots(figsize=(10, 5))
|
|
235
|
+
p = splot(results, ax, ylim, xlab, ylab, title, legend, ref_line, theming)
|
|
236
|
+
plt.tight_layout()
|
|
237
|
+
plt.show()
|
|
238
|
+
|
|
239
|
+
else:
|
|
240
|
+
fig, ax = plt.subplots(figsize=(10, 5))
|
|
241
|
+
p = gplot(results, ax, ylim, xlab, ylab, title, xgap, legend, ref_line, theming)
|
|
242
|
+
plt.tight_layout()
|
|
243
|
+
plt.show()
|
|
244
|
+
|
|
245
|
+
return p
|
|
File without changes
|