[Baseball Hack] Ich habe versucht, das Python MLB Score & Grade-Datenerfassungsskript mit Go in einem halben Tag zu kopieren

Was ich getan habe

Vor etwas mehr als einem Monat habe ich das MLB-Baseball-Datenskript "[py-retrosheet](https://github.com/Shinichi-Nakagawa/py-retrosheet" py-retrosheet ")" kopiert, das mit Python 3.5 mit Golang kompatibel ist. Es war.

go-retrosheet

Es ist ein Skript auf Retrosheet (http://www.retrosheet.org/), das Übereinstimmungsdaten und Ergebnisse (hauptsächlich At-Bat-Daten) herunterlädt, sie auf eine Ebene analysiert, die für die Aggregation und Analyse verwendet werden kann, und CSV ausspuckt.

Das Original hat eine Funktion zum Durchsuchen der Datenbank (MySQL usw.), aber ich werde dies später vornehmen (Grund: Ngo, der die Schlafzeit nicht verkürzen möchte ... w).

Es spielt keine Rolle, aber der Name "Go! Retrosheet" hat viel Schwung und ist cool (Lesesticks)

Warum hast du dich entschieden, es mit Golang zu schaffen?

Es gibt zwei Gründe.

In Last #gocon heißt es: "Ich habe noch nie Golang gemacht, obwohl ich gehen kann, also habe ich etwas mit Golang gemacht." Mit der Motivation von "Lass uns in LT springen!" [Ich habe in einem halben Tag eine Tour of Go gemacht und die Django-Anwendung mit Martini umgeschrieben und einen wunderbaren Sprung in LT gemacht](http://shinyorke.hatenablog.com/entry/2015/06/21 / 195656 "Golang Ich habe einen halben Tag später eine Baseball-App geschrieben und den Sommerbericht #gocon der LT ~ Go Conference 2015 gestartet.") Aber wenn ich darüber nachdenke, denke ich, dass ich Golang seitdem nicht mehr geschrieben habe (da war PyCon unterwegs). ..), ich werde diesmal schreiben, um an die Teilnahme von #gocon im Winter zu erinnern! Ich habe mit diesem Gefühl angefangen. LT dachte darüber nach, es rechtzeitig zu tun.

Auch als schwerwiegender Grund

Ich weiß nicht, ob Go genug ist, um zu sagen ... (oder besser gesagt, ich bin ein Anfänger im Knirschen), also lasst uns die Güte und Eigenschaften von Go beim Kopieren erneut bestätigen! Die Bedeutung von "Lernen" ist ebenfalls enthalten.

Was ich gemacht habe

Ich habe gofmt und golint bestanden, aber es gibt insgesamt nur wenige Kommentare und es kann angebracht sein ... Experten, ich warte auf Meinungen m (_ _) m

Außerdem habe ich keine Tests geschrieben, daher handelt es sich um einen knusprigen Legacy-Code.

download.go

Dies ist der Code, der das Archiv (Zip-Datei) mit der CSV von Retrosheet im Dateiverzeichnis herunterlädt und dekomprimiert.

Ich habe den Code zum Entpacken der Zip-Datei unter Bezugnahme auf [hier] geschrieben (https://gist.github.com/hnaohiro/4572580 "Beispiel zum Entpacken der Zip-Datei mit Golang") und danke, dass Sie ordnungsgemäß funktioniert haben! Aber ich frage mich, ob ich schlauer schreiben kann? Ich dachte.

Außerdem habe ich zum ersten Mal Parallelverarbeitung in Go geschrieben, aber ich war überrascht, dass ich es so klar schreiben konnte.

download.go


// Copyright  The Shinichi Nakagawa. All rights reserved.
// license that can be found in the LICENSE file.

package main

import (
	"archive/zip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
)

const (
	// DirName a saved download file directory
	DirName = "files"
)

// IsExist return a file exist
func IsExist(filename string) bool {

	// Exists check
	_, err := os.Stat(filename)
	return err == nil
}

// MakeWorkDirectory a make work directory
func MakeWorkDirectory(dirname string) {
	if IsExist(dirname) {
		return
	}
	os.MkdirAll(dirname, 0777)
}

// DownloadArchives a download files for retrosheet
func DownloadArchives(url string, dirname string) string {

	// get archives
	fmt.Println(fmt.Sprintf("download: %s", url))
	response, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Println(fmt.Sprintf("status: %s", response.Status))

	// download
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	_, filename := path.Split(url)
	fmt.Println(filename)
	fullfilename := fmt.Sprintf("%s/%s", dirname, filename)
	file, err := os.OpenFile(fullfilename, os.O_CREATE|os.O_WRONLY, 0777)
	if err != nil {
		fmt.Println(err)
	}
	defer func() {
		file.Close()
	}()

	file.Write(body)

	return fullfilename

}

// Unzip return error a open read & write archives
func Unzip(fullfilename string, outputdirectory string) error {
	r, err := zip.OpenReader(fullfilename)
	if err != nil {
		return err
	}
	defer r.Close()
	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		path := filepath.Join(outputdirectory, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(path, f.Mode())
		} else {
			f, err := os.OpenFile(
				path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer f.Close()

			_, err = io.Copy(f, rc)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// GetEventsFileURL return a events file URL
func GetEventsFileURL(year int) string {
	return fmt.Sprintf("http://www.retrosheet.org/events/%deve.zip", year)
}

// GetGameLogsURL return a game logs URL
func GetGameLogsURL(year int) string {
	return fmt.Sprintf("http://www.retrosheet.org/gamelogs/gl%d.zip", year)
}

func main() {
	// Commandline Options
	var fromYear = flag.Int("f", 2010, "Season Year(From)")
	var toYear = flag.Int("t", 2014, "Season Year(To)")
	flag.Parse()

	MakeWorkDirectory(DirName)

	wait := new(sync.WaitGroup)
	// Generate URL
	urls := []string{}
	for year := *fromYear; year < *toYear+1; year++ {
		urls = append(urls, GetEventsFileURL(year))
		wait.Add(1)
		urls = append(urls, GetGameLogsURL(year))
		wait.Add(1)
	}

	// Download files
	for _, url := range urls {
		go func(url string) {
			fullfilename := DownloadArchives(url, DirName)
			err := Unzip(fullfilename, DirName)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			wait.Done()
		}(url)
	}
	wait.Wait()

}

dataset.go

chadwick ist ein Skript, das ein Dataset erstellt, indem ein Befehl in einer Bibliothek zum Auswerten von Daten gedrückt wird.

Es fiel mir schwer, die in der Chadwick-Bibliothek implementierten Befehle "cwgame" und "cwevent" auszuführen.

Es ist dasselbe wie die Python-Version bis zu dem Punkt, an dem Change Directory zum Ausführen von Befehlen verwendet wird (Argumente in / out sind relative Pfade), aber die Pfadeinstellungen sind beim Schreiben verwirrt, und es gibt viele Argumente für das Format der Zeichenfolge (this). Ist mein Designfehler, aber ich wusste nicht, wie man Python "" {hoge} ".format (hoge =" fuga ")") schreibt, und ich kämpfte mit langweiliger Sucht.

dataset.go


// Copyright  The Shinichi Nakagawa. All rights reserved.
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
)

const (
	// ProjectRootDir : Project Root
	ProjectRootDir = "."
	// InputDirName : inputfile directory
	InputDirName = "./files"
	// OutputDirName : outputfile directory
	OutputDirName = "./csv"
	// CwPath : chadwick path
	CwPath = "/usr/local/bin"
	// CwEvent : cwevent command
	CwEvent = "%s/cwevent -q -n -f 0-96 -x 0-62 -y %d ./%d*.EV* > %s/events-%d.csv"
	// CwGame : cwgame command
	CwGame = "%s/cwgame -q -n -f 0-83 -y %d ./%d*.EV* > %s/games-%d.csv"
)

// ParseCsv a parse to eventfile(output:csv file)
func ParseCsv(command string, rootDir string, inputDir string) {
	os.Chdir(inputDir)
	out, err := exec.Command("sh", "-c", command).Output()

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Println(string(out))
	os.Chdir(rootDir)

}

func main() {
	// Commandline Options
	var fromYear = flag.Int("f", 2010, "Season Year(From)")
	var toYear = flag.Int("t", 2014, "Season Year(To)")
	flag.Parse()

	// path
	rootDir, _ := filepath.Abs(ProjectRootDir)
	inputDir, _ := filepath.Abs(InputDirName)
	outputDir := OutputDirName

	wait := new(sync.WaitGroup)
	// Generate URL
	commandList := []string{}
	for year := *fromYear; year < *toYear+1; year++ {
		commandList = append(commandList, fmt.Sprintf(CwEvent, CwPath, year, year, outputDir, year))
		wait.Add(1)
		commandList = append(commandList, fmt.Sprintf(CwGame, CwPath, year, year, outputDir, year))
		wait.Add(1)
	}
	for _, command := range commandList {
		fmt.Println(command)
		go func(command string) {
			ParseCsv(command, rootDir, inputDir)
			wait.Done()
		}(command)
	}
	wait.Wait()

}

Erfolge und Eindrücke

Jetzt, da ich einen Datensatz (CSV) nach Belieben erstellen kann, hat es meiner Meinung nach funktioniert.

Es gibt Raum für Verbesserungen in Design und Konfiguration, aber ich bin der Meinung, dass es klarer geschrieben werden kann als die Python-Version.

Der Editor verwendete übrigens das Go-Plug-In von IntelliJ IDEA.

Ich habe es benutzt, weil ich dafür bezahlt habe, einschließlich PyCharm, also dachte ich, ich würde es verbrauchen, aber es ist gut. Go wird weiterhin mit IntelliJ arbeiten.

Außerdem hatte ich das Gefühl, dass ich gerade v0.1 veröffentlicht hatte, also hörte ich auf, in LT zu springen (enttäuschend)

Hausaufgaben

Ich werde eine Implementierung durchführen, die den Datensatz in MySQL überträgt.

Danach habe ich es mit der Python-Version auf der Bank verglichen, und da ich Go on Docker noch nie verwendet habe, werde ich es mit dieser Bibliothek studieren.

Recommended Posts

[Baseball Hack] Ich habe versucht, das Python MLB Score & Grade-Datenerfassungsskript mit Go in einem halben Tag zu kopieren
Python-Skript, das MLB-Spieldaten im Wert von 15 Jahren in 10 Minuten in MySQL speichert (Baseball Hack!)
Ich habe das Windows PowerShell-Kochbuch durch ein Python-Skript ersetzt.
Ich habe auch versucht, die Funktionsmonade und die Zustandsmonade mit dem Generator in Python nachzuahmen
Ich schrieb einen Test in "Ich habe versucht, die Wahrscheinlichkeit eines Bingospiels mit Python zu simulieren".
Eine Geschichte, die nicht funktioniert hat, als ich versucht habe, mich mit dem Python-Anforderungsmodul anzumelden
Führen Sie den Python-Interpreter im Skript aus
Ich habe eine funktionale Sprache mit Python ausprobiert
[Python & SQLite] Ich habe den erwarteten Wert eines Rennens mit Pferden im 1x-Gewinnbereich ① analysiert
Einführung in die KI-Erstellung mit Python! Teil 2 Ich habe versucht, den Hauspreis in Boston mit einem neuronalen Netz vorherzusagen
Ich habe versucht, das Bild mit Python + OpenCV zu "glätten"
Ich habe versucht, das Bild mit Python + OpenCV zu "differenzieren"
Ich habe versucht, die Daten mit Zwietracht zu speichern
Ich habe versucht, mit Python ein Tippspiel zu spielen
Ich habe versucht, "Birthday Paradox" mit Python zu simulieren
Ich habe die Methode der kleinsten Quadrate in Python ausprobiert
Ich habe versucht, CloudWatch-Daten mit Python abzurufen
Ich habe versucht, das Bild mit Python + OpenCV zu "binarisieren"
Ich habe versucht, mit Python Faiss zu laufen, Go, Rust
Ich habe versucht, ein Python 3-Modul in C hinzuzufügen
Ich habe Python satt, also habe ich versucht, die Daten mit nehan zu analysieren (ich möchte sogar mit Corona live gehen) - Teil 2)
Ich habe Python satt, also habe ich versucht, die Daten mit nehan zu analysieren (ich möchte sogar mit Corona live gehen) - Teil 1)
Ich habe ein gestapeltes Balkendiagramm mit matplotlib von Python erstellt und eine Datenbeschriftung hinzugefügt
[New Corona] Ist der nächste Höhepunkt im Dezember? Ich habe die Trendanalyse mit Python versucht!
Ich habe versucht, einen Pandas-Datenrahmen zu erstellen, indem ich mit Python Informationen zum Lebensmittelrückruf abgekratzt habe
Ich habe versucht, eine Funktion zu erstellen, um zu beurteilen, ob die wichtigsten Aktien der Welt Sommerzeit mit Python sind
Ich habe versucht, die in Python installierten Pakete grafisch darzustellen
Ich habe jeden Tag LeetCode ausprobiert. 7. Reverse Integer (Python, Go)
Ich habe versucht, eine CSV-Datei mit Python zu berühren
Ich habe versucht, Soma Cube mit Python zu lösen
Ich habe versucht, einen Pseudo-Pachislot in Python zu implementieren
Ich habe jeden Tag 20 LeetCode ausprobiert. Gültige Klammern (Python, Go)
Ich möchte mit einem Roboter in Python arbeiten.
Ein Memo, dass ich den Datenspeicher mit Python berührt habe
Ich habe versucht, mit Scrapy Daten von einer Website zu sammeln
Ich habe versucht, das Problem mit Python Vol.1 zu lösen
Ich habe jeden Tag LeetCode 9 ausprobiert. Palindrome Number (Python, Go)
Ich habe jeden Tag LeetCode ausprobiert. 1. Zwei Summen (Python, Go)
Ich habe versucht, die API mit dem Python-Client von echonest zu erreichen
Ich habe die gleiche Datenanalyse mit kaggle notebook (python) und PowerBI gleichzeitig versucht ②
Ich habe eine Klasse erstellt, um das Analyseergebnis von MeCab in ndarray mit Python zu erhalten
Ich habe die gleiche Datenanalyse mit kaggle notebook (python) und PowerBI gleichzeitig versucht ①
Ich habe versucht, das Offline-Spracherkennungssystem Julius mit Python in der virtuellen Docker-Umgebung auszuführen
Ich habe versucht, die Entropie des Bildes mit Python zu finden
Versuchen Sie, COVID-19 Tokyo-Daten mit Python zu kratzen
Ich habe versucht, das Bild mit Python + OpenCV "gammakorrektur" zu machen
Ich habe versucht zu simulieren, wie sich die Infektion mit Python ausbreitet
Ich habe ein einfaches Tippspiel mit tkinter of Python gemacht