Articolo 1
La parte impegnativa qui al punto 1 di Sfida 10 non è il codice ma le complessità intorno alla definizione di quantile. Innanzitutto, manteniamo le cose indipendenti dalla distribuzione e lavoriamo con l'ordine di rango dei valori piuttosto che con i valori direttamente. Cioè, ordiniamo i valori in ordine crescente e li numeriamo in base a dove appaiono in quell'ordine. Dovremo decidere cosa fare con le cravatte. Potremmo continuare a classificarli in sequenza o dare loro lo stesso valore. Lo svantaggio del primo è che possiamo avere lo stesso valore che appare in più di un gruppo. Lo svantaggio di quest'ultimo approccio è che il numero di elementi per gruppo può essere meno equilibrato. Questi due approcci corrispondono a classifica e Pareggio in classifica Funzioni JSL. Non abbiamo parlato di quale valore dare ai pareggi nel secondo approccio. Potremmo dare a tutti loro il successivo valore dell'ordine di graduatoria sequenziale. In alternativa, potremmo fare la media dei valori ordinati in sequenza. In entrambi i casi, probabilmente vorremmo dare al successivo valore sequenziale non legato il grado che avrebbe ricevuto se non ci fossero stati pareggi. Pareggio in classifica fa la media dei ranghi, e per semplicità useremo questo.
La prossima domanda è come dividere i gruppi. Potremmo provare a dividerli il più equamente possibile. Un modo semplice per farlo sarebbe quello di classificare i pareggi in sequenza mettendo almeno Pavimento ( n / G ) elementi in ciascun gruppo (dove n è il numero totale di elementi e G è il numero di gruppi quantili). Dovremo decidere dove mettere il n – G * Pavimento ( n / G ) < G valori aggiuntivi. Il codice seguente si occupa di tutto questo in modo semplice e ordinato:
Massimale(Classifica(inVec)/(N Row(inVec)/quant))
dove inVec è il vettore di input (colonna) e quantita il numero di gruppi quantili.
Il codice sopra può portare alla suddivisione dei valori legati tra i gruppi. Per evitare ciò, possiamo sostituire classifica insieme a Pareggio in classifica . Di seguito è riportato un esempio della funzione completa:
quantileGroup = Function({inVec,quant=10,method=1},{outVec,sumTblRef,ranks},
inVec = Shape(inVec,N Row(invec)*N Col(inVec),1);
If(method ==1,
outVec = Ceiling(Ranking(inVec)/(N Row(inVec)/quant)),
outVec = Ceiling(Ranking Tie(inVec)/(N Row(inVec)/quant))
);
outVec;
);
Articolo 2
Ci sono diversi modi in cui possiamo aggregare i gruppi a basso conteggio: per numero assoluto di osservazioni, numero relativo di osservazioni (ad esempio, gruppi con conteggi inferiori al 10% del gruppo più grande), fissando il numero totale di gruppi, ecc. Ciascuno ha i suoi pregi e difetti. Per fornire una soluzione flessibile, ho scelto di adottare un approccio sia interattivo che visivo, gestito con un grafico a barre regolabile tramite un dispositivo di scorrimento. In questo modo, potevo spostare il cursore avanti e indietro finché non trovavo una distribuzione di gruppi che sembrava buona. La difficoltà era trovare un codice che fosse veloce quando il numero di gruppi era grande. Per semplificare, volevo lavorare con le operazioni e le piattaforme della tabella dati (con Ricalcolo automatico acceso). Di seguito è riportato un esempio di come potrebbe apparire prima e dopo:
Prima

Dopo

In generale, questo codice funziona costruendo una tabella riassuntiva dai gruppi e utilizzando i conteggi dei gruppi (il N righe colonna) come a frequenza variabile. Ciò evita la necessità di aggiornare i valori dalla tabella originale, che potrebbe essere notevolmente più grande. Il codice può essere trovato in Gruppi aggregati.JSL . Un elenco dei valori del gruppo originale dal riepilogo è memorizzato nei gruppi di variabili globali oltre a un ordine di rango vettoriale dei conteggi (classifiche). Spostando il cursore si esegue il aggregatiLowCounts funzioni dove il numero di richiesta di gruppi ( nGruppi ) viene passato in base alla posizione del cursore. Il numero di gruppi può essere facilmente selezionato dal vettore dei ranghi. Ad esempio, se si desiderano 10 gruppi totali, il valore per qualsiasi gruppo originale con un rango 10 o superiore viene impostato sul valore del gruppo aggregato (ad esempio, "Altro") con tutti gli altri gruppi che rimangono invariati. Poiché la tabella è ordinata, è sufficiente modificare i valori dalla riga 10 in poi.
Articolo 3 / Articolo 4
La mia motivazione per questo articolo era come un precursore dell'Articolo 4. Trovare qualcosa in un array associativo (AA) è molto più veloce che usare posizione . Costruire un AA con le 209.405 parole di Lista parole.JMP ho impiegato poco meno di 12 secondi sul mio computer. La tabella seguente mostra il tempo necessario per trovare un elenco di parole casuali dalla tabella utilizzando uno dei due posizione o un AA.
N parole
|
Posizione (sec)
|
Matrice associativa (sec)
|
1
|
0.19
|
0.00045
|
5
|
0.91
|
0.000127
|
10
|
1.78
|
0,000244
|
25
|
4.51
|
0,000592
|
50
|
9.11
|
0.001019
|
70
|
12.47
|
0.001454
|
100
|
17.82
|
0.002155
|
Per gli elenchi più lunghi di 70 parole, è utile creare l'AA. Il codice che ho usato per testare questi due approcci è in Loc vs AA.JSL .
Ci sono vantaggi e svantaggi nell'usare gli AA come strutture di dati ricercabili. Tra i lati positivi, puoi associare qualsiasi valore a una chiave AA, inclusi altri AA. Gli svantaggi sono che le chiavi devono essere univoche (non è un grosso problema nella mia opzione) e, cosa più importante, il processo di ricerca deve essere aumentato se i valori di ricerca non sono nell'elenco delle chiavi. Ciò significa che l'implementazione è relativamente semplice quando tutti i possibili valori di ricerca sono noti a priori, ma un po' più complicata quando non lo sono. Questo non significa che non possiamo usarli; dobbiamo solo essere un po' più furbi. Qualcosa come il codice qui sotto funzionerebbe per il caso citato nel post taggato all'articolo 3 . Un po' più ingombrante e probabilmente più lento in alcuni casi, ma estremamente flessibile. Può essere facilmente adattato a situazioni in cui il dizionario di ricerca cambia semplicemente modificando il contenuto dell'array associativo.
aaMap = Associative Array(
{"1","2","3","4","5","6","7","8","9","0","M","K"},
{"1","2","3","4","5","6","7","8","9","0","e6","e3"}
);
findAndReplace = Function({inList,aaMap},
outNums = Transform Each({nextString},inList,Output("Matrix"),
nextChars = Items(nextString,"");
Num(Concat Items(Transform Each({nextChar},nextChars,aaMap[nextChar]),""));
);
outNums
);
E se volessi affrontare qualcosa di un po' più generale, più grande, come costruire un dizionario di tutte le parole che compaiono nella traduzione inglese di Les Misérables? Partiamo dal presupposto che una parola è composta solo da caratteri alfabetici, comprese eventualmente lettere al di fuori del set di caratteri ASCII standard (ad esempio, quelli con segni diacritici). Tutto il resto è un delimitatore. La sfida qui è che non siamo sicuri di quali caratteri delimitanti potremmo incontrare. Poiché sappiamo quali caratteri compongono le parole, possiamo presumere che se non è nell'elenco, è un delimitatore.
Il pattern matching ha gli strumenti perfetti per trovare le parole che useremo per costruire il dizionario: Pat Span e Pat Break .potremmo usare Parole , ma poi dovremmo controllare ogni elemento per i delimitatori incorporati. L'uso del pattern matching ci consente di fare tutto in un solo passaggio. Il codice è sotto. L'intero processo ha richiesto tra gli 8 ei 9 secondi. Per applicare il codice a un testo diverso, potrebbero essere necessarie alcune regolazioni fini, come l'inclusione di caratteri aggiuntivi e il salvataggio del file in una codifica diversa (ho usato utf-16 per acquisire caratteri ASCII estesi). sono in debito con @Craige_Hales per avermi aiutato con questo dopo che ho faticato a farlo funzionare. Il file di testo per Les Misérables è stato scaricato dal Progetto Gutenberg . Puoi trovarlo qui .
bigText = Load Text File("Les Misérables.txt");
aaLookup = Associative Array();
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZé´É";
wordLoc = 0;
Pat Match(bigText,
Pat Repeat(
(
Pat Pos() >> nextLoc
+ Pat Span( letters ) >> nextWord
+ Pat Test(
Try(
Insert Into(aaLookup[Lowercase(nextWord)],nextLoc),
aaLookup<<INSERT></INSERT>
Simpatico! Il Provare () costrutto che stai utilizzando per l'array associativo Inserire() potrebbe essere più veloce del testare prima un elemento esistente.
Quando ero abbastanza giovane da entrare in un circolo di lettura, uno dei miei compagni di viaggio ha avuto modo di leggere un passaggio con la parola Yosemite , che non avevo mai incontrato. Ricordo di essere rimasto sorpreso dalla sua pronuncia di quattro sillabe e di aver notato che l'insegnante era piacevolmente sorpreso.
Anni dopo, qualcuno mi disse che potevo dire Lo zoppo è Rob e ottenere il nome del gioco abbastanza corretto da sfuggire all'attenzione.