[Baseball Hack] J'ai essayé de copier le script d'acquisition de données de score et de note Python MLB avec Go en une demi-journée

Ce que j'ai fait

Il y a un peu plus d'un mois, j'ai copié le script de données MLB baseball "[py-retrosheet](https://github.com/Shinichi-Nakagawa/py-retrosheet" py-retrosheet ")" compatible avec Python 3.5 avec golang. C'était.

go-retrosheet

Il s'agit d'un script sur Retrosheet (http://www.retrosheet.org/) qui télécharge les données de correspondance et les résultats (principalement des données au bâton), les analyse à un niveau qui peut être utilisé pour l'agrégation et l'analyse, et crache le CSV.

L'original a une fonction pour fouiller dans la base de données (MySQL, etc.), mais je le ferai plus tard (raison: je ne veux pas réduire le temps de sommeil ... w).

Ce n'est pas grave, mais le nom "Go! Retrosheet" a beaucoup d'élan et est cool (bâtons de lecture)

Pourquoi avez-vous décidé de le faire avec Golang

Il y a deux raisons.

Dans Last #gocon, "Je n'ai jamais fait Golang même si je peux y aller, alors j'ai fait quelque chose avec Golang. Avec la motivation de "Let's jump in LT!" [J'ai fait A Tour of Go en une demi-journée et réécrit l'application Django avec Martini et j'ai eu un merveilleux saut dans LT](http://shinyorke.hatenablog.com/entry/2015/06/21 / 195656 "Golang J'ai écrit une application de baseball une demi-journée plus tard et j'ai lancé le rapport d'été de LT ~ Go Conference 2015 #gocon") Mais quand j'y pense, je pense que je n'ai pas écrit Golang depuis (il y avait PyCon en route). ..), j'écrirai cette fois pour commémorer la participation de #gocon en hiver! J'ai commencé avec ce sentiment. LT pensait le faire à temps.

Aussi, comme raison sérieuse,

Je ne sais pas assez Go pour dire ... (ou plutôt, je suis un débutant en crunching), alors reconfirmons la bonté et les fonctionnalités de Go de la copie! La signification du terme «apprentissage» est également incluse.

Ce que j'ai fait

Je passe gofmt et golint, mais il y a peu de commentaires sur l'ensemble & ça peut convenir ... Experts, j'attends les avis m (_ _) m

De plus, je n'ai écrit aucun test, c'est donc un code hérité croustillant.

download.go

C'est le code qui télécharge et décompresse l'archive (fichier zip) contenant le CSV de Retrosheet sous le répertoire des fichiers.

J'ai écrit le code pour décompresser le zip en me référant à ici et merci de travailler correctement! Mais je me demande si je peux écrire plus intelligemment? J'ai pensé.

Aussi, j'ai écrit le traitement parallèle dans Go pour la première fois, mais j'ai été surpris de pouvoir l'écrire si clairement.

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 est un script qui crée un jeu de données en appuyant sur une commande dans une bibliothèque dédiée aux données de score.

J'ai eu du mal à exécuter les commandes "cwgame" et "cwevent" implémentées dans la bibliothèque chadwick.

C'est la même chose que la version Python jusqu'au point où la commande est exécutée avec Change Directory (l'argument in / out est un chemin relatif), mais le paramètre Path est confus lors de l'écriture, et il existe de nombreux arguments pour le format de la chaîne de caractères (this) C'est mon erreur de conception, mais je ne savais pas comment écrire Python "" {hoge} ".format (hoge =" fuga ")"), et je me suis débattu avec une dépendance ennuyeuse.

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()

}

Réalisations et impressions

Maintenant que je peux créer un ensemble de données (CSV) comme je le souhaite, je pense que cela a fonctionné.

Il y a place à l'amélioration dans la conception et la configuration, mais je pense que cela peut être écrit plus clairement que la version Python.

À propos, l'éditeur a utilisé le plug-in Go d'IntelliJ IDEA.

Je l'ai utilisé parce que je payais pour cela, y compris PyCharm, alors j'ai pensé que je l'utiliserais, mais c'est bien. Go continuera d'utiliser IntelliJ.

De plus, j'avais l'impression que je venais de sortir la v0.1 plus tôt, alors j'ai arrêté de sauter en LT (décevant)

devoirs

Je vais faire une implémentation qui pousse l'ensemble de données dans MySQL.

Après cela, je l'ai comparé à la version Python sur le banc, et comme je n'ai jamais utilisé Go sur Docker, je vais l'étudier avec cette bibliothèque.

Recommended Posts

[Baseball Hack] J'ai essayé de copier le script d'acquisition de données de score et de note Python MLB avec Go en une demi-journée
Script Python qui stocke 15 ans de données de jeu MLB dans MySQL en 10 minutes (Baseball Hack!)
J'ai remplacé le livre de recettes Windows PowerShell par un script python.
J'ai aussi essayé d'imiter la fonction monade et la monade d'état avec le générateur en Python
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
Une histoire qui n'a pas fonctionné lorsque j'ai essayé de me connecter avec le module de requêtes Python
Exécuter l'interpréteur Python dans le script
J'ai essayé un langage fonctionnel avec Python
[Python & SQLite] J'ai analysé la valeur attendue d'une course avec des chevaux dans la fourchette 1x win ①
Introduction à la création d'IA avec Python! Partie 2 J'ai essayé de prédire le prix de l'immobilier dans la ville de Boston avec un réseau neuronal
J'ai essayé de "lisser" l'image avec Python + OpenCV
J'ai essayé de "différencier" l'image avec Python + OpenCV
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé de jouer à un jeu de frappe avec Python
J'ai essayé de simuler "Birthday Paradox" avec Python
J'ai essayé la méthode des moindres carrés en Python
J'ai essayé d'obtenir des données CloudWatch avec Python
J'ai essayé de "binariser" l'image avec Python + OpenCV
J'ai essayé d'exécuter faiss avec python, Go, Rust
J'ai essayé d'ajouter un module Python 3 en C
J'en ai marre de Python, alors j'ai essayé d'analyser les données avec nehan (je veux aller vivre même avec Corona) -Partie 2)
J'en ai marre de Python, alors j'ai essayé d'analyser les données avec nehan (je veux aller vivre même avec Corona) -Partie 1)
J'ai créé un graphique à barres empilées avec matplotlib de Python et ajouté une étiquette de données
[New Corona] Le prochain pic est-il en décembre? J'ai essayé l'analyse des tendances avec Python!
J'ai essayé de créer un cadre de données pandas en grattant les informations de rappel d'aliments avec Python
J'ai essayé de créer une fonction pour juger si les principaux stocks du monde sont l'heure d'été avec python
J'ai essayé de représenter graphiquement les packages installés en Python
J'ai essayé LeetCode tous les jours 7. Integer Integer (Python, Go)
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé d'implémenter un pseudo pachislot en Python
J'ai essayé LeetCode tous les jours 20. Parenthèses valides (Python, Go)
Je veux travailler avec un robot en python.
Un mémo que j'ai touché au magasin de données avec python
J'ai essayé de collecter des données sur un site Web avec Scrapy
J'ai essayé de résoudre le problème avec Python Vol.1
J'ai essayé LeetCode tous les jours 9. Palindrome Number (Python, Go)
J'ai essayé LeetCode tous les jours 1. Two Sum (Python, Go)
J'ai essayé de frapper l'API avec le client python d'echonest
J'ai essayé la même analyse de données avec kaggle notebook (python) et PowerBI en même temps ②
J'ai créé une classe pour obtenir le résultat de l'analyse par MeCab dans ndarray avec python
J'ai essayé la même analyse de données avec kaggle notebook (python) et PowerBI en même temps ①
J'ai essayé d'exécuter le système de reconnaissance vocale hors ligne Julius avec python dans l'environnement virtuel Docker
J'ai essayé de trouver l'entropie de l'image avec python
Essayez de gratter les données COVID-19 Tokyo avec Python
J'ai essayé la "correction gamma" de l'image avec Python + OpenCV
J'ai essayé de simuler la propagation de l'infection avec Python
J'ai fait un jeu de frappe simple avec tkinter de Python