標籤:

[Python + SQL] NBA史上最弱的球隊是哪一個

原文:[Python入門] 05 元組與資料庫 - 集智

作者:Kaiser

部分代碼會有「代碼補完」字樣的注釋,原文是留給讀者自己補完並在線評測的,相當於小作業,這裡就請大家自行腦補吧。

01. elo值

elo值就像現在競技網遊里的天梯系統,隊伍在每場比賽後會根據表現有所調整,勝增敗減,小勝小增,大勝大增。elo值反映了一支隊伍在常規賽中的勝場期望,1800的對應期望是獲勝67場以上,就是王朝級強隊了。具體的天梯分段分布如下:

ELO值 匹配戰績 對應隊伍描述

1800 67-15 史詩級別

1700 60-22 總冠軍爭奪者

1600 51-31 季後賽水平

1500 41-41 平均水平

1400 31-51 樂透水平

1300 22-60 無言以對

1200 15-67 糟糕透頂

歷史上最高紀錄是96年總決賽階段的公牛,曾一度突破了1850分。

知名數據分析網站538(fivethirtyeight.com)提供了NBA歷史賽程的elo值記錄(至2015賽季),有六萬餘條數據。這個數據量不能算很大,但是在本地用Excel直接打開操作,估計體驗還是挺痛苦的,這裡就介紹一下如何用Python+SQL來處理,或許會對廣大勞形於Excel之間的朋友們有所幫助。

02. 元組

元組(tuple)是另一種Python中常用的數據類型,他跟列表非常相似,都可以包含若干元素,並且元素的調用都是通過方括弧[]+索引的形式。

sample_list = [0,1,2,3]sample_tuple = (0,1,2,3)# 列表的第1個元素sample_list[0]# 元組的第2個元素sample_tuple[1]

主要區別在於:

  • 元組用括弧()定義,列表用方括弧[]定義
  • 元組不可更改
  • 即使只有一個元素,也需要有逗號,如(item1, )。如果缺了這個逗號,得到的仍是元素本身,而不是元組。

元組只能在定義時賦值,如果強行更改會得到解釋器的錯誤提示。

TypeError:"tuple" object does not support item assignment

有的函數返回值並非一個數值或字元,而是具有多重輸出,這時就以元組格式存在。比如下例返回的是兩個輸入參數的和與乘積,可以點擊運行查看,兩個輸出在()中。

def sum_times(x, y): return (x + y), (x * y)sum_times(2,3)

03. 資料庫

在之前的幾篇教程中,我們已經接觸過了一些數據集,既有在程序中生成的,也有存儲在文本文件中的。對於體積較大的數據集,一般都存在文件中,程序運行時再讀入內存。

但是文件存儲數據(.txt或.csv)還面臨很大的問題,一是當數據量比計算機內存還要大的時候,會帶來沉重的計算負擔甚至無法運行;二是數據有可能高頻變化,比如電商在雙11高峰期每秒就有幾萬次交易,普通的文件格式顯然無法處理。

於是資料庫(database)應運而生。資料庫首先是結構化(structured)存儲數據的方式,為了更加靈活的處理數據,程序不再一次性將所有數據讀入內存,而是根據具體需要進行查詢(query),獲得相應的數據集。完成這些工作的程序語言叫作SQL(Structured Query Language),在計算機方面稍有常識的朋友想必都對這個縮寫不會陌生。

著名的關係型資料庫管理系統有MySQL, Oracle等。

04. SQLite

SQL是專為查詢、操作資料庫所用的語言,所以不像Python, JavaScript等語言那樣功能豐富,擴展多樣。

一個資料庫由若干個表(table)組成,就像每個Excel文件里有多個Sheets。每個表又包含行(row)列(column),這就比較好理解了。一代表一個樣本,而多定義了各個維度上的屬性。

SQLite是一種輕型的資料庫管理系統,佔用資源極低且處理速度快,目前更新到了3版本。Python有專門處理SQLite語句的庫sqlite3

import sqlite3

以下我們將以一份NBA的歷史賽程數據為例,來了解SQL的基本法則與應用方法。(數據來源:FiveThirtyEight)

05. 查詢

與資料庫進行通信的最基本形式是查詢(query),即返回符合條件的數據子集,其基本關鍵字是SELECT,SELECT後面跟所選列的表頭。前面提到過一個資料庫里可能存在多個表,所以查詢語句必須指定來源表,關鍵字是FROM。

SELECT column1, column2, ... FROM table;

注意SQL是要求句末有分號(;)的,這與Python的習慣大為不同,可能會喚起一度為C++支配的恐懼。

NBA資料庫里只含一張表,名為sheet,假設我們想要查詢sheet表中的team_id列,那麼對應的SQL是:

SELECT team_id FROM nba_history;

如果資料庫非常龐大,那麼即使哪怕僅僅查詢某些列,也是不小的計算量,這裡可以引入新的關鍵字LIMIT,之後接想要查詢的行數。比如只想要前5行的team_id列,那麼SQL為:

SELECT team_id FROM nba_history LIMIT 5;

下例將通過Python調用SQLite,查詢elo_nwin_equiv兩列的前5行:

import sqlite3conn = sqlite3.connect("/mnt/vol0/Py_Intro/05_tuple_database/nbaallelo.db")cur = conn.cursor()# 代碼補完query = #代碼補完cur.execute(query)elos = cur.fetchall()conn.close()print(elos)

可見,查詢語句返回的是以元組(tuple)為元素的列表(list)。上例的輸出即是5個(elo_n, win_equiv)組成的列表,應為:

[(1293.2767, 40.29483), (1306.7233, 41.70517), (1309.6521, 42.012257), (1297.0712, 40.692783), (1279.6189, 38.864048)]

06. SQLite with Python

在04節最後的例子中,除了SQL語句,還有很多Python命令,這些是Python調用SQLite的語句。

連接對象

首先是建立與資料庫的聯繫,sqlite3.connect()將返回Connection實例對象,然後存為conn變數,此時的conn對應的是整個資料庫。

import sqlite3conn = sqlite3.connect("/mnt/vol0/Py_Intro/05_tuple_database/nbaallelo.db")

游標對象

Connection對象的.cursor()可以創建游標對象(cursor object)。游標對象可以對資料庫執行SQL語句並進行更靈活的數據操作。

query是純SQL語句,通過cur.execute()實際執行,此時資料庫查詢的結果仍在cur對象中。最後調用cur.fetchall()將查詢結果全部返回,並存至變數elos,就是最終得到的元組列表。

如果只想返回一條查詢結果,可以使用cur.fetchone()

關於Python與SQLite3的聯合應用,以後還會深入講解,本篇仍將重點回歸到SQL語句上。

07. Where

僅僅從資料庫的某個表中查詢某一列的前若干行,這樣的操作局限性太大,很難滿足應用需求。實際上我們感興趣的數據子集並非總是按照順序排列,而是符合某種限制條件。

為了進一步縮減精確查詢範圍,可以使用關鍵字WHERE。比如我們想要查詢NBA歷史上,賽後elo值elo_n高於1850的強隊ID,其SQL語句是:

SELECT team_id, elo_n FROM sheet WHERE elo_n > 1850

查詢結果表明,歷史上唯一一支elo值曾經突破1850的隊伍是1996年總決賽時期的芝加哥公牛。

下面請查詢elo_n低於1100的弱隊ID,及其對應比賽日期。sqlite3庫以及連接對象、游標對象已經預定義,可以從定義SQL查詢語句query開始。

# 代碼補完query =# 代碼補完cur.execute(query)elo_1100 = cur.fetchall()conn.close()print(elo_1100)

查詢篩選結果顯示,歷史上一度衰到1100以下的弱旅,也只有1968年一支名為"Squires"的隊伍。

他們這麼弱,我認為與名字起的不好是分不開的。"Squire"在英文中是「侍從」的意思,在《爐石傳說》中,就有很多僕從名為"Squire",比如11聖盾小兵:

原文地址:[Python入門] 05 元組與資料庫 - 集智

系列首篇:[Python入門] 01 基本法則 - 集智

交流群組:557373801

想成為合作作者的朋友,歡迎與我聯繫。

推薦閱讀:

簡單三步,用 Python 發郵件

TAG:Python | SQL | NBA |