Motivation für diesen Artikel
Dies ist eine ausführliche Erklärung, wie die Leistung aller drei Benchmark-Variablenauswahlalgorithmen reproduziert/reproduziert werden kann, die für einen bevorstehenden Artikel über ein neues Variablenauswahlverfahren namens schätzungsweise erschöpfende Regression ausgewählt wurden, eine rechnerisch machbare Different zur erschöpfenden Regression, auch bekannt als Greatest Subset Choice oder Regression aller Teilmengen. Ich schreibe diesen Artikel, weil ich mich bei der Aktualisierung des Readme-Bereichs des GitHub-Repositorys, den ich für meine Rolle in diesem äußerst spannenden Forschungsprojekt erstellt habe, dazu entschieden habe, ihn in Phrase zu kopieren und einzufügen, und er struggle bereits 1.400 Wörter und 3 Seiten lang!
Hier ist übrigens ein Hyperlink zu diesem Repo: https://github.com/Spencermstarr/EER-Research-Project/tree/main
Ich verabscheue Credentialismus, aber er ist heutzutage im Guten wie im Schlechten so, additionally los geht’s. Ich habe einen MS in Datenanalysetechnik von der George Mason College; und ich begann meinen Teil als Mitarbeiter an diesem Forschungsprojekt als meine Kursarbeit für ein unabhängiges Forschungswahlfach, das ich letzten Sommer belegt hatte (ich habe letzten Monat meinen Abschluss gemacht), aber am Ende des Kurses nicht fertig struggle, sodass ich einen unvollständigen Kurs belegen durfte im Kurs, solange ich ihn bis Mitte Oktober abgeschlossen habe und ich dazu in der Lage struggle.
Allerdings struggle das erst der Anfang meiner Arbeit, wie ich in den Weihnachtsferien herausfand, als der Hauptautor mich kontaktierte und mir mitteilte, dass er mit der Artwork und Weise, wie er den Code zur Erstellung der 58.000 auszuführenden synthetischen Datensätze erstellt hatte, nicht zufrieden sei der EER-Algorithmus und die drei Benchmarks, auf die wir uns geeinigt haben, nämlich das Lasso und die schrittweise Rückwärts- und Vorwärtsregression. Der Grund, warum er nicht zufrieden struggle, liegt darin, dass er alle synthetischen Datensätze mit Monte-Carlo-Methoden so konstruierte, dass die wahren zugrunde liegenden Strukturfaktoren für jeden von ihnen und in ihren Titeln explizit bekannt waren. Die 58.000 Datensätze (die alle die gleichen Abmessungen haben, 501 x 31, Überschriften und 500 Beobachtungen in einer Spalte für die abhängige Variable/den Regressanden, gefolgt von 30 Spalten, von denen jede 500 Beobachtungen für einen der 30 Kandidaten-Regressoren/unabhängigen Variablen enthält ) wurden alle auf die folgende Weise benannt: n1-n2-n3-n4 wobei n der Grad der Multikollinearität zwischen allen 30 Kandidaten-Regressoren, n2 die Anzahl der Strukturvariablen, n3 der Grad der Fehlervarianz und n4 nur ein ist Index der 500 zufälligen Variationen für jeden festen Satz der vorherigen statistischen Merkmale jedes Datensatzes, den Sie betrachten.
Das Downside bestand jedoch darin, dass in diesem ersten Satz von 58.000 synthetischen Datensätzen, die er erstellt hatte, dieser Satz echter zugrunde liegender Variablen, die jeden Datensatz charakterisierten, immer einfach der erste n1 von ihnen struggle, und der Hauptautor glaubte, was meiner Meinung nach durchaus berechtigt struggle, dass dies der Fall sein könnte bevorzugen von Natur aus eine schrittweise Vorwärtsregression. Daher wäre es besser, die synthetischen Datensätze so zu regenerieren, dass auch die Reihenfolge der Strukturvariablen für jeden Datensatz zufällig bestimmt würde.
Deshalb überarbeitete er den VBA-Code, den er zum Generieren der synthetischen Datensätze verwendete, so, dass alle zuvor beschriebenen Merkmale jedes Datensatzes gleich waren, dieses Mal jedoch zwei zusätzliche Zeilen in jedem Datensatz waren, die sich direkt über den Kopfzeilen befanden. Es wird einfacher, sie zu beschreiben, nachdem Sie unten einen Screenshot von einem von ihnen gezeigt haben:
Jetzt enthält die erste Zeile 30 binäre Indikatoren, die 1 sagen, wenn dieser Regressorkandidat ein Mitglied des wahren zugrunde liegenden Strukturmodells ist, das diesen Datensatz charakterisiert, und 0, wenn dies nicht der Fall ist. Die 2. Reihe ist meiner Meinung nach überflüssig, wenn ich hier ehrlich bin.
Als zunächst unglückliches Ergebnis dieser Überarbeitung und Neugenerierung der Datensätze, auf denen ich die drei Benchmark-Algorithmen ausführen sollte, musste ich meinen Code erheblich umschreiben, sehr erheblich, sage ich, und während meiner Weihnachtsferien nicht weniger, unverschämt!
Mein Mitarbeiter hat keinen weiteren Satz von 58.000 synthetischen Datensätzen erstellt, sondern dieses Mal einen Satz von 260.000 davon, was besser ist, weil es mehr Variationen in den zugrunde liegenden statistischen Bedingungen gibt, anhand derer der von ihm vorgeschlagene Algorithmus getestet wird, aber das struggle der Fall Außerdem ist es doppelt zeitaufwändig, damit zu arbeiten, was einer der Gründe dafür ist, dass ich mir so große Mühe gebe, solche detaillierten Aufzeichnungen über alles zu machen, um den Zuschauern großen Schmerz und eine Tragödie zu ersparen, wenn sie es selbst überprüfen möchten, wenn ich, wie nur Ich bin ein kleiner Junge, der an seiner allerersten wissenschaftlichen Arbeit arbeitet. Ich habe bei meiner Arbeit keine Fehler gemacht.
Ich beginne damit, den Code zu teilen, den ich zum Importieren und Laden der Datensätze in RStudio verwendet habe. Mein gesamter Code ist in R und wurde über RStudio ausgeführt. Dieses R-Skript heißt R-Skript zum Laden und Vorbereiten der Datensätze.R und befindet sich im Ordner „Stage 2 Scripts“ im GitHub Repo. Lasst uns anfangen:
# load all crucial packages
library(plyr)
library(dplyr)
library(stringi)
library(purrr)
library(knowledge.desk)
library(parallel)# Extract the entire particular person spreadsheet containing csv recordsdata
# within the file folder referred to as 'Information' which is crammed random artificial observations
# to run FS, Stepwise, and presumably EER on in order to allow correct comparability of their outcomes.
# There are 260k spreadsheets on this folder although,
# so solely do that when you have a really highly effective laptop at hand, my new 32 GB of
# RAM having laptop computer was nowhere close to up for that activity!
# So, I run this on subsets I copy+pastad from the Information folder into new folders
# of both 5,000, 10,000, or 15,000 at a time and simply named them by the vary of datasets they embody.
folderpath <- "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50"
system.time(paths_list <- checklist.recordsdata(path = folderpath,
full.names = TRUE,
recursive = TRUE))
Ich verwende die Funktion checklist.recordsdata() innerhalb einer Funktion lapply(), um die Dateipfade der einzelnen Datensätze abzurufen, aber es gibt ein Downside. Die Funktion lapply() ist das Arbeitspferd jedes Skripts, das Sie sehen werden! Wenn ich mir die Reihenfolge ansehe, ist es falsch:
> head(paths_list)
[1] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-1.csv"
[2] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-10.csv"
[3] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-11.csv"
[4] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-12.csv"
[5] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-13.csv"
[6] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-14.csv"
Das hat mich einige Zeit verblüfft, die Lösung wurde mir auf Stack Overflow bereitgestellt:
# shorten the names of every of the datasets corresponding to every file path in paths_list
DS_names_list <- basename(paths_list)
DS_names_list <- instruments::file_path_sans_ext(DS_names_list)
> head(DS_names_list)
[1] "0-3-1-1" "0-3-1-10" "0-3-1-11" "0-3-1-12" "0-3-1-13"
[6] "0-3-1-14"# kind each of the checklist of file names in order that they're within the correct order
my_order = DS_names_list |>
# cut up aside the numbers
strsplit(cut up = "-", mounted = TRUE) |> unlist() |>
# convert them to numeric and get them in a knowledge body
as.numeric() |> matrix(nrow = size(DS_names_list), byrow = TRUE) |>
as.knowledge.body() |>
# get the suitable ordering to kind the information body
do.name(order, args = _)
DS_names_list = DS_names_list[my_order]
paths_list = paths_list[my_order]
Wenn ich mich recht erinnere, habe ich in der Frage ausdrücklich darum gebeten, die gekürzte Model der Dateipfade in die Lösung aufzunehmen. Vor dieser Antwort struggle mir der neue verallgemeinerte R-Pipe-Operator |> eigentlich nicht bekannt, daher hat er mir nicht nur bei der Beantwortung meiner engen Frage geholfen!
Folgendes bekomme ich jetzt:
> head(DS_names_list)
[1] "0-3-1-1" "0-3-1-2" "0-3-1-3" "0-3-1-4" "0-3-1-5" "0-3-1-6"> head(paths_list)
[1] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-1.csv"
[2] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-2.csv"
[3] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-3.csv"
[4] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-4.csv"
[5] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-5.csv"
[6] "C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Information Folders/high 50/0-3-1-6.csv"
Downside gelöst! Als Nächstes zum Datensatzimport habe ich in diesem Teil versucht, mit mehreren verschiedenen Optionen zu experimentieren, um die Laufzeit zu verkürzen, einschließlich read_csv() aus dem readr-Paket, aber selbst das Laden von nur 10.000 Datensätzen dauerte sehr lange. Ich habe mich schließlich für die Funktion fread() aus dem Paket knowledge.desk entschieden, die in einem lapply() verschachtelt ist. Es liest alle N Datensätze ein und speichert sie im treffend benannten Datasets-Objekt, das selbst eine Liste mit N Elementen ist, die alle knowledge.tables sind, die schnellere knowledge.frame-äquivalente Different, die vom oben genannten Paket mit demselben Namen bereitgestellt wird.
## Import all 260k of the person csv recordsdata under.
## To be able to accomplish this, I will probably be utilizing the readr library in R.
## This line reads the entire knowledge in every of the csv recordsdata
## utilizing the title of every retailer within the checklist we simply created.
#system.time( datasets <- lapply(paths_list, fread) )
# relying on how lots of the datasets you run this on at a time, it may possibly
# actually take a very long time, I used to be do subsets of 5k & 10k at a time, and to
# pace that up, when I'm not doing anything on my laptop computer that's taxing,
# I parallelize this importation activity.
CL <- makeCluster(detectCores() - 2L)
clusterExport(CL, c('paths_list'))
system.time(datasets <- parLapply(cl = CL, X = paths_list,
enjoyable = knowledge.desk::fread))
Der Grund dafür, dass das Laden so lange dauert, liegt darin, dass keine dieser Lesefunktionen erkennen kann, dass jede Zeile ab 4:503 numerisch ist. Wie Sie unten sehen können, werden sie in einer äußerst unglücklichen Wendung der Ereignisse alle als Zeichenfolgen eingegeben:
> str(datasets[[1]])
Lessons ‘knowledge.desk’ and 'knowledge.body': 503 obs. of 31 variables:
$ V1 : chr "Regressor current" "" "Y" "2.916939916" ...
$ V2 : chr "0" "1" "X1" "1.058704562" ...
$ V3 : chr "0" "2" "X2" "-1.620390448" ...
$ V4 : chr "0" "3" "X3" "-1.187970361" ...
$ V5 : chr "0" "4" "X4" "-0.442929766" ...
$ V6 : chr "0" "5" "X5" "-0.402632433" ...
$ V7 : chr "0" "6" "X6" "-0.027501853" ...
.
.
.> head(datasets[[1]])
V1 V2 V3 V4
1: Regressor current 0 0 0
2: 1 2 3
3: Y X1 X2 X3
4: 2.916939916 1.058704562 -1.620390448 -1.187970361
5: 1.917297535 -0.672083963 -0.918559209 0.980072821
6: 3.036556879 0.726865905 3.882613175 0.813976886
V5 V6 V7 V8
1: 0 0 0 0
2: 4 5 6 7
3: X4 X5 X6 X7
4: -0.442929766 -0.402632433 -0.027501853 1.963675974
5: -1.36125037 -0.342735517 -0.872367647 0.363388278
6: 0.398140844 0.092130625 -0.205455864 0.523450412
V9 V10 V11 V12
1: 1 0 1 0
2: 8 9 10 11
3: X8 X9 X10 X11
4: 1.315668458 -1.050015499 0.798635241 -0.889227465
5: -0.988933033 0.121579447 1.573511099 -1.428803426
6: 0.981070487 -0.916156959 -1.186403884 1.17196279
.
.
.
Und um das Ganze noch schlimmer zu machen: Es gibt auch keine Spaltennamen. Das zu beheben ist jedoch nicht schwierig, alles, was man braucht, ist colnames() in einem weiteren lapply(). Interessanterweise bietet dies irgendwie auch den netten zusätzlichen kleinen Vorteil, dass die Funktion head() sowohl die Standardergebnisse als auch die Standardergebnisse der Funktion tail() auf eine hübsche Artwork und Weise liefert, was ziemlich nett ist:
# change column names of all of the columns within the knowledge.desk 'datasets'
datasets <- lapply(datasets, operate(dataset_i) {
colnames(dataset_i) <- c("Y","X1","X2","X3","X4","X5","X6","X7","X8",
"X9","X10","X11","X12","X13","X14","X15",
"X16","X17","X18","X19","X20","X21","X22",
"X23","X24","X25","X26","X27","X28","X29","X30")
dataset_i })> head(datasets[1])
[[1]]
Y X1 X2 X3
1: Regressor current 0 0 0
2: 1 2 3
3: Y X1 X2 X3
4: 2.916939916 1.058704562 -1.620390448 -1.187970361
5: 1.917297535 -0.672083963 -0.918559209 0.980072821
---
499: 3.538042884 -0.513772293 0.649608496 -1.202785118
500: 1.908729432 0.042693735 -0.627050749 0.99327264
501: -0.051598726 0.089726174 -0.368348071 -1.864227479
502: 2.471159018 0.098448113 0.696949149 -1.680584162
503: -2.03920828 0.055659648 0.290353389 1.844002761
X4 X5 X6 X7
1: 0 0 0 0
2: 4 5 6 7
3: X4 X5 X6 X7
4: -0.442929766 -0.402632433 -0.027501853 1.963675974
5: -1.36125037 -0.342735517 -0.872367647 0.363388278
---
499: 1.383394554 0.481157649 1.761069392 0.158508074
500: -1.089312243 -0.194320772 1.972402627 -0.907776763
501: -0.049805525 1.597388079 -1.067372139 -0.184131012
502: 0.352159541 -0.364212893 -0.608091244 -0.907343628
503: -0.074654003 0.604434241 -1.52730828 0.697443703
Danach folgt der wichtigste Teil der Transformation und Vorverarbeitung der Daten, bevor irgendetwas ausgeführt wird: das Extrahieren der wertvollen Informationen, die in der ersten Zeile jedes Datensatzes bereitgestellt werden, und das anschließende Entfernen dieser Informationen sowie der unnötigen zweiten Zeile. Ich beginne damit, den ersten Schritt in diesem Prozess falsch zu benennen, nämlich die Reihe binärer Indikatoren in einem eigenen Objekt als Structural_IVs zu speichern, weil ich zu weit gegangen bin, aber es ist jetzt in so vielen verschiedenen Skripten, dass ich es nicht ändere. Dann verwende ich Structural_IVs, um mithilfe der Namensfunktion tatsächlich die Sätze von Strukturvariablen in jedem Datensatz zu extrahieren, und mache das Gleiche, allerdings auch für die Umkehrung, die später entscheidend sein wird. Hier ist dieser Code:
Structural_IVs <- lapply(datasets, operate(j) {j[1, -1]})
> head(Structural_IVs, n = 3)
[[1]]
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17
1: 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0
X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30
1: 0 0 0 0 0 1 0 0 0 0 0 0 0[[2]]
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17
1: 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30
1: 1 0 0 0 0 1 0 0 0 0 0 0 0
[[3]]
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17
1: 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0
X18 X19 X20 X21 X22 X23 X24 X25 X26 X27 X28 X29 X30
1: 0 0 0 0 0 0 0 0 0 0 0 0 0
Structural_Variables <- lapply(Structural_IVs, operate(i) {names(i)[i == 1]})
> head(Structural_Variables, n = 3)
[[1]]
[1] "X8" "X10" "X23"
[[2]]
[1] "X12" "X18" "X23"
[[3]]
[1] "X11" "X13" "X16"
Nonstructural_Variables <- lapply(Structural_IVs, operate(i) {
names(i)[i == 0] })
> head(Nonstructural_Variables, n = 3)
[[1]]
[1] "X1" "X2" "X3" "X4" "X5" "X6" "X7" "X9" "X11"
[10] "X12" "X13" "X14" "X15" "X16" "X17" "X18" "X19" "X20"
[19] "X21" "X22" "X24" "X25" "X26" "X27" "X28" "X29" "X30"
[[2]]
[1] "X1" "X2" "X3" "X4" "X5" "X6" "X7" "X8" "X9"
[10] "X10" "X11" "X13" "X14" "X15" "X16" "X17" "X19" "X20"
[19] "X21" "X22" "X24" "X25" "X26" "X27" "X28" "X29" "X30"
[[3]]
[1] "X1" "X2" "X3" "X4" "X5" "X6" "X7" "X8" "X9"
[10] "X10" "X12" "X14" "X15" "X17" "X18" "X19" "X20" "X21"
[19] "X22" "X23" "X24" "X25" "X26" "X27" "X28" "X29" "X30"
All_Variable_Names <- lapply(datasets, operate(ok) {names(ok)})
> head(All_Variable_Names, n = 1)
[[1]]
[1] "Y" "X1" "X2" "X3" "X4" "X5" "X6" "X7" "X8"
[10] "X9" "X10" "X11" "X12" "X13" "X14" "X15" "X16" "X17"
[19] "X18" "X19" "X20" "X21" "X22" "X23" "X24" "X25" "X26"
[28] "X27" "X28" "X29" "X30"
# assign all 30 candidate regressor names to an object
var_names <- c("X1","X2","X3","X4","X5","X6","X7","X8",
"X9","X10","X11","X12","X13","X14","X15",
"X16","X17","X18","X19","X20","X21","X22",
"X23","X24","X25","X26","X27","X28","X29","X30")
Der letzte Teil, var_names, erfüllt tatsächlich die gleiche Aufgabe wie All_Variable_Names für später, muss jedoch nur einmal ausgeführt werden. Der Grund, warum ich vor dem vorherigen Codeabschnitt eine viel längere Erklärung eingefügt habe, liegt darin, dass ich faul geworden bin und mein fleißiges Kommentieren während des Prozesses dort eingestellt habe. Bei den letzten Vorverarbeitungsschritten im letzten Codeabschnitt, den ich in Teil 1 dieser Artikelserie vorstellen werde, tritt dieses Downside jedoch nicht auf:
### truncate & remodel the datasets checklist earlier than operating the regressions
# Step 1: take away the primary 2 non-numerical rows of every dataset.
datasets <- lapply(datasets, operate(i) {i[-1:-3, ]})
# Step 2: they're chr quite than numeric, repair that.
system.time(datasets <- lapply(datasets, (X) { lapply(X, as.numeric) }))
# Step 3: convert all of them to knowledge.tables, a sooner various to knowledge.frames
datasets <- lapply(datasets, operate(i) { as.knowledge.desk(i) })#stopCluster(CL)
#rm(CL)
rm(paths_list, my_order, folderpath)
save.picture("C:/Customers/Spencer/OneDrive/Paperwork/Analytics Initiatives/EER undertaking/Saved WorkSpaces/datasets WorkSpace for 'high 50'.RData")
Ich habe diesen letzten Teil des Skripts hier eingefügt, weil ich über einen relativ schönen HP Quad-Core Intel(R) Core(TM) i5–1155G7 der 11. Era mit 2,50 GHz und 32 GB RAM verfügte, bestehend aus einem passenden Paar 16 GB Group Group Inc 3200 MHz Da in jedem Steckplatz RAM steckt, habe ich die 260.000 Beispieldatensätze jeweils nur in 5.000 oder 10.000 Stück geladen.
Im nächsten Artikel dieser Reihe werde ich den Code durchgehen, den ich zum Ausführen des ersten Bechmarks, des Lassos, verwendet habe. Fortgesetzt werden…