From daeb3cdd884a0c9952830ac28042bccde29269e0 Mon Sep 17 00:00:00 2001 From: Alex Moreo Date: Wed, 10 Mar 2021 11:40:28 +0100 Subject: [PATCH] cleaning master --- NewMethods/fgsld/__init__.py | 0 NewMethods/fgsld/em.py | 116 -------- NewMethods/fgsld/fglsd_test.py | 75 ----- NewMethods/fgsld/fine_grained_sld.py | 107 -------- NewMethods/fgsld/metrics.py | 260 ------------------ NewMethods/fgsld/plot_fglsd.png | Bin 165809 -> 0 bytes NewMethods/methods.py | 174 ------------ NewMethods/new_experiments.py | 48 ---- NewMethods/new_gen_tables.py | 148 ---------- NewMethods/settings.py | 4 - TweetSentQuant/Gao_Sebastiani_results.txt | 89 ------ TweetSentQuant/evaluate_results.py | 35 --- TweetSentQuant/experiments.py | 214 --------------- TweetSentQuant/gen_plots.py | 95 ------- TweetSentQuant/gen_tables.py | 145 ---------- TweetSentQuant/settings.py | 8 - TweetSentQuant/tabular.py | 318 ---------------------- TweetSentQuant/util.py | 89 ------ 18 files changed, 1925 deletions(-) delete mode 100644 NewMethods/fgsld/__init__.py delete mode 100644 NewMethods/fgsld/em.py delete mode 100644 NewMethods/fgsld/fglsd_test.py delete mode 100644 NewMethods/fgsld/fine_grained_sld.py delete mode 100644 NewMethods/fgsld/metrics.py delete mode 100644 NewMethods/fgsld/plot_fglsd.png delete mode 100644 NewMethods/methods.py delete mode 100644 NewMethods/new_experiments.py delete mode 100644 NewMethods/new_gen_tables.py delete mode 100644 NewMethods/settings.py delete mode 100644 TweetSentQuant/Gao_Sebastiani_results.txt delete mode 100644 TweetSentQuant/evaluate_results.py delete mode 100644 TweetSentQuant/experiments.py delete mode 100644 TweetSentQuant/gen_plots.py delete mode 100644 TweetSentQuant/gen_tables.py delete mode 100644 TweetSentQuant/settings.py delete mode 100644 TweetSentQuant/tabular.py delete mode 100644 TweetSentQuant/util.py diff --git a/NewMethods/fgsld/__init__.py b/NewMethods/fgsld/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/NewMethods/fgsld/em.py b/NewMethods/fgsld/em.py deleted file mode 100644 index 0f6ab6d..0000000 --- a/NewMethods/fgsld/em.py +++ /dev/null @@ -1,116 +0,0 @@ -import numpy as np -import logging -from collections import namedtuple - -from sklearn.metrics import brier_score_loss -from sklearn.preprocessing import MultiLabelBinarizer - -from metrics import smoothmacroF1, isometric_brier_decomposition, isomerous_brier_decomposition - -History = namedtuple('History', ('posteriors', 'priors', 'y', 'iteration', 'stopping_criterium')) -MeasureSingleHistory = namedtuple('MeasureSingleHistory', ( - 'soft_acc', 'soft_f1', 'abs_errors', 'test_priors', 'train_priors', 'predict_priors', 'brier', - 'isometric_ref_loss', 'isometric_cal_loss', 'isomerous_ref_loss', 'isomerous_cal_loss' -)) - - -def get_measures_single_history(history: History, multi_class) -> MeasureSingleHistory: - y = history.y - - y_bin = MultiLabelBinarizer(classes=list(range(history.posteriors.shape[1]))).fit_transform(np.expand_dims(y, 1)) - - soft_acc = soft_accuracy(y, history.posteriors) - f1 = smoothmacroF1(y_bin, history.posteriors) - - if multi_class: - test_priors = np.mean(y_bin, 0) - abs_errors = abs(test_priors - history.priors) - train_priors = history.priors - predict_priors = np.mean(history.posteriors, 0) - brier = 0 - else: - test_priors = np.mean(y_bin, 0)[1] - abs_errors = abs(test_priors - history.priors[1]) - train_priors = history.priors[1] - predict_priors = np.mean(history.posteriors[:, 1]) - brier = brier_score_loss(y, history.posteriors[:, 1]) - - isometric_cal_loss, isometric_ref_loss = isometric_brier_decomposition(y, history.posteriors) - isomerous_em_cal_loss, isomerous_em_ref_loss = isomerous_brier_decomposition(y, history.posteriors) - - return MeasureSingleHistory( - soft_acc, f1, abs_errors, test_priors, train_priors, predict_priors, brier, isometric_ref_loss, - isometric_cal_loss, isomerous_em_ref_loss, isomerous_em_cal_loss - ) - - -def soft_accuracy(y, posteriors): - return sum(posteriors[y == c][:, c].sum() for c in range(posteriors.shape[1])) / posteriors.sum() - - -def soft_f1(y, posteriors): - cont_matrix = { - 'TPM': posteriors[y == 1][:, 1].sum(), - 'TNM': posteriors[y == 0][:, 0].sum(), - 'FPM': posteriors[y == 0][:, 1].sum(), - 'FNM': posteriors[y == 1][:, 0].sum() - } - precision = cont_matrix['TPM'] / (cont_matrix['TPM'] + cont_matrix['FPM']) - recall = cont_matrix['TPM'] / (cont_matrix['TPM'] + cont_matrix['FNM']) - return 2 * (precision * recall / (precision + recall)) - - -def em(y, posteriors_zero, priors_zero, epsilon=1e-6, multi_class=False, return_posteriors_hist=False): - """ - Implements the prior correction method based on EM presented in: - "Adjusting the Outputs of a Classifier to New a Priori Probabilities: A Simple Procedure" - Saerens, Latinne and Decaestecker, 2002 - http://www.isys.ucl.ac.be/staff/marco/Publications/Saerens2002a.pdf - - :param y: true labels of test items, to measure accuracy, precision and recall. - :param posteriors_zero: posterior probabilities on test items, as returned by a classifier. A 2D-array with shape - Ø(items, classes). - :param priors_zero: prior probabilities measured on training set. - :param epsilon: stopping threshold. - :param multi_class: whether the algorithm is running in a multi-label multi-class context or not. - :param return_posteriors_hist: whether posteriors for each iteration should be returned or not. If true, the returned - posteriors_s will actually be the list of posteriors for every iteration. - :return: posteriors_s, priors_s, history: final adjusted posteriors, final adjusted priors, a list of length s - where each element is a tuple with the step counter, the current priors (as list), the stopping criterium value, - accuracy, precision and recall. - """ - s = 0 - priors_s = np.copy(priors_zero) - posteriors_s = np.copy(posteriors_zero) - if return_posteriors_hist: - posteriors_hist = [posteriors_s.copy()] - val = 2 * epsilon - history = list() - history.append(get_measures_single_history(History(posteriors_zero, priors_zero, y, s, 1), multi_class)) - while not val < epsilon and s < 999: - # M step - priors_s_minus_one = priors_s.copy() - priors_s = posteriors_s.mean(0) - - # E step - ratios = priors_s / priors_zero - denominators = 0 - for c in range(priors_zero.shape[0]): - denominators += ratios[c] * posteriors_zero[:, c] - for c in range(priors_zero.shape[0]): - posteriors_s[:, c] = ratios[c] * posteriors_zero[:, c] / denominators - - # check for stop - val = 0 - for i in range(len(priors_s_minus_one)): - val += abs(priors_s_minus_one[i] - priors_s[i]) - - logging.debug(f"Em iteration: {s}; Val: {val}") - s += 1 - if return_posteriors_hist: - posteriors_hist.append(posteriors_s.copy()) - history.append(get_measures_single_history(History(posteriors_s, priors_s, y, s, val), multi_class)) - - if return_posteriors_hist: - return posteriors_hist, priors_s, history - return posteriors_s, priors_s, history diff --git a/NewMethods/fgsld/fglsd_test.py b/NewMethods/fgsld/fglsd_test.py deleted file mode 100644 index 4735a53..0000000 --- a/NewMethods/fgsld/fglsd_test.py +++ /dev/null @@ -1,75 +0,0 @@ -from sklearn.calibration import CalibratedClassifierCV -from sklearn.svm import LinearSVC - -from NewMethods.fgsld.fine_grained_sld import FineGrainedSLD -from method.aggregative import EMQ, CC -from quapy.data import LabelledCollection -from quapy.method.base import BaseQuantifier -import quapy as qp -import quapy.functional as F -from sklearn.linear_model import LogisticRegression - - -class FakeFGLSD(BaseQuantifier): - def __init__(self, learner, nbins, isomerous): - self.learner = learner - self.nbins = nbins - self.isomerous = isomerous - - def fit(self, data: LabelledCollection): - self.Xtr, self.ytr = data.Xy - self.learner.fit(self.Xtr, self.ytr) - return self - - def quantify(self, instances): - tr_priors = F.prevalence_from_labels(self.ytr, n_classes=2) - fgsld = FineGrainedSLD(self.Xtr, instances, self.ytr, tr_priors, self.learner, n_bins=self.nbins) - priors, posteriors = fgsld.run(self.isomerous) - return priors - - def get_params(self, deep=True): - pass - - def set_params(self, **parameters): - pass - - - -qp.environ['SAMPLE_SIZE'] = 500 - -dataset = qp.datasets.fetch_reviews('hp') -qp.data.preprocessing.text2tfidf(dataset, min_df=5, inplace=True) - -training = dataset.training -test = dataset.test - -cls = CalibratedClassifierCV(LinearSVC()) - - -method_names, true_prevs, estim_prevs, tr_prevs = [], [], [], [] - -for model, model_name in [ - (CC(cls), 'CC'), - (FakeFGLSD(cls, nbins=1, isomerous=False), 'FGSLD-1'), - (FakeFGLSD(cls, nbins=2, isomerous=False), 'FGSLD-2'), - #(FakeFGLSD(cls, nbins=5, isomerous=False), 'FGSLD-5'), - #(FakeFGLSD(cls, nbins=10, isomerous=False), 'FGSLD-10'), - #(FakeFGLSD(cls, nbins=50, isomerous=False), 'FGSLD-50'), - #(FakeFGLSD(cls, nbins=100, isomerous=False), 'FGSLD-100'), -# (FakeFGLSD(cls, nbins=1, isomerous=False), 'FGSLD-1'), - #(FakeFGLSD(cls, nbins=10, isomerous=True), 'FGSLD-10-ISO'), - # (FakeFGLSD(cls, nbins=50, isomerous=False), 'FGSLD-50'), - (EMQ(cls), 'SLD'), -]: - print('running ', model_name) - model.fit(training) - true_prev, estim_prev = qp.evaluation.artificial_sampling_prediction( - model, test, qp.environ['SAMPLE_SIZE'], n_repetitions=10, n_prevpoints=21, n_jobs=-1 - ) - method_names.append(model_name) - true_prevs.append(true_prev) - estim_prevs.append(estim_prev) - tr_prevs.append(training.prevalence()) - - -qp.plot.binary_diagonal(method_names, true_prevs, estim_prevs, train_prev=tr_prevs[0], savepath='./plot_fglsd.png') diff --git a/NewMethods/fgsld/fine_grained_sld.py b/NewMethods/fgsld/fine_grained_sld.py deleted file mode 100644 index f955491..0000000 --- a/NewMethods/fgsld/fine_grained_sld.py +++ /dev/null @@ -1,107 +0,0 @@ -import numpy as np -from metrics import isomerous_bins, isometric_bins -from em import History, get_measures_single_history - - -class FineGrainedSLD: - def __init__(self, x_tr, x_te, y_tr, tr_priors, clf, n_bins=10): - self.y_tr = y_tr - self.clf = clf - self.tr_priors = tr_priors - self.tr_preds = clf.predict_proba(x_tr) - self.te_preds = clf.predict_proba(x_te) - self.n_bins = n_bins - self.history: [History] = [] - self.multi_class = False - - def run(self, isomerous_binning, epsilon=1e-6, compute_bins_at_every_iter=False, return_posteriors_hist=False): - """ - Run the FGSLD algorithm. - - :param isomerous_binning: whether to use isomerous or isometric binning. - :param epsilon: stopping condition. - :param compute_bins_at_every_iter: whether FGSLD should recompute the posterior bins at every iteration or not. - :param return_posteriors_hist: whether to return posteriors at every iteration or not. - :return: If `return_posteriors_hist` is true, the returned posteriors will be a list of numpy arrays, else a single numpy array with posteriors at last iteration. - """ - smoothing_tr = 1 / (2 * self.y_tr.shape[0]) - smoothing_te = smoothing_tr - s = 0 - tr_bin_priors = np.zeros((self.n_bins, self.tr_preds.shape[1]), dtype=np.float) - te_bin_priors = np.zeros((self.n_bins, self.te_preds.shape[1]), dtype=np.float) - tr_bins = self.__create_bins(training=True, isomerous_binning=isomerous_binning) - te_bins = self.__create_bins(training=False, isomerous_binning=isomerous_binning) - self.__compute_bins_priors(tr_bin_priors, self.tr_preds, tr_bins, smoothing_tr) - - val = 2 * epsilon - if return_posteriors_hist: - posteriors_hist = [self.te_preds.copy()] - while not val < epsilon and s < 1000: - assert np.all(np.around(self.te_preds.sum(axis=1), 4) == 1), f"Probabilities do not sum to 1:\ns={s}, " \ - f"probs={self.te_preds.sum(axis=1)}" - if compute_bins_at_every_iter: - te_bins = self.__create_bins(training=False, isomerous_binning=isomerous_binning) - - if s == 0: - te_bin_priors_prev = tr_bin_priors.copy() - else: - te_bin_priors_prev = te_bin_priors.copy() - self.__compute_bins_priors(te_bin_priors, self.te_preds, te_bins, smoothing_te) - - te_preds_cp = self.te_preds.copy() - for label_idx, bins in te_bins.items(): - for i, bin_ in enumerate(bins): - if bin_.shape[0] == 0: - continue - self.te_preds[:, label_idx][bin_] = (te_preds_cp[:, label_idx][bin_]) * \ - (te_bin_priors[i][label_idx] / te_bin_priors_prev[i][label_idx]) - - # Normalization step - self.te_preds = (self.te_preds.T / self.te_preds.sum(axis=1)).T - - val = 0 - for label_idx in range(te_bin_priors.shape[1]): - if (temp := max(abs((te_bin_priors[:, label_idx] / te_bin_priors_prev[:, label_idx]) - 1))) > val: - val = temp - s += 1 - if return_posteriors_hist: - posteriors_hist.append(self.te_preds.copy()) - if return_posteriors_hist: - return self.te_preds.mean(axis=0), posteriors_hist - return self.te_preds.mean(axis=0), self.te_preds - - def __compute_bins_priors(self, bin_priors_placeholder, posteriors, bins, smoothing): - for label_idx, bins in bins.items(): - for i, bin_ in enumerate(bins): - if bin_.shape[0] == 0: - bin_priors_placeholder[i, label_idx] = smoothing - continue - numerator = posteriors[:, label_idx][bin_].mean() - bin_prior = (numerator + smoothing) / (1 + self.n_bins * smoothing) # normalize priors - bin_priors_placeholder[i, label_idx] = bin_prior - - def __find_bin_idx(self, label_bins: [np.array], idx: int or list): - if hasattr(idx, '__len__'): - idxs = np.zeros(len(idx), dtype=np.int) - for i, bin_ in enumerate(label_bins): - for j, id_ in enumerate(idx): - if id_ in bin_: - idxs[j] = i - return idxs - else: - for i, bin_ in enumerate(label_bins): - if idx in bin_: - return i - - def __create_bins(self, training: bool, isomerous_binning: bool): - bins = {} - preds = self.tr_preds if training else self.te_preds - if isomerous_binning: - for label_idx in range(preds.shape[1]): - bins[label_idx] = isomerous_bins(label_idx, preds, self.n_bins) - else: - intervals = np.linspace(0., 1., num=self.n_bins, endpoint=False) - for label_idx in range(preds.shape[1]): - bins_ = isometric_bins(label_idx, preds, intervals, 0.1) - bins[label_idx] = [bins_[i] for i in intervals] - return bins diff --git a/NewMethods/fgsld/metrics.py b/NewMethods/fgsld/metrics.py deleted file mode 100644 index c95e757..0000000 --- a/NewMethods/fgsld/metrics.py +++ /dev/null @@ -1,260 +0,0 @@ -import numpy as np - -""" -Scikit learn provides a full set of evaluation metrics, but they treat special cases differently. -I.e., when the number of true positives, false positives, and false negatives ammount to 0, all -affected metrics (precision, recall, and thus f1) output 0 in Scikit learn. -We adhere to the common practice of outputting 1 in this case since the classifier has correctly -classified all examples as negatives. -""" - - -def isometric_brier_decomposition(true_labels, predicted_labels, bin_intervals=np.arange(0., 1.1, 0.1), step=0.1): - """ - The Isometric Brier decomposition or score is obtained by partitioning U into intervals I_1j,...,I_bj that - have equal length, where U is the total size of our test set (i.e., true_labels.shape[0]). This means that, - if b=10 then I_1j = [0.0,0.1), I_2j = [0.2, 0.3),...,I_bj = [0.9,1.0). - - bin_intervals is a numpy.array containing the range of the different intervals. Since it is a single dimensional - array, for every interval I_n we take the posterior probabilities Pr_n(x) such that I_n <= Pr_n(x) < I_n + step. - This variable defaults to np.arange(0., 1.0, 0.1), i.e. an array like [0.1, 0.2, ..., 1.0]. - - :return: a tuple (calibration score, refinement score) - """ - labels = set(true_labels) - calibration_score, refinement_score = 0.0, 0.0 - for i in range(len(labels)): - bins = isometric_bins(i, predicted_labels, bin_intervals, step) - c_score, r_score = brier_decomposition(bins.values(), true_labels, predicted_labels, class_=i) - calibration_score += c_score - refinement_score += r_score - return calibration_score, refinement_score - - -def isomerous_brier_decomposition(true_labels, predicted_labels, n=10): - """ - The Isomerous Brier decomposition or score is obtained by partitioning U into intervals I_1j,...,I_bj such that - the corresponding bins B_1j,...,B_bj have equal size, where U is our test set. This means that, for every x' in - B_sj and x'' in B_tj with s < t, it holds that Pr(c_j|x') <= Pr(c_j|x'') and |B_sj| == |B_tj|, for any s,t in - {1,...,b}. - - The n variable holds the number of bins we want (defaults to 10). Notice that we perform a numpy.array_split on - the predicted_labels, creating l % n sub-arrays of size l//n + 1 and the rest of size l//n, where l is the length - of the array. - - :return: a tuple (calibration score, refinement score) - """ - - labels = set(true_labels) - calibration_score, refinement_score = 0.0, 0.0 - for i in range(len(labels)): - bins = isomerous_bins(i, predicted_labels, n) - c_score, r_score = brier_decomposition(bins, true_labels, predicted_labels, class_=i) - calibration_score += c_score - refinement_score += r_score - return calibration_score, refinement_score - - -def brier_decomposition(bins, true_labels, predicted_labels, class_=1): - """ - :param bins: must be an array of indices - :return: a tuple (calibration_score, refinement_score) - """ - calibration_score = 0 - refinement_score = 0 - for bin_ in bins: - if bin_.size <= 0: - continue - v_x = (bin_.shape[0] / true_labels.shape[0]) - ro_x = np.mean(true_labels[bin_] == class_) - calibration_score += v_x * (predicted_labels[bin_, class_].mean() - ro_x)**2 - refinement_score += (v_x * ro_x) * (1 - ro_x) - labels_len = len(set(true_labels)) - return calibration_score / (labels_len * len(bins)), refinement_score / (labels_len * len(bins)) - - -def isometric_bins(label_index, predicted_labels, bin_intervals, step): - predicted_class_label = predicted_labels[:, label_index] - return {interv: np.where(np.logical_and(interv <= predicted_class_label, predicted_class_label < interv + step))[0] - for interv in bin_intervals} - - -def isomerous_bins(label_index, predicted_labels, n): - sorted_indices = predicted_labels[:, label_index].argsort() - return np.array_split(sorted_indices, n) - - -# true_labels and predicted_labels are two matrices in sklearn.preprocessing.MultiLabelBinarizer format -def macroF1(true_labels, predicted_labels): - return macro_average(true_labels, predicted_labels, f1) - - -# true_labels and predicted_labels are two matrices in sklearn.preprocessing.MultiLabelBinarizer format -def microF1(true_labels, predicted_labels): - return micro_average(true_labels, predicted_labels, f1) - - -# true_labels and predicted_labels are two matrices in sklearn.preprocessing.MultiLabelBinarizer format -def macroK(true_labels, predicted_labels): - return macro_average(true_labels, predicted_labels, K) - - -# true_labels and predicted_labels are two matrices in sklearn.preprocessing.MultiLabelBinarizer format -def microK(true_labels, predicted_labels): - return micro_average(true_labels, predicted_labels, K) - - -# true_labels is a matrix in sklearn.preprocessing.MultiLabelBinarizer format and posterior_probabilities is a matrix -# of the same shape containing real values in [0,1] -def smoothmacroF1(true_labels, posterior_probabilities): - return macro_average(true_labels, posterior_probabilities, f1, metric_statistics=soft_single_metric_statistics) - - -# true_labels is a matrix in sklearn.preprocessing.MultiLabelBinarizer format and posterior_probabilities is a matrix -# of the same shape containing real values in [0,1] -def smoothmicroF1(true_labels, posterior_probabilities): - return micro_average(true_labels, posterior_probabilities, f1, metric_statistics=soft_single_metric_statistics) - - -# true_labels is a matrix in sklearn.preprocessing.MultiLabelBinarizer format and posterior_probabilities is a matrix -# of the same shape containing real values in [0,1] -def smoothmacroK(true_labels, posterior_probabilities): - return macro_average(true_labels, posterior_probabilities, K, metric_statistics=soft_single_metric_statistics) - - -# true_labels is a matrix in sklearn.preprocessing.MultiLabelBinarizer format and posterior_probabilities is a matrix -# of the same shape containing real values in [0,1] -def smoothmicroK(true_labels, posterior_probabilities): - return micro_average(true_labels, posterior_probabilities, K, metric_statistics=soft_single_metric_statistics) - - -class ContTable: - def __init__(self, tp=0, tn=0, fp=0, fn=0): - self.tp = tp - self.tn = tn - self.fp = fp - self.fn = fn - - def get_d(self): return self.tp + self.tn + self.fp + self.fn - - def get_c(self): return self.tp + self.fn - - def get_not_c(self): return self.tn + self.fp - - def get_f(self): return self.tp + self.fp - - def get_not_f(self): return self.tn + self.fn - - def p_c(self): return (1.0 * self.get_c()) / self.get_d() - - def p_not_c(self): return 1.0 - self.p_c() - - def p_f(self): return (1.0 * self.get_f()) / self.get_d() - - def p_not_f(self): return 1.0 - self.p_f() - - def p_tp(self): return (1.0 * self.tp) / self.get_d() - - def p_tn(self): return (1.0 * self.tn) / self.get_d() - - def p_fp(self): return (1.0 * self.fp) / self.get_d() - - def p_fn(self): return (1.0 * self.fn) / self.get_d() - - def tpr(self): - c = 1.0 * self.get_c() - return self.tp / c if c > 0.0 else 0.0 - - def fpr(self): - _c = 1.0 * self.get_not_c() - return self.fp / _c if _c > 0.0 else 0.0 - - def __add__(self, other): - return ContTable(tp=self.tp + other.tp, tn=self.tn + other.tn, fp=self.fp + other.fp, fn=self.fn + other.fn) - - -def accuracy(cell): - return (cell.tp + cell.tn) * 1.0 / (cell.tp + cell.fp + cell.fn + cell.tn) - - -def f1(cell): - num = 2.0 * cell.tp - den = 2.0 * cell.tp + cell.fp + cell.fn - if den > 0: return num / den - # we define f1 to be 1 if den==0 since the classifier has correctly classified all instances as negative - return 1.0 - - -def K(cell): - specificity, recall = 0., 0. - - AN = cell.tn + cell.fp - if AN != 0: - specificity = cell.tn * 1. / AN - - AP = cell.tp + cell.fn - if AP != 0: - recall = cell.tp * 1. / AP - - if AP == 0: - return 2. * specificity - 1. - elif AN == 0: - return 2. * recall - 1. - else: - return specificity + recall - 1. - - -# computes the (hard) counters tp, fp, fn, and tn fron a true and predicted vectors of hard decisions -# true_labels and predicted_labels are two vectors of shape (number_documents,) -def hard_single_metric_statistics(true_labels, predicted_labels): - assert len(true_labels) == len(predicted_labels), "Format not consistent between true and predicted labels." - nd = len(true_labels) - tp = np.sum(predicted_labels[true_labels == 1]) - fp = np.sum(predicted_labels[true_labels == 0]) - fn = np.sum(true_labels[predicted_labels == 0]) - tn = nd - (tp + fp + fn) - return ContTable(tp=tp, tn=tn, fp=fp, fn=fn) - - -# computes the (soft) contingency table where tp, fp, fn, and tn are the cumulative masses for the posterioir -# probabilitiesfron with respect to the true binary labels -# true_labels and posterior_probabilities are two vectors of shape (number_documents,) -def soft_single_metric_statistics(true_labels, posterior_probabilities): - assert len(true_labels) == len(posterior_probabilities), "Format not consistent between true and predicted labels." - pos_probs = posterior_probabilities[true_labels == 1] - neg_probs = posterior_probabilities[true_labels == 0] - tp = np.sum(pos_probs) - fn = np.sum(1. - pos_probs) - fp = np.sum(neg_probs) - tn = np.sum(1. - neg_probs) - return ContTable(tp=tp, tn=tn, fp=fp, fn=fn) - - -# if the classifier is single class, then the prediction is a vector of shape=(nD,) which causes issues when compared -# to the true labels (of shape=(nD,1)). This method increases the dimensions of the predictions. -def __check_consistency_and_adapt(true_labels, predictions): - if predictions.ndim == 1: - return __check_consistency_and_adapt(true_labels, np.expand_dims(predictions, axis=1)) - if true_labels.ndim == 1: - return __check_consistency_and_adapt(np.expand_dims(true_labels, axis=1), predictions) - if true_labels.shape != predictions.shape: - raise ValueError("True and predicted label matrices shapes are inconsistent %s %s." - % (true_labels.shape, predictions.shape)) - _, nC = true_labels.shape - return true_labels, predictions, nC - - -def macro_average(true_labels, predicted_labels, metric, metric_statistics=hard_single_metric_statistics): - true_labels, predicted_labels, nC = __check_consistency_and_adapt(true_labels, predicted_labels) - return np.mean([metric(metric_statistics(true_labels[:, c], predicted_labels[:, c])) for c in range(nC)]) - - -def micro_average(true_labels, predicted_labels, metric, metric_statistics=hard_single_metric_statistics): - true_labels, predicted_labels, nC = __check_consistency_and_adapt(true_labels, predicted_labels) - - accum = ContTable() - for c in range(nC): - other = metric_statistics(true_labels[:, c], predicted_labels[:, c]) - accum = accum + other - - return metric(accum) diff --git a/NewMethods/fgsld/plot_fglsd.png b/NewMethods/fgsld/plot_fglsd.png deleted file mode 100644 index e434eadd72f21ab3a304ea83f3cc6c7ccff50bb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165809 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 diff --git a/NewMethods/methods.py b/NewMethods/methods.py deleted file mode 100644 index b47927d..0000000 --- a/NewMethods/methods.py +++ /dev/null @@ -1,174 +0,0 @@ -import numpy as np -from sklearn.base import BaseEstimator -from sklearn.decomposition import PCA -from sklearn.preprocessing import StandardScaler - -import quapy as qp -from typing import Union - -from quapy.data import LabelledCollection -from quapy.method.base import BaseQuantifier, BinaryQuantifier -from quapy.method.aggregative import PACC, EMQ, HDy -import quapy.functional as F -from tqdm import tqdm -from scipy.sparse import issparse, csr_matrix -import scipy - - -class PACCSLD(PACC): - """ - This method combines the EMQ improved posterior probabilities with PACC. - Note: the posterior probabilities are re-calibrated with EMQ only during prediction, and not also during fit since, - for PACC, the validation split is known to have the same prevalence as the training set (this is because the split - is stratified) and thus the posterior probabilities should not be re-calibrated for a different prior (it actually - happens to degrades performance). - """ - - def fit(self, data: qp.data.LabelledCollection, fit_learner=True, val_split:Union[float, int, qp.data.LabelledCollection]=0.4): - self.train_prevalence = F.prevalence_from_labels(data.labels, data.n_classes) - return super(PACCSLD, self).fit(data, fit_learner, val_split) - - def aggregate(self, classif_posteriors): - priors, posteriors = EMQ.EM(self.train_prevalence, classif_posteriors, epsilon=1e-4) - return super(PACCSLD, self).aggregate(posteriors) - - -class HDySLD(HDy): - """ - This method combines the EMQ improved posterior probabilities with HDy. - Note: [same as PACCSLD] - """ - def fit(self, data: qp.data.LabelledCollection, fit_learner=True, - val_split: Union[float, int, qp.data.LabelledCollection] = 0.4): - self.train_prevalence = F.prevalence_from_labels(data.labels, data.n_classes) - return super(HDySLD, self).fit(data, fit_learner, val_split) - - def aggregate(self, classif_posteriors): - priors, posteriors = EMQ.EM(self.train_prevalence, classif_posteriors, epsilon=1e-4) - return super(HDySLD, self).aggregate(posteriors) - - - -class AveragePoolQuantification(BinaryQuantifier): - def __init__(self, learner, sample_size, trials, n_components=-1, zscore=False): - self.learner = learner - self.sample_size = sample_size - self.trials = trials - - self.do_zscore = zscore - self.zscore = StandardScaler() if self.do_zscore else None - - self.do_pca = n_components>0 - self.pca = PCA(n_components) if self.do_pca else None - - def fit(self, data: LabelledCollection): - training, validation = data.split_stratified(train_prop=0.7) - - X, y = [], [] - - nprevpoints = F.get_nprevpoints_approximation(self.trials, data.n_classes) - for sample in tqdm( - training.artificial_sampling_generator(self.sample_size, n_prevalences=nprevpoints, repeats=1), - desc='generating averages' - ): - X.append(sample.instances.mean(axis=0)) - y.append(sample.prevalence()[1]) - while len(X) < self.trials: - sample = training.sampling(self.sample_size, F.uniform_simplex_sampling(data.n_classes)) - X.append(sample.instances.mean(axis=0)) - y.append(sample.prevalence()) - X = np.asarray(np.vstack(X)) - y = np.asarray(y) - - if self.do_pca: - X = self.pca.fit_transform(X) - print(X.shape) - - if self.do_zscore: - X = self.zscore.fit_transform(X) - - print('training regressor...') - self.regressor = self.learner.fit(X, y) - - # correction at 0: - print('getting corrections...') - X0 = np.asarray(np.vstack([validation.sampling(self.sample_size, 0., shuffle=False).instances.mean(axis=0) for _ in range(100)])) - X1 = np.asarray(np.vstack([validation.sampling(self.sample_size, 1., shuffle=False).instances.mean(axis=0) for _ in range(100)])) - - if self.do_pca: - X0 = self.pca.transform(X0) - X1 = self.pca.transform(X1) - - if self.do_zscore: - X0 = self.zscore.transform(X0) - X1 = self.zscore.transform(X1) - - self.correction_0 = self.regressor.predict(X0).mean() - self.correction_1 = self.regressor.predict(X1).mean() - - print('correction-0', self.correction_0) - print('correction-1', self.correction_1) - print('done') - - def quantify(self, instances): - ave = np.asarray(instances.mean(axis=0)) - - if self.do_pca: - ave = self.pca.transform(ave) - if self.do_zscore: - ave = self.zscore.transform(ave) - phat = self.regressor.predict(ave).item() - phat = np.clip((phat-self.correction_0)/(self.correction_1-self.correction_0), 0, 1) - return np.asarray([1-phat, phat]) - - def set_params(self, **parameters): - self.learner.set_params(**parameters) - - def get_params(self, deep=True): - return self.learner.get_params(deep=deep) - - -class WinnowOrthogonal(BaseEstimator): - - def __init__(self): - pass - - def fit(self, X, y): - self.classes_ = np.asarray(sorted(np.unique(y))) - w1 = np.asarray(X[y == 0].mean(axis=0)).flatten() - w2 = np.asarray(X[y == 1].mean(axis=0)).flatten() - diff = w2 - w1 - orth = np.ones_like(diff) - orth[0] = -diff[1:].sum() / diff[0] - orth /= np.linalg.norm(orth) - self.w = orth - self.b = w1.dot(orth) - return self - - def decision_function(self, X): - if issparse(X): - Z = X.dot(csr_matrix(self.w).T).toarray().flatten() - return Z - self.b - else: - return np.matmul(X, self.w) - self.b - - def predict(self, X): - return 1 * (self.decision_function(X) > 0) - - def split(self, X, y): - s = self.predict(X) - X0a = X[np.logical_and(y == 0, s == 0)] - X0b = X[np.logical_and(y == 0, s == 1)] - X1a = X[np.logical_and(y == 1, s == 0)] - X1b = X[np.logical_and(y == 1, s == 1)] - y0a = np.zeros(X0a.shape[0], dtype=np.int) - y0b = np.zeros(X0b.shape[0], dtype=np.int) - y1a = np.ones(X1a.shape[0], dtype=np.int) - y1b = np.ones(X1b.shape[0], dtype=np.int) - return X0a, X0b, X1a, X1b, y0a, y0b, y1a, y1b - - def get_params(self): - return {} - - def set_params(self, **params): - pass diff --git a/NewMethods/new_experiments.py b/NewMethods/new_experiments.py deleted file mode 100644 index d60b158..0000000 --- a/NewMethods/new_experiments.py +++ /dev/null @@ -1,48 +0,0 @@ -from sklearn.linear_model import LogisticRegression -import quapy as qp -from classification.methods import PCALR -from method.meta import QuaNet -from quapy.method.aggregative import * -from NewMethods.methods import * -from experiments import run, SAMPLE_SIZE -import numpy as np -import itertools -from joblib import Parallel, delayed -import settings -import argparse -import torch - -parser = argparse.ArgumentParser(description='Run experiments for Tweeter Sentiment Quantification') -parser.add_argument('results', metavar='RESULT_PATH', type=str, help='path to the directory where to store the results') -#parser.add_argument('svmperfpath', metavar='SVMPERF_PATH', type=str, help='path to the directory with svmperf') -args = parser.parse_args() - - -def quantification_models(): - def newLR(): - return LogisticRegression(max_iter=1000, solver='lbfgs', n_jobs=-1) - __C_range = np.logspace(-4, 5, 10) - lr_params = {'C': __C_range, 'class_weight': [None, 'balanced']} - svmperf_params = {'C': __C_range} - #yield 'paccsld', PACCSLD(newLR()), lr_params - yield 'hdysld', OneVsAll(HDySLD(newLR())), lr_params # <-- promising! - - #device = 'cuda' if torch.cuda.is_available() else 'cpu' - #print(f'Running QuaNet in {device}') - #yield 'quanet', QuaNet(PCALR(**newLR().get_params()), SAMPLE_SIZE, device=device), lr_params - - -if __name__ == '__main__': - - print(f'Result folder: {args.results}') - np.random.seed(0) - - optim_losses = ['mae'] - datasets = qp.datasets.TWITTER_SENTIMENT_DATASETS_TRAIN - models = quantification_models() - - results = Parallel(n_jobs=settings.N_JOBS)( - delayed(run)(experiment) for experiment in itertools.product(optim_losses, datasets, models) - ) - - diff --git a/NewMethods/new_gen_tables.py b/NewMethods/new_gen_tables.py deleted file mode 100644 index c6aeb7e..0000000 --- a/NewMethods/new_gen_tables.py +++ /dev/null @@ -1,148 +0,0 @@ -import quapy as qp -import numpy as np -from os import makedirs -import sys, os -import pickle -from experiments import result_path -from gen_tables import save_table, experiment_errors -from tabular import Table -import argparse - -tables_path = './tables' -MAXTONE = 50 # sets the intensity of the maximum color reached by the worst (red) and best (green) results - -makedirs(tables_path, exist_ok=True) - -sample_size = 100 -qp.environ['SAMPLE_SIZE'] = sample_size - - -nice = { - 'mae':'AE', - 'mrae':'RAE', - 'ae':'AE', - 'rae':'RAE', - 'svmkld': 'SVM(KLD)', - 'svmnkld': 'SVM(NKLD)', - 'svmq': 'SVM(Q)', - 'svmae': 'SVM(AE)', - 'svmnae': 'SVM(NAE)', - 'svmmae': 'SVM(AE)', - 'svmmrae': 'SVM(RAE)', - 'quanet': 'QuaNet', - 'hdy': 'HDy', - 'hdysld': 'HDy-SLD', - 'dys': 'DyS', - 'svmperf':'', - 'sanders': 'Sanders', - 'semeval13': 'SemEval13', - 'semeval14': 'SemEval14', - 'semeval15': 'SemEval15', - 'semeval16': 'SemEval16', - 'Average': 'Average' -} - - -def nicerm(key): - return '\mathrm{'+nice[key]+'}' - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Generate tables for Tweeter Sentiment Quantification') - parser.add_argument('results', metavar='RESULT_PATH', type=str, - help='path to the directory containing the results of the methods tested in Gao & Sebastiani') - parser.add_argument('newresults', metavar='RESULT_PATH', type=str, - help='path to the directory containing the results for the experimental methods') - args = parser.parse_args() - - datasets = qp.datasets.TWITTER_SENTIMENT_DATASETS_TEST - evaluation_measures = [qp.error.ae, qp.error.rae] - gao_seb_methods = ['cc', 'acc', 'pcc', 'pacc', 'sld', 'svmq', 'svmkld', 'svmnkld'] - new_methods = ['hdy'] # methods added to the Gao & Sebastiani methods - experimental_methods = ['hdysld'] # experimental - - for i, eval_func in enumerate(evaluation_measures): - - # Tables evaluation scores for AE and RAE (two tables) - # ---------------------------------------------------- - - eval_name = eval_func.__name__ - - added_methods = ['svmm' + eval_name] + new_methods - methods = gao_seb_methods + added_methods + experimental_methods - nold_methods = len(gao_seb_methods) - nnew_methods = len(added_methods) - nexp_methods = len(experimental_methods) - - # fill data table - table = Table(benchmarks=datasets, methods=methods) - for dataset in datasets: - for method in methods: - if method in experimental_methods: - path = args.newresults - else: - path = args.results - table.add(dataset, method, experiment_errors(path, dataset, method, eval_name)) - - # write the latex table - tabular = """ - \\begin{tabularx}{\\textwidth}{|c||""" + ('Y|'*nold_methods) + '|' + ('Y|'*nnew_methods) + '|' + ('Y|'*nexp_methods) + """} \hline - & \multicolumn{"""+str(nold_methods)+"""}{c||}{Methods tested in~\cite{Gao:2016uq}} & - \multicolumn{"""+str(nnew_methods)+"""}{c|}{} & - \multicolumn{"""+str(nexp_methods)+"""}{c|}{}\\\\ \hline - """ - rowreplace={dataset: nice.get(dataset, dataset.upper()) for dataset in datasets} - colreplace={method:'\side{' + nice.get(method, method.upper()) +'$^{' + nicerm(eval_name) + '}$} ' for method in methods} - - tabular += table.latexTabular(benchmark_replace=rowreplace, method_replace=colreplace) - tabular += "\n\end{tabularx}" - - save_table(f'./tables/tab_results_{eval_name}.new.tex', tabular) - - # Tables ranks for AE and RAE (two tables) - # ---------------------------------------------------- - # fill the data table - ranktable = Table(benchmarks=datasets, methods=methods, missing='--') - for dataset in datasets: - for method in methods: - ranktable.add(dataset, method, values=table.get(dataset, method, 'rank')) - - # write the latex table - tabular = """ - \\begin{tabularx}{\\textwidth}{|c||""" + ('Y|'*nold_methods) + '|' + ('Y|'*nnew_methods) + '|' + ('Y|'*nexp_methods) + """} \hline - & \multicolumn{"""+str(nold_methods)+"""}{c||}{Methods tested in~\cite{Gao:2016uq}} & - \multicolumn{"""+str(nnew_methods)+"""}{c|}{} & - \multicolumn{"""+str(nexp_methods)+"""}{c|}{}\\\\ \hline - """ - for method in methods: - tabular += ' & \side{' + nice.get(method, method.upper()) +'$^{' + nicerm(eval_name) + '}$} ' - tabular += '\\\\\hline\n' - - for dataset in datasets: - tabular += nice.get(dataset, dataset.upper()) + ' ' - for method in methods: - newrank = ranktable.get(dataset, method) - if newrank != '--': - newrank = f'{int(newrank)}' - color = ranktable.get_color(dataset, method) - if color == '--': - color = '' - tabular += ' & ' + f'{newrank}' + color - tabular += '\\\\\hline\n' - tabular += '\hline\n' - - tabular += 'Average ' - for method in methods: - newrank = ranktable.get_average(method) - if newrank != '--': - newrank = f'{newrank:.1f}' - color = ranktable.get_average(method, 'color') - if color == '--': - color = '' - tabular += ' & ' + f'{newrank}' + color - tabular += '\\\\\hline\n' - tabular += "\end{tabularx}" - - save_table(f'./tables/tab_rank_{eval_name}.new.tex', tabular) - - print("[Done]") diff --git a/NewMethods/settings.py b/NewMethods/settings.py deleted file mode 100644 index 2ade31a..0000000 --- a/NewMethods/settings.py +++ /dev/null @@ -1,4 +0,0 @@ -import multiprocessing - -N_JOBS = -2 #multiprocessing.cpu_count() -SAMPLE_SIZE = 100 \ No newline at end of file diff --git a/TweetSentQuant/Gao_Sebastiani_results.txt b/TweetSentQuant/Gao_Sebastiani_results.txt deleted file mode 100644 index de0e6dd..0000000 --- a/TweetSentQuant/Gao_Sebastiani_results.txt +++ /dev/null @@ -1,89 +0,0 @@ - AE RAE -SemEval13 SVM-KLD 0.0722 0.1720 - SVM-NKLD 0.0714 0.2756 - SVM-QBETA2 0.0782 0.2775 - LR-CC 0.0996 0.3095 - LR-EM 0.1191 0.3923 - LR-PCC 0.0344 0.1506 - LR-ACC 0.0806 0.2479 - LR-PACC 0.0812 0.2626 -SemEval14 SVM-KLD 0.0843 0.2268 - SVM-NKLD 0.0836 0.3367 - SVM-QBETA2 0.1018 0.3680 - LR-CC 0.1043 0.3212 - LR-EM 0.0807 0.3517 - LR-PCC 0.1001 0.4277 - LR-ACC 0.0581 0.2360 - LR-PACC 0.0533 0.2573 -SemEval15 SVM-KLD 0.1185 0.3789 - SVM-NKLD 0.1155 0.4720 - SVM-QBETA2 0.1263 0.4762 - LR-CC 0.1101 0.2879 - LR-EM 0.1204 0.2949 - LR-PCC 0.0460 0.1973 - LR-ACC 0.1064 0.2971 - LR-PACC 0.1013 0.2729 -SemEval16 SVM-KLD 0.0385 0.1512 - SVM-NKLD 0.0830 0.3249 - SVM-QBETA2 0.1201 0.5156 - LR-CC 0.0500 0.1771 - LR-EM 0.0646 0.2126 - LR-PCC 0.0379 0.1553 - LR-ACC 0.0542 0.2246 - LR-PACC 0.0864 0.3504 -Sanders SVM-KLD 0.0134 0.0630 - SVM-NKLD 0.0950 0.3965 - SVM-QBETA2 0.1098 0.4360 - LR-CC 0.0671 0.2682 - LR-EM 0.0715 0.2849 - LR-PCC 0.0150 0.0602 - LR-ACC 0.0338 0.1306 - LR-PACC 0.0301 0.1173 -SST SVM-KLD 0.0413 0.1458 - SVM-NKLD 0.0749 0.2497 - SVM-QBETA2 0.0671 0.2343 - LR-CC 0.0330 0.1239 - LR-EM 0.0369 0.1190 - LR-PCC 0.0282 0.1068 - LR-ACC 0.0492 0.1689 - LR-PACC 0.0841 0.2302 -OMD SVM-KLD 0.0305 0.0999 - SVM-NKLD 0.0437 0.1279 - SVM-QBETA2 0.0624 0.1826 - LR-CC 0.0524 0.1527 - LR-EM 0.0648 0.1886 - LR-PCC 0.0046 0.0095 - LR-ACC 0.0239 0.0753 - LR-PACC 0.0100 0.0293 -HCR SVM-KLD 0.0414 0.2191 - SVM-NKLD 0.0604 0.2324 - SVM-QBETA2 0.1272 0.4600 - LR-CC 0.0525 0.1817 - LR-EM 0.0895 0.3093 - LR-PCC 0.0055 0.0202 - LR-ACC 0.0240 0.1026 - LR-PACC 0.0329 0.1436 -GASP SVM-KLD 0.0171 0.0529 - SVM-NKLD 0.0503 0.3416 - SVM-QBETA2 0.0640 0.4402 - LR-CC 0.0189 0.1297 - LR-EM 0.0231 0.1589 - LR-PCC 0.0097 0.0682 - LR-ACC 0.0150 0.1038 - LR-PACC 0.0087 0.0597 -WA SVM-KLD 0.0647 0.1957 - SVM-NKLD 0.0393 0.1357 - SVM-QBETA2 0.0798 0.2332 - LR-CC 0.0434 0.1270 - LR-EM 0.0391 0.1145 - LR-PCC 0.0338 0.0990 - LR-ACC 0.0407 0.1197 - LR-PACC 0.0277 0.0815 -WB SVM-KLD 0.0613 0.1791 - SVM-NKLD 0.0534 0.1756 - SVM-QBETA2 0.0249 0.0774 - LR-CC 0.0132 0.0399 - LR-EM 0.0244 0.0773 - LR-PCC 0.0123 0.0390 - LR-ACC 0.0230 0.0719 - LR-PACC 0.0165 0.0515 \ No newline at end of file diff --git a/TweetSentQuant/evaluate_results.py b/TweetSentQuant/evaluate_results.py deleted file mode 100644 index 2b8a4d0..0000000 --- a/TweetSentQuant/evaluate_results.py +++ /dev/null @@ -1,35 +0,0 @@ -import numpy as np -import quapy as qp -import settings -import os -import pickle -from glob import glob -import itertools -import pathlib - -qp.environ['SAMPLE_SIZE'] = settings.SAMPLE_SIZE - -resultdir = './results' -methods = ['*'] - - -def evaluate_results(methods, datasets, error_name): - results_str = [] - all = [] - error = qp.error.from_name(error_name) - for method, dataset in itertools.product(methods, datasets): - for experiment in glob(f'{resultdir}/{dataset}-{method}-{error_name}.pkl'): - true_prevalences, estim_prevalences, tr_prev, te_prev, te_prev_estim, best_params = \ - pickle.load(open(experiment, 'rb')) - result = error(true_prevalences, estim_prevalences) - string = f'{pathlib.Path(experiment).name}: {result:.3f}' - results_str.append(string) - all.append(result) - results_str = sorted(results_str) - for r in results_str: - print(r) - print() - print(f'Ave: {np.mean(all):.3f}') - - -evaluate_results(methods=['epacc*mae1k'], datasets=['*'], error_name='mae') diff --git a/TweetSentQuant/experiments.py b/TweetSentQuant/experiments.py deleted file mode 100644 index 3f3c2d7..0000000 --- a/TweetSentQuant/experiments.py +++ /dev/null @@ -1,214 +0,0 @@ -from sklearn.linear_model import LogisticRegression -import quapy as qp -from classification.methods import PCALR -from method.meta import QuaNet -from method.non_aggregative import MaximumLikelihoodPrevalenceEstimation -from quapy.method.aggregative import CC, ACC, PCC, PACC, EMQ, OneVsAll, SVMQ, SVMKLD, SVMNKLD, SVMAE, SVMRAE, HDy -from quapy.method.meta import EPACC, EEMQ -import quapy.functional as F -import numpy as np -import os -import pickle -import itertools -from joblib import Parallel, delayed -import settings -import argparse -import torch -import shutil - - -qp.environ['SAMPLE_SIZE'] = settings.SAMPLE_SIZE - -def newLR(): - return LogisticRegression(max_iter=1000, solver='lbfgs', n_jobs=-1) - -__C_range = np.logspace(-4, 5, 10) -lr_params = {'C': __C_range, 'class_weight': [None, 'balanced']} -svmperf_params = {'C': __C_range} - -def quantification_models(): - # methods tested in Gao & Sebastiani 2016 - yield 'cc', CC(newLR()), lr_params - yield 'acc', ACC(newLR()), lr_params - yield 'pcc', PCC(newLR()), lr_params - yield 'pacc', PACC(newLR()), lr_params - yield 'sld', EMQ(newLR()), lr_params - yield 'svmq', OneVsAll(SVMQ(args.svmperfpath)), svmperf_params - yield 'svmkld', OneVsAll(SVMKLD(args.svmperfpath)), svmperf_params - yield 'svmnkld', OneVsAll(SVMNKLD(args.svmperfpath)), svmperf_params - - # methods added - yield 'svmmae', OneVsAll(SVMAE(args.svmperfpath)), svmperf_params - yield 'svmmrae', OneVsAll(SVMRAE(args.svmperfpath)), svmperf_params - yield 'hdy', OneVsAll(HDy(newLR())), lr_params - - -def quantification_cuda_models(): - device = 'cuda' if torch.cuda.is_available() else 'cpu' - print(f'Running QuaNet in {device}') - learner = PCALR(**newLR().get_params()) - yield 'quanet', QuaNet(learner, settings.SAMPLE_SIZE, checkpointdir=args.checkpointdir, device=device), lr_params - - -def quantification_ensembles(): - param_mod_sel = { - 'sample_size': settings.SAMPLE_SIZE, - 'n_prevpoints': 21, - 'n_repetitions': 5, - 'verbose': False - } - common={ - 'max_sample_size': 1000, - 'n_jobs': settings.ENSEMBLE_N_JOBS, - 'param_grid': lr_params, - 'param_mod_sel': param_mod_sel, - 'val_split': 0.4, - 'min_pos': 10 - } - - # hyperparameters will be evaluated within each quantifier of the ensemble, and so the typical model selection - # will be skipped (by setting hyperparameters to None) - hyper_none = None - #yield 'epaccmaeptr', EPACC(newLR(), optim='mae', policy='ptr', **common), hyper_none - yield 'epaccmaemae1k', EPACC(newLR(), optim='mae', policy='mae', **common), hyper_none - # yield 'esldmaeptr', EEMQ(newLR(), optim='mae', policy='ptr', **common), hyper_none - # yield 'esldmaemae', EEMQ(newLR(), optim='mae', policy='mae', **common), hyper_none - - #yield 'epaccmraeptr', EPACC(newLR(), optim='mrae', policy='ptr', **common), hyper_none - #yield 'epaccmraemrae', EPACC(newLR(), optim='mrae', policy='mrae', **common), hyper_none - #yield 'esldmraeptr', EEMQ(newLR(), optim='mrae', policy='ptr', **common), hyper_none - #yield 'esldmraemrae', EEMQ(newLR(), optim='mrae', policy='mrae', **common), hyper_none - - -def evaluate_experiment(true_prevalences, estim_prevalences): - print('\nEvaluation Metrics:\n'+'='*22) - for eval_measure in [qp.error.mae, qp.error.mrae]: - err = eval_measure(true_prevalences, estim_prevalences) - print(f'\t{eval_measure.__name__}={err:.4f}') - print() - - -def evaluate_method_point_test(true_prev, estim_prev): - print('\nPoint-Test evaluation:\n' + '=' * 22) - print(f'true-prev={F.strprev(true_prev)}, estim-prev={F.strprev(estim_prev)}') - for eval_measure in [qp.error.mae, qp.error.mrae]: - err = eval_measure(true_prev, estim_prev) - print(f'\t{eval_measure.__name__}={err:.4f}') - - -def result_path(path, dataset_name, model_name, optim_loss): - return os.path.join(path, f'{dataset_name}-{model_name}-{optim_loss}.pkl') - - -def is_already_computed(dataset_name, model_name, optim_loss): - if dataset_name=='semeval': - check_datasets = ['semeval13', 'semeval14', 'semeval15'] - else: - check_datasets = [dataset_name] - return all(os.path.exists(result_path(args.results, name, model_name, optim_loss)) for name in check_datasets) - - -def save_results(dataset_name, model_name, optim_loss, *results): - rpath = result_path(args.results, dataset_name, model_name, optim_loss) - qp.util.create_parent_dir(rpath) - with open(rpath, 'wb') as foo: - pickle.dump(tuple(results), foo, pickle.HIGHEST_PROTOCOL) - - -def run(experiment): - - optim_loss, dataset_name, (model_name, model, hyperparams) = experiment - - if is_already_computed(dataset_name, model_name, optim_loss=optim_loss): - print(f'result for dataset={dataset_name} model={model_name} loss={optim_loss} already computed.') - return - elif (optim_loss == 'mae' and 'mrae' in model_name) or (optim_loss=='mrae' and 'mae' in model_name): - print(f'skipping model={model_name} for optim_loss={optim_loss}') - return - else: - print(f'running dataset={dataset_name} model={model_name} loss={optim_loss}') - - benchmark_devel = qp.datasets.fetch_twitter(dataset_name, for_model_selection=True, min_df=5, pickle=True) - benchmark_devel.stats() - - # model selection (hyperparameter optimization for a quantification-oriented loss) - if hyperparams is not None: - model_selection = qp.model_selection.GridSearchQ( - model, - param_grid=hyperparams, - sample_size=settings.SAMPLE_SIZE, - n_prevpoints=21, - n_repetitions=5, - error=optim_loss, - refit=False, - timeout=60*60, - verbose=True - ) - model_selection.fit(benchmark_devel.training, benchmark_devel.test) - model = model_selection.best_model() - best_params = model_selection.best_params_ - else: - best_params = {} - - # model evaluation - test_names = [dataset_name] if dataset_name != 'semeval' else ['semeval13', 'semeval14', 'semeval15'] - for test_no, test_name in enumerate(test_names): - benchmark_eval = qp.datasets.fetch_twitter(test_name, for_model_selection=False, min_df=5, pickle=True) - if test_no == 0: - print('fitting the selected model') - # fits the model only the first time - model.fit(benchmark_eval.training) - - true_prevalences, estim_prevalences = qp.evaluation.artificial_sampling_prediction( - model, - test=benchmark_eval.test, - sample_size=settings.SAMPLE_SIZE, - n_prevpoints=21, - n_repetitions=25, - n_jobs=-1 if isinstance(model, qp.method.meta.Ensemble) else 1 - ) - test_estim_prevalence = model.quantify(benchmark_eval.test.instances) - test_true_prevalence = benchmark_eval.test.prevalence() - - evaluate_experiment(true_prevalences, estim_prevalences) - evaluate_method_point_test(test_true_prevalence, test_estim_prevalence) - save_results(test_name, model_name, optim_loss, - true_prevalences, estim_prevalences, - benchmark_eval.training.prevalence(), test_true_prevalence, test_estim_prevalence, - best_params) - - #if isinstance(model, QuaNet): - #model.clean_checkpoint_dir() - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Run experiments for Tweeter Sentiment Quantification') - parser.add_argument('results', metavar='RESULT_PATH', type=str, - help='path to the directory where to store the results') - parser.add_argument('--svmperfpath', metavar='SVMPERF_PATH', type=str, default='./svm_perf_quantification', - help='path to the directory with svmperf') - parser.add_argument('--checkpointdir', metavar='PATH', type=str, default='./checkpoint', - help='path to the directory where to dump QuaNet checkpoints') - args = parser.parse_args() - - print(f'Result folder: {args.results}') - np.random.seed(0) - - optim_losses = ['mae', 'mrae'] - datasets = qp.datasets.TWITTER_SENTIMENT_DATASETS_TRAIN - - models = quantification_models() - qp.util.parallel(run, itertools.product(optim_losses, datasets, models), n_jobs=settings.N_JOBS) - - models = quantification_cuda_models() - qp.util.parallel(run, itertools.product(optim_losses, datasets, models), n_jobs=settings.CUDA_N_JOBS) - - models = quantification_ensembles() - qp.util.parallel(run, itertools.product(optim_losses, datasets, models), n_jobs=1) - # Parallel(n_jobs=1)( - # delayed(run)(experiment) for experiment in itertools.product(optim_losses, datasets, models) - # ) - - #shutil.rmtree(args.checkpointdir, ignore_errors=True) - - diff --git a/TweetSentQuant/gen_plots.py b/TweetSentQuant/gen_plots.py deleted file mode 100644 index 4952999..0000000 --- a/TweetSentQuant/gen_plots.py +++ /dev/null @@ -1,95 +0,0 @@ -import quapy as qp -import settings -import os -import pathlib -import pickle -from glob import glob -import sys -from TweetSentQuant.util import nicename -from os.path import join - - -qp.environ['SAMPLE_SIZE'] = settings.SAMPLE_SIZE -plotext='png' - -resultdir = './results' -plotdir = './plots' -os.makedirs(plotdir, exist_ok=True) - -def gather_results(methods, error_name): - method_names, true_prevs, estim_prevs, tr_prevs = [], [], [], [] - for method in methods: - for experiment in glob(f'{resultdir}/*-{method}-m{error_name}.pkl'): - true_prevalences, estim_prevalences, tr_prev, te_prev, te_prev_estim, best_params = pickle.load(open(experiment, 'rb')) - method_names.append(nicename(method)) - true_prevs.append(true_prevalences) - estim_prevs.append(estim_prevalences) - tr_prevs.append(tr_prev) - return method_names, true_prevs, estim_prevs, tr_prevs - - -def plot_error_by_drift(methods, error_name, logscale=False, path=None): - print('plotting error by drift') - if path is not None: - path = join(path, f'error_by_drift_{error_name}.{plotext}') - method_names, true_prevs, estim_prevs, tr_prevs = gather_results(methods, error_name) - qp.plot.error_by_drift( - method_names, - true_prevs, - estim_prevs, - tr_prevs, - n_bins=20, - error_name=error_name, - show_std=False, - logscale=logscale, - title=f'Quantification error as a function of distribution shift', - savepath=path - ) - - -def diagonal_plot(methods, error_name, path=None): - print('plotting diagonal plots') - if path is not None: - path = join(path, f'diag_{error_name}') - method_names, true_prevs, estim_prevs, tr_prevs = gather_results(methods, error_name) - qp.plot.binary_diagonal(method_names, true_prevs, estim_prevs, pos_class=0, title='Negative', legend=False, show_std=False, savepath=f'{path}_neg.{plotext}') - qp.plot.binary_diagonal(method_names, true_prevs, estim_prevs, pos_class=1, title='Neutral', legend=False, show_std=False, savepath=f'{path}_neu.{plotext}') - qp.plot.binary_diagonal(method_names, true_prevs, estim_prevs, pos_class=2, title='Positive', legend=True, show_std=False, savepath=f'{path}_pos.{plotext}') - - -def binary_bias_global(methods, error_name, path=None): - print('plotting bias global') - if path is not None: - path = join(path, f'globalbias_{error_name}') - method_names, true_prevs, estim_prevs, tr_prevs = gather_results(methods, error_name) - qp.plot.binary_bias_global(method_names, true_prevs, estim_prevs, pos_class=0, title='Negative', savepath=f'{path}_neg.{plotext}') - qp.plot.binary_bias_global(method_names, true_prevs, estim_prevs, pos_class=1, title='Neutral', savepath=f'{path}_neu.{plotext}') - qp.plot.binary_bias_global(method_names, true_prevs, estim_prevs, pos_class=2, title='Positive', savepath=f'{path}_pos.{plotext}') - - -def binary_bias_bins(methods, error_name, path=None): - print('plotting bias local') - if path is not None: - path = join(path, f'localbias_{error_name}') - method_names, true_prevs, estim_prevs, tr_prevs = gather_results(methods, error_name) - qp.plot.binary_bias_bins(method_names, true_prevs, estim_prevs, pos_class=0, title='Negative', legend=False, savepath=f'{path}_neg.{plotext}') - qp.plot.binary_bias_bins(method_names, true_prevs, estim_prevs, pos_class=1, title='Neutral', legend=False, savepath=f'{path}_neu.{plotext}') - qp.plot.binary_bias_bins(method_names, true_prevs, estim_prevs, pos_class=2, title='Positive', legend=True, savepath=f'{path}_pos.{plotext}') - - -gao_seb_methods = ['cc', 'acc', 'pcc', 'pacc', 'sld', 'svmq', 'svmkld', 'svmnkld'] -new_methods_ae = ['svmmae' , 'epaccmaeptr', 'epaccmaemae', 'hdy', 'quanet'] -new_methods_rae = ['svmmrae' , 'epaccmraeptr', 'epaccmraemrae', 'hdy', 'quanet'] - -plot_error_by_drift(gao_seb_methods+new_methods_ae, error_name='ae', path=plotdir) -plot_error_by_drift(gao_seb_methods+new_methods_rae, error_name='rae', logscale=True, path=plotdir) - -diagonal_plot(gao_seb_methods+new_methods_ae, error_name='ae', path=plotdir) -diagonal_plot(gao_seb_methods+new_methods_rae, error_name='rae', path=plotdir) - -binary_bias_global(gao_seb_methods+new_methods_ae, error_name='ae', path=plotdir) -binary_bias_global(gao_seb_methods+new_methods_rae, error_name='rae', path=plotdir) - -#binary_bias_bins(gao_seb_methods+new_methods_ae, error_name='ae', path=plotdir) -#binary_bias_bins(gao_seb_methods+new_methods_rae, error_name='rae', path=plotdir) - diff --git a/TweetSentQuant/gen_tables.py b/TweetSentQuant/gen_tables.py deleted file mode 100644 index 585c453..0000000 --- a/TweetSentQuant/gen_tables.py +++ /dev/null @@ -1,145 +0,0 @@ -import quapy as qp -import numpy as np -from os import makedirs -import sys, os -import pickle -import argparse -from TweetSentQuant.util import nicename, get_ranks_from_Gao_Sebastiani -import settings -from experiments import result_path -from tabular import Table - -tables_path = './tables' -MAXTONE = 50 # sets the intensity of the maximum color reached by the worst (red) and best (green) results - -makedirs(tables_path, exist_ok=True) - -qp.environ['SAMPLE_SIZE'] = settings.SAMPLE_SIZE - - -def save_table(path, table): - print(f'saving results in {path}') - with open(path, 'wt') as foo: - foo.write(table) - - -def experiment_errors(path, dataset, method, loss): - path = result_path(path, dataset, method, 'm'+loss if not loss.startswith('m') else loss) - if os.path.exists(path): - true_prevs, estim_prevs, _, _, _, _ = pickle.load(open(path, 'rb')) - err_fn = getattr(qp.error, loss) - errors = err_fn(true_prevs, estim_prevs) - return errors - return None - - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Generate tables for Tweeter Sentiment Quantification') - parser.add_argument('results', metavar='RESULT_PATH', type=str, - help='path to the directory where to store the results') - args = parser.parse_args() - - datasets = qp.datasets.TWITTER_SENTIMENT_DATASETS_TEST - evaluation_measures = [qp.error.ae, qp.error.rae] - gao_seb_methods = ['cc', 'acc', 'pcc', 'pacc', 'sld', 'svmq', 'svmkld', 'svmnkld'] - new_methods = ['hdy', 'quanet'] - - gao_seb_ranks, gao_seb_results = get_ranks_from_Gao_Sebastiani() - - for i, eval_func in enumerate(evaluation_measures): - - # Tables evaluation scores for AE and RAE (two tables) - # ---------------------------------------------------- - - eval_name = eval_func.__name__ - added_methods = ['svmm' + eval_name, f'epaccm{eval_name}ptr', f'epaccm{eval_name}m{eval_name}'] + new_methods - methods = gao_seb_methods + added_methods - nold_methods = len(gao_seb_methods) - nnew_methods = len(added_methods) - - # fill data table - table = Table(benchmarks=datasets, methods=methods) - for dataset in datasets: - for method in methods: - table.add(dataset, method, experiment_errors(args.results, dataset, method, eval_name)) - - # write the latex table - # tabular = """ - # \\begin{tabularx}{\\textwidth}{|c||""" + ('Y|'*nold_methods)+ '|' + ('Y|'*nnew_methods) + """} \hline - # & \multicolumn{"""+str(nold_methods)+"""}{c||}{Methods tested in~\cite{Gao:2016uq}} & - # \multicolumn{"""+str(nnew_methods)+"""}{c|}{} \\\\ \hline - # """ - tabular = """ - \\resizebox{\\textwidth}{!}{% - \\begin{tabular}{|c||""" + ('c|' * nold_methods) + '|' + ('c|' * nnew_methods) + """} \hline - & \multicolumn{""" + str(nold_methods) + """}{c||}{Methods tested in~\cite{Gao:2016uq}} & - \multicolumn{""" + str(nnew_methods) + """}{c|}{} \\\\ \hline - """ - rowreplace={dataset: nicename(dataset) for dataset in datasets} - colreplace={method: nicename(method, eval_name, side=True) for method in methods} - - tabular += table.latexTabular(benchmark_replace=rowreplace, method_replace=colreplace) - tabular += """ - \end{tabular}% - } - """ - - save_table(f'./tables/tab_results_{eval_name}.new.tex', tabular) - - # Tables ranks for AE and RAE (two tables) - # ---------------------------------------------------- - methods = gao_seb_methods - - table.dropMethods(added_methods) - - # fill the data table - ranktable = Table(benchmarks=datasets, methods=methods, missing='--') - for dataset in datasets: - for method in methods: - ranktable.add(dataset, method, values=table.get(dataset, method, 'rank')) - - # write the latex table - tabular = """ - \\resizebox{\\textwidth}{!}{% - \\begin{tabular}{|c||""" + ('c|' * len(gao_seb_methods)) + """} \hline - & \multicolumn{""" + str(nold_methods) + """}{c|}{Methods tested in~\cite{Gao:2016uq}} \\\\ \hline - """ - for method in methods: - tabular += ' & ' + nicename(method, eval_name, side=True) - tabular += "\\\\\hline\n" - - for dataset in datasets: - tabular += nicename(dataset) + ' ' - for method in methods: - newrank = ranktable.get(dataset, method) - oldrank = gao_seb_ranks[f'{dataset}-{method}-{eval_name}'] - if newrank != '--': - newrank = f'{int(newrank)}' - color = ranktable.get_color(dataset, method) - if color == '--': - color = '' - tabular += ' & ' + f'{newrank}' + f' ({oldrank}) ' + color - tabular += '\\\\\hline\n' - tabular += '\hline\n' - - tabular += 'Average ' - for method in methods: - newrank = ranktable.get_average(method) - oldrank = gao_seb_ranks[f'Average-{method}-{eval_name}'] - if newrank != '--': - newrank = f'{newrank:.1f}' - oldrank = f'{oldrank:.1f}' - color = ranktable.get_average(method, 'color') - if color == '--': - color = '' - tabular += ' & ' + f'{newrank}' + f' ({oldrank}) ' + color - tabular += '\\\\\hline\n' - tabular += """ - \end{tabular}% - } - """ - - save_table(f'./tables/tab_rank_{eval_name}.new.tex', tabular) - - print("[Done]") diff --git a/TweetSentQuant/settings.py b/TweetSentQuant/settings.py deleted file mode 100644 index 8064fa8..0000000 --- a/TweetSentQuant/settings.py +++ /dev/null @@ -1,8 +0,0 @@ -import multiprocessing - -N_JOBS = -2 #multiprocessing.cpu_count() -CUDA_N_JOBS = 2 -ENSEMBLE_N_JOBS = -2 - -SAMPLE_SIZE = 100 - diff --git a/TweetSentQuant/tabular.py b/TweetSentQuant/tabular.py deleted file mode 100644 index cb90f3f..0000000 --- a/TweetSentQuant/tabular.py +++ /dev/null @@ -1,318 +0,0 @@ -import numpy as np -import itertools -from scipy.stats import ttest_ind_from_stats, wilcoxon - - -class Table: - VALID_TESTS = [None, "wilcoxon", "ttest"] - - def __init__(self, benchmarks, methods, lower_is_better=True, ttest='ttest', prec_mean=3, - clean_zero=False, show_std=False, prec_std=3, average=True, missing=None, missing_str='--', color=True): - assert ttest in self.VALID_TESTS, f'unknown test, valid are {self.VALID_TESTS}' - - self.benchmarks = np.asarray(benchmarks) - self.benchmark_index = {row:i for i, row in enumerate(benchmarks)} - - self.methods = np.asarray(methods) - self.method_index = {col:j for j, col in enumerate(methods)} - - self.map = {} - # keyed (#rows,#cols)-ndarrays holding computations from self.map['values'] - self._addmap('values', dtype=object) - self.lower_is_better = lower_is_better - self.ttest = ttest - self.prec_mean = prec_mean - self.clean_zero = clean_zero - self.show_std = show_std - self.prec_std = prec_std - self.add_average = average - self.missing = missing - self.missing_str = missing_str - self.color = color - - self.touch() - - @property - def nbenchmarks(self): - return len(self.benchmarks) - - @property - def nmethods(self): - return len(self.methods) - - def touch(self): - self._modif = True - - def update(self): - if self._modif: - self.compute() - - def _getfilled(self): - return np.argwhere(self.map['fill']) - - @property - def values(self): - return self.map['values'] - - def _indexes(self): - return itertools.product(range(self.nbenchmarks), range(self.nmethods)) - - def _addmap(self, map, dtype, func=None): - self.map[map] = np.empty((self.nbenchmarks, self.nmethods), dtype=dtype) - if func is None: - return - m = self.map[map] - f = func - indexes = self._indexes() if map == 'fill' else self._getfilled() - for i, j in indexes: - m[i, j] = f(self.values[i, j]) - - def _addrank(self): - for i in range(self.nbenchmarks): - filled_cols_idx = np.argwhere(self.map['fill'][i]).flatten() - col_means = [self.map['mean'][i,j] for j in filled_cols_idx] - ranked_cols_idx = filled_cols_idx[np.argsort(col_means)] - if not self.lower_is_better: - ranked_cols_idx = ranked_cols_idx[::-1] - self.map['rank'][i, ranked_cols_idx] = np.arange(1, len(filled_cols_idx)+1) - - def _addcolor(self): - for i in range(self.nbenchmarks): - filled_cols_idx = np.argwhere(self.map['fill'][i]).flatten() - if filled_cols_idx.size==0: - continue - col_means = [self.map['mean'][i,j] for j in filled_cols_idx] - minval = min(col_means) - maxval = max(col_means) - for col_idx in filled_cols_idx: - val = self.map['mean'][i,col_idx] - norm = (maxval - minval) - if norm > 0: - normval = (val - minval) / norm - else: - normval = 0.5 - if self.lower_is_better: - normval = 1 - normval - self.map['color'][i, col_idx] = color_red2green_01(normval) - - def _run_ttest(self, row, col1, col2): - mean1 = self.map['mean'][row, col1] - std1 = self.map['std'][row, col1] - nobs1 = self.map['nobs'][row, col1] - mean2 = self.map['mean'][row, col2] - std2 = self.map['std'][row, col2] - nobs2 = self.map['nobs'][row, col2] - _, p_val = ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2) - return p_val - - def _run_wilcoxon(self, row, col1, col2): - values1 = self.map['values'][row, col1] - values2 = self.map['values'][row, col2] - _, p_val = wilcoxon(values1, values2) - return p_val - - def _add_statistical_test(self): - if self.ttest is None: - return - self.some_similar = [False]*self.nmethods - for i in range(self.nbenchmarks): - filled_cols_idx = np.argwhere(self.map['fill'][i]).flatten() - if len(filled_cols_idx) <= 1: - continue - col_means = [self.map['mean'][i,j] for j in filled_cols_idx] - best_pos = filled_cols_idx[np.argmin(col_means)] - - for j in filled_cols_idx: - if j==best_pos: - continue - if self.ttest == 'ttest': - p_val = self._run_ttest(i, best_pos, j) - else: - p_val = self._run_wilcoxon(i, best_pos, j) - - pval_outcome = pval_interpretation(p_val) - self.map['ttest'][i, j] = pval_outcome - if pval_outcome != 'Diff': - self.some_similar[j] = True - - def compute(self): - self._addmap('fill', dtype=bool, func=lambda x: x is not None) - self._addmap('mean', dtype=float, func=np.mean) - self._addmap('std', dtype=float, func=np.std) - self._addmap('nobs', dtype=float, func=len) - self._addmap('rank', dtype=int, func=None) - self._addmap('color', dtype=object, func=None) - self._addmap('ttest', dtype=object, func=None) - self._addmap('latex', dtype=object, func=None) - self._addrank() - self._addcolor() - self._add_statistical_test() - if self.add_average: - self._addave() - self._modif = False - - def _is_column_full(self, col): - return all(self.map['fill'][:, self.method_index[col]]) - - def _addave(self): - ave = Table(['ave'], self.methods, lower_is_better=self.lower_is_better, ttest=self.ttest, average=False, - missing=self.missing, missing_str=self.missing_str) - for col in self.methods: - values = None - if self._is_column_full(col): - if self.ttest == 'ttest': - values = np.asarray(self.map['mean'][:, self.method_index[col]]) - else: # wilcoxon - values = np.concatenate(self.values[:, self.method_index[col]]) - ave.add('ave', col, values) - self.average = ave - - def add(self, benchmark, method, values): - if values is not None: - values = np.asarray(values) - if values.ndim==0: - values = values.flatten() - rid, cid = self._coordinates(benchmark, method) - self.map['values'][rid, cid] = values - self.touch() - - def get(self, benchmark, method, attr='mean'): - self.update() - assert attr in self.map, f'unknwon attribute {attr}' - rid, cid = self._coordinates(benchmark, method) - if self.map['fill'][rid, cid]: - v = self.map[attr][rid, cid] - if v is None or (isinstance(v,float) and np.isnan(v)): - return self.missing - return v - else: - return self.missing - - def _coordinates(self, benchmark, method): - assert benchmark in self.benchmark_index, f'benchmark {benchmark} out of range' - assert method in self.method_index, f'method {method} out of range' - rid = self.benchmark_index[benchmark] - cid = self.method_index[method] - return rid, cid - - def get_average(self, method, attr='mean'): - self.update() - if self.add_average: - return self.average.get('ave', method, attr=attr) - return None - - def get_color(self, benchmark, method): - color = self.get(benchmark, method, attr='color') - if color is None: - return '' - return color - - def latex(self, benchmark, method): - self.update() - i,j = self._coordinates(benchmark, method) - if self.map['fill'][i,j] == False: - return self.missing_str - - mean = self.map['mean'][i,j] - l = f" {mean:.{self.prec_mean}f}" - if self.clean_zero: - l = l.replace(' 0.', '.') - - isbest = self.map['rank'][i,j] == 1 - if isbest: - l = "\\textbf{"+l.strip()+"}" - - stat = '' - if self.ttest is not None and self.some_similar[j]: - test_label = self.map['ttest'][i,j] - if test_label == 'Sim': - stat = '^{\dag\phantom{\dag}}' - elif test_label == 'Same': - stat = '^{\ddag}' - elif isbest or test_label == 'Diff': - stat = '^{\phantom{\ddag}}' - - std = '' - if self.show_std: - std = self.map['std'][i,j] - std = f" {std:.{self.prec_std}f}" - if self.clean_zero: - std = std.replace(' 0.', '.') - std = f" \pm {std:{self.prec_std}}" - - if stat!='' or std!='': - l = f'{l}${stat}{std}$' - - if self.color: - l += ' ' + self.map['color'][i,j] - - return l - - def latexTabular(self, benchmark_replace={}, method_replace={}, average=True): - tab = ' & ' - tab += ' & '.join([method_replace.get(col, col) for col in self.methods]) - tab += ' \\\\\hline\n' - for row in self.benchmarks: - rowname = benchmark_replace.get(row, row) - tab += rowname + ' & ' - tab += self.latexRow(row) - - if average: - tab += '\hline\n' - tab += 'Average & ' - tab += self.latexAverage() - return tab - - def latexRow(self, benchmark, endl='\\\\\hline\n'): - s = [self.latex(benchmark, col) for col in self.methods] - s = ' & '.join(s) - s += ' ' + endl - return s - - def latexAverage(self, endl='\\\\\hline\n'): - if self.add_average: - return self.average.latexRow('ave', endl=endl) - - def getRankTable(self): - t = Table(benchmarks=self.benchmarks, methods=self.methods, prec_mean=0, average=True) - for rid, cid in self._getfilled(): - row = self.benchmarks[rid] - col = self.methods[cid] - t.add(row, col, self.get(row, col, 'rank')) - t.compute() - return t - - def dropMethods(self, methods): - drop_index = [self.method_index[m] for m in methods] - new_methods = np.delete(self.methods, drop_index) - new_index = {col:j for j, col in enumerate(new_methods)} - - self.map['values'] = self.values[:,np.asarray([self.method_index[m] for m in new_methods], dtype=int)] - self.methods = new_methods - self.method_index = new_index - self.touch() - - -def pval_interpretation(p_val): - if 0.005 >= p_val: - return 'Diff' - elif 0.05 >= p_val > 0.005: - return 'Sim' - elif p_val > 0.05: - return 'Same' - - -def color_red2green_01(val, maxtone=50): - if np.isnan(val): return None - assert 0 <= val <= 1, f'val {val} out of range [0,1]' - - # rescale to [-1,1] - val = val * 2 - 1 - if val < 0: - color = 'red' - tone = maxtone * (-val) - else: - color = 'green' - tone = maxtone * val - return '\cellcolor{' + color + f'!{int(tone)}' + '}' - diff --git a/TweetSentQuant/util.py b/TweetSentQuant/util.py deleted file mode 100644 index fef866e..0000000 --- a/TweetSentQuant/util.py +++ /dev/null @@ -1,89 +0,0 @@ -import numpy as np - - -nice = { - 'mae':'AE', - 'mrae':'RAE', - 'ae':'AE', - 'rae':'RAE', - 'svmkld': 'SVM(KLD)', - 'svmnkld': 'SVM(NKLD)', - 'svmq': 'SVM(Q)', - 'svmae': 'SVM(AE)', - 'svmnae': 'SVM(NAE)', - 'svmmae': 'SVM(AE)', - 'svmmrae': 'SVM(RAE)', - 'quanet': 'QuaNet', - 'hdy': 'HDy', - 'dys': 'DyS', - 'epaccmaeptr': 'E(PACC)$_\mathrm{Ptr}$', - 'epaccmaemae': 'E(PACC)$_\mathrm{AE}$', - 'epaccmraeptr': 'E(PACC)$_\mathrm{Ptr}$', - 'epaccmraemrae': 'E(PACC)$_\mathrm{RAE}$', - 'svmperf':'', - 'sanders': 'Sanders', - 'semeval13': 'SemEval13', - 'semeval14': 'SemEval14', - 'semeval15': 'SemEval15', - 'semeval16': 'SemEval16', - 'Average': 'Average' -} - - -def nicerm(key): - return '\mathrm{'+nice[key]+'}' - - -def nicename(method, eval_name=None, side=False): - m = nice.get(method, method.upper()) - if eval_name is not None: - o = '$^{' + nicerm(eval_name) + '}$' - m = (m+o).replace('$$','') - if side: - m = '\side{'+m+'}' - return m - - -def load_Gao_Sebastiani_previous_results(): - def rename(method): - old2new = { - 'kld': 'svmkld', - 'nkld': 'svmnkld', - 'qbeta2': 'svmq', - 'em': 'sld' - } - return old2new.get(method, method) - - gao_seb_results = {} - with open('./Gao_Sebastiani_results.txt', 'rt') as fin: - lines = fin.readlines() - for line in lines[1:]: - line = line.strip() - parts = line.lower().split() - if len(parts) == 4: - dataset, method, ae, rae = parts - else: - method, ae, rae = parts - learner, method = method.split('-') - method = rename(method) - gao_seb_results[f'{dataset}-{method}-ae'] = float(ae) - gao_seb_results[f'{dataset}-{method}-rae'] = float(rae) - return gao_seb_results - - -def get_ranks_from_Gao_Sebastiani(): - gao_seb_results = load_Gao_Sebastiani_previous_results() - datasets = set([key.split('-')[0] for key in gao_seb_results.keys()]) - methods = np.sort(np.unique([key.split('-')[1] for key in gao_seb_results.keys()])) - ranks = {} - for metric in ['ae', 'rae']: - for dataset in datasets: - scores = [gao_seb_results[f'{dataset}-{method}-{metric}'] for method in methods] - order = np.argsort(scores) - sorted_methods = methods[order] - for i, method in enumerate(sorted_methods): - ranks[f'{dataset}-{method}-{metric}'] = i+1 - for method in methods: - rankave = np.mean([ranks[f'{dataset}-{method}-{metric}'] for dataset in datasets]) - ranks[f'Average-{method}-{metric}'] = rankave - return ranks, gao_seb_results