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.
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)
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,
Passer au traitement parallèle ~
Go est fort contre ◯◯, donc le baseball est aussi Go ~
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.
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()
}
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)
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