Οποιοδήποτε πείραμα περιλαμβάνει μια αντιστάθμιση μεταξύ γρήγορων αποτελεσμάτων και ευαισθησίας μετρήσεων. Εάν η επιλεγμένη μέτρηση είναι ευρεία ως προς τη διακύμανση, πρέπει να περιμένουμε πολύ για να διασφαλίσουμε ότι τα αποτελέσματα του πειράματος είναι ακριβή. Ας εξετάσουμε μια μέθοδο για να βοηθήσουμε τους αναλυτές να ενισχύσουν τα πειράματά τους χωρίς να χάσουν πολύ χρόνο ή ευαισθησία μετρήσεων.
Ας υποθέσουμε ότι διεξάγουμε ένα τυπικό πείραμα για να δοκιμάσουμε έναν νέο αλγόριθμο κατάταξης, με τη διάρκεια περιόδου σύνδεσης ως κύρια μέτρηση. Επιπλέον, σκεφτείτε ότι το κοινό μας μπορεί να κατηγοριοποιηθεί χονδρικά σε τρεις ομάδες: 1 εκατομμύριο έφηβοι, 2 εκατομμύρια χρήστες ηλικίας 18-45 ετών και 3 εκατομμύρια χρήστες ηλικίας 45 ετών και άνω. Η απόκριση σε έναν νέο αλγόριθμο κατάταξης θα διαφέρει σημαντικά μεταξύ αυτών των ομάδων κοινού. Αυτή η μεγάλη διακύμανση μειώνει την ευαισθησία της μέτρησης.
Με άλλα λόγια, ο πληθυσμός μπορεί να χωριστεί σε τρία στρώματα, τα οποία περιγράφονται παρακάτω:
Ας πούμε ότι κάθε στοιχείο έχει μια κανονική κατανομή. Στη συνέχεια, η κύρια μέτρηση για τον πληθυσμό έχει επίσης κανονική κατανομή.
Χωρίζουμε τυχαία όλους τους χρήστες από τον πληθυσμό σε ένα κλασικό σχέδιο πειράματος χωρίς να λαμβάνουμε υπόψη τις διαφορές μεταξύ των χρηστών μας. Έτσι, θεωρούμε το δείγμα με την ακόλουθη αναμενόμενη τιμή και διακύμανση.
Ένας άλλος τρόπος είναι η τυχαία διαίρεση μέσα σε κάθε στρώμα σύμφωνα με το βάρος του στρώματος στον γενικό πληθυσμό.
Σε αυτήν την περίπτωση, η αναμενόμενη τιμή και η διακύμανση είναι τα ακόλουθα.
Η αναμενόμενη τιμή είναι η ίδια όπως στην πρώτη επιλογή. Ωστόσο, η διακύμανση είναι μικρότερη, γεγονός που εγγυάται υψηλότερη μετρική ευαισθησία.
Τώρα, ας εξετάσουμε τη μέθοδο του Neyman . Προτείνουν τη διαίρεση των χρηστών τυχαία μέσα σε κάθε στρώμα με συγκεκριμένα βάρη.
Άρα, η αναμενόμενη τιμή και η διακύμανση είναι ίσες με τις ακόλουθες σε αυτήν την περίπτωση.
Η αναμενόμενη τιμή είναι ίση με την αναμενόμενη τιμή στην πρώτη περίπτωση ασυμπτωτικά. Ωστόσο, η απόκλιση είναι πολύ μικρότερη.
Έχουμε αποδείξει την αποτελεσματικότητα αυτής της μεθόδου θεωρητικά. Ας προσομοιώσουμε δείγματα και ας δοκιμάσουμε τη μέθοδο στρωματοποίησης εμπειρικά.
Ας εξετάσουμε τρεις περιπτώσεις:
Θα εφαρμόσουμε και τις τρεις μεθόδους σε όλες τις περιπτώσεις και θα σχεδιάσουμε ένα ιστόγραμμα και ένα πλαίσιο για να τις συγκρίνουμε.
Αρχικά, ας δημιουργήσουμε μια τάξη στην Python που προσομοιώνει τον γενικό πληθυσμό μας που αποτελείται από τρία στρώματα.
class GeneralPopulation: def __init__(self, means: [float], stds: [float], sizes: [int], random_state: int = 15 ): """ Initializes our General Population and saves the given distributions :param means: List of expectations for normal distributions :param stds: List of standard deviations for normal distributions :param sizes: How many objects will be in each strata :param random_state: Parameter fixing randomness. Needed so that when conducting experiment repeatedly with the same input parameters, the results remained the same """ self.strats = [st.norm(mean, std) for mean, std in zip(means, stds)] self._sample(sizes) self.random_state = random_state def _sample(self, sizes): """Creates a general population sample as a mixture of strata :param sizes: List with sample sizes of the corresponding normal distributions """ self.strats_samples = [rv.rvs(size) for rv, size in zip(self.strats, sizes)] self.general_samples = np.hstack(self.strats_samples) self.N = self.general_samples.shape[0] # number of strata self.count_strats = len(sizes) # ratios for every strata in GP self.ws = [size/self.N for size in sizes] # ME and Std for GP self.m = np.mean(self.general_samples) self.sigma = np.std(self.general_samples) # ME and std for all strata self.ms = [np.mean(strat_sample) for strat_sample in self.strats_samples] self.sigmas = [np.std(strat_sample) for strat_sample in self.strats_samples]
Στη συνέχεια, ας προσθέσουμε συναρτήσεις για τις τρεις μεθόδους δειγματοληψίας που περιγράφονται στο θεωρητικό μέρος.
def random_subsampling(self, size): """Creates a random subset of the entire population :param sizes: subsample size """ rc = np.random.choice(self.general_samples, size=size) return rc def proportional_subsampling(self, size): """Creates a subsample with the number of elements, proportional shares of strata :param sizes: subsample size """ self.strats_size_proport = [int(np.floor(size*w)) for w in self.ws] rc = [] for k in range(len(self.strats_size_proport)): rc.append(np.random.choice(self.strats_samples[k], size=self.strats_size_proport[k])) return rc def optimal_subsampling(self, size): """Creates a subsample with the optimal number of elements relative to strata :param sizes: subsample size """ sum_denom = 0 for k in range(self.count_strats): sum_denom += self.ws[k] * self.sigmas[k] self.strats_size_optimal = [int(np.floor((size*w*sigma)/sum_denom)) for w, sigma in zip(self.ws, self.sigmas)] if 0 in self.strats_size_optimal: raise ValueError('Strats size is 0, please change variance of smallest strat!') rc = [] for k in range(len(self.strats_size_optimal)): rc.append(np.random.choice(self.strats_samples[k], size=self.strats_size_optimal[k])) return rc
Επίσης, για το εμπειρικό μέρος, χρειαζόμαστε πάντα μια συνάρτηση για την προσομοίωση της πειραματικής διαδικασίας.
def run_experiments(self, n_sub, subsampling_method, n_experiments=1000): """Conducts a series of experiments and saves the results :param n_sub: size of sample :param subsampling_method: method for creating a subsample :param n_experiments: number of experiment starts """ means_s = [] if(len(self.general_samples)<100): n_sub = 20 if(subsampling_method == 'random_subsampling'): for n in range(n_experiments): rc = self.random_subsampling(n_sub) mean = rc.sum()/len(rc) means_s.append(mean) else: for n in range(n_experiments): if(subsampling_method == 'proportional_subsampling'): rc = self.proportional_subsampling(n_sub) elif(subsampling_method == 'optimal_subsampling'): rc = self.optimal_subsampling(n_sub) strats_mean = [] for k in range(len(rc)): strats_mean.append(sum(rc[k])/len(rc[k])) # Mean for a mixture means_s.append(sum([w_k*mean_k for w_k, mean_k in zip(self.ws, strats_mean)])) return means_s
Αν δούμε τον γενικό πληθυσμό, όπου όλα τα στρώματά μας έχουν τις ίδιες τιμές και διακυμάνσεις, τα αποτελέσματα και των τριών μεθόδων αναμένεται να είναι λίγο-πολύ ίσα.
Διαφορετικά μέσα και ίσες διακυμάνσεις έφεραν πιο συναρπαστικά αποτελέσματα. Η χρήση διαστρωμάτωσης μειώνει δραματικά τη διακύμανση.
Σε περιπτώσεις με ίσους μέσους όρους και διαφορετικές διακυμάνσεις, βλέπουμε μείωση διασποράς στη μέθοδο του Neyman.
Τώρα, μπορείτε να εφαρμόσετε τη μέθοδο διαστρωμάτωσης για να μειώσετε τη μετρική διακύμανση και να ενισχύσετε το πείραμα, εάν ομαδοποιήσετε το κοινό σας και το διαιρέσετε τεχνικά τυχαία μέσα σε κάθε σύμπλεγμα με συγκεκριμένα βάρη!