Python數據科學:技術詳解與商業實戰(第三章至第五章學習筆記)

文章目錄

第二章 Python 的基本運算

2.1 算術運算

算術運算符說明表
符號 含義 符號 含義
** 乘方 正負號 +、-
乘除 *、/ 整除 /
取餘 % 加減 +、-

2.2 比較運算

  • 數值型:按照值進行比較

  • 字符型:按照 ASCII 碼進行比較

    比較運算符說明表
    符號 含義 符號 含義
    <、> 小於、大於 <=、>= 小於等於、大於等於
    == 比較等於或者邏輯等於 != 不等於
#數值型
3 < 4 < 7
4 > 1 == 1
4 != 3
8 > 10
2 <= 5

        輸出結果

True
True
True
False
True
#字符型
'A' < 'a'
'ab' > 'cd'
'abc' == 'abc'

        輸出結果

True
False
True

2.3 邏輯運算

        邏輯運算符優先級:not(取反)、and(並且)、or(或者)

x,y = 2,8
x < 6
Out[184]: True
#not    
not(x < 6)
Out[185]: False
    
x is y
Out[186]: False

not(x is y)
Out[187]: True

#and  
(x < 6) and (y > 10)
Out[189]: False
    
#or   
(x < 6) or (y > 10)
Out[188]: True

比較運算和邏輯運算的結果都是布爾型的值。


四則運算小練習

        從鍵盤輸入兩個整數,求這個兩個整數的和、差、積、商(嘗試用一般除法和整除兩種方式)並輸出。

        提示:注意input()函數的返回類型。

#參考答案
x = int(input("input x : "))
y = int(input("input y : "))
print("x+y=",x + y)#加法運算
print("x-y=",x - y)#減法運算
print("x*y=",x * y)#乘法運算
print("x/y=",x / y)#除法運算
print("x//y=",x // y)#整除運算

        運算結果

input x : 9

input y : 2
x+y= 11
x-y= 7
x*y= 18
x/y= 4.5
x//y= 4

第3章 數據科學的Python編程基礎

序列

        Python 中有6種序列,其中字符串、元組和列表是最常用的形式,序列中每個元素都有一個跟位置相關的序號,也稱爲索引,對一個有 N 個元素的序列來說,第一個元素的索引從 0 開始,最後一個元素的索引就是 N-1 ,也可以從最後一個元素開始計數,最後一個元素的索引就是 -1 ,第一個元素的索引就是 -N

        序列對象是可迭代的,也就是說可以遍歷對象的內部元素。序列對象的迭代需要用到它的索引,一次可以訪問一個或多個元素,也叫切片。

序列相關操作

  • 標準類型運算符

    • 值比較
    • 對象身份比較
    • 布爾運算(邏輯運算)
  • 序列類型運算符

    • 獲取
    • 重複
    • 連接
    • 判斷
  • 內建函數

    • 序列類型轉換內建函數
    • 序列類型可用內建函數

標準類型運算符

序列類型運算符

week = ['Monday','Tuesday','Wednesday','Thursday','Friday']
#列表逆序:week[::-1]
print(week[0],week[-5],'\n',week[4],week[-1],'\n',week[0:4],week[:4],'\n', week[::-1]) 
'ba'*3 #內容重複
'Hello' + ' Python' #連接
'BA' in ('BAT') #判斷

        輸出結果

Monday Monday 
 Friday Friday 
 ['Monday', 'Tuesday', 'Wednesday', 'Thursday'] ['Monday', 'Tuesday', 'Wednesday', 'Thursday'] 
 ['Friday', 'Thursday', 'Wednesday', 'Tuesday', 'Monday']

Out[568]: 'bababa'

Out[569]: 'Hello Python'

Out[570]: True

序列類型轉換內建函數

list()
str()
tuple()
tuple('apple')
list('apple')

序列類型其他常用內建函數

函數 含義 函數 含義
len() 長度 sorted() 排序
max() 最大值 min() 最小值
sum() 求和 reversed() 逆序
enumerate() zip()

        enumerate() 函數,返回的是一個 enumerate 對象,它的元素是由元素的索引和值構成的一個一個的元組,在又要遍歷索引又要遍歷元素的時候使用非常方便,zip() 函數由一系列可迭代的對象作爲參數,返回一個 zip 對象,它把對象對應的元素打包成一個一個的元組。
        還有另一個常用的序列 range() 對象。
range函數

        rang()函數可以產生一組有規律的數據,

語法規則

        假設你需要一個整數序列,特別是等差數列,那麼你就可以使用Python中的range()函數。range()函數使用的時候可以帶不同的參數,可以是一個、兩個或者三個。
  • range(start,end,step=1) 三個參數:第一個參數表示起始值,默認值是0,第二個參數代表終止值,這個值是不包含在內的,第三個參數是步長,默認值是1。
  • range(start,end) 兩個參數:默認步長爲1,只設置起始值和終止值。
  • range(end) 一個參數:默認起始值爲0,步長爲1,只設置終止值

        產生一系列整數,返回值是一個range對象,也是Python中的一種內建序列,range對象是可迭代的。

#range()函數
list(range(5,26,5))#三個參數
list(range(5,10))#兩個參數
list(range(5))#一個參數

        輸出結果

Out[240]: [5, 10, 15, 20, 25]
Out[241]: [5, 6, 7, 8, 9]
Out[242]: [0, 1, 2, 3, 4]

        規律:假設步長爲1的情況,產生值的個數等於參數值的差。比如上面的例子中range(5,10),步長爲1,最終可以產生10-5=5個值。
        Python 3中的range()函數跟Python 2當中的xrange()函數比較類似,函數返回結果是一個range對象,類似一個生成器generator,生成器利用惰性計算的機制,也就是說它不會一次性的產生所有的數,而是每計算出一個條目以後,把這個條目產生出來,也就是用多少生成多少,類似一個lazy list。range()函數在循環中使用非常多,特別適合和for語句聯用。

        Python 的基本數據類型包括幾種,如下所示

3.1 Python 的基本數據類型

3.1.1 字符串(str)

        單引號、雙引號、三引號包圍的都是字符串

'apple'
"example"
'''instance'''

換行符

print('The first row \nThe second row')

        輸出結果

The first row 
The second row

製表符

#製表符
print('123\t456')

原始字符串操作符(r/R): 用於一些不希望轉義字符起作用的地方

#加r原始輸出
print(r'You can choose a/b/c')
print(r'C:\Some\name')

        輸出結果

You can choose a/b/c
C:\Some\name

轉義字符

        如果我們不使用原始字符串操作符,我們可以使用轉義字符使得路徑中的反斜槓原始輸出。也就是在字符串裏面,如果你想要表示一個反斜槓的話,你需要兩個反斜槓表示。

py = open('G:\Pythoncode\test.py','w') #出現錯誤
Traceback (most recent call last):

  File "<ipython-input-192-88c4715893f4>", line 1, in <module>
    py = open('G:\Pythoncode\test.py','w')

OSError: [Errno 22] Invalid argument: 'G:\\Pythoncode\test.py'
        
py = open('G:\\Pythoncode\\test.py','w')#轉移字符“\”
#或者
py = open(R'G:\Pythoncode\test.py','w')#加上原始字符串操作符r或者R

        這裏簡單說一下:open()用來打開一個文件,文件的路徑就是它的第一個參數,而 w 就是寫文件。上面的語句的功能就是在G盤的Pythoncode目錄下新建一個text.py文件。

字符串的加法

'I love '+'Python'

使用索引訪問元素

introduce = "Please speak English"
introduce[0:6]
introduce[-1]

        輸出結果

'Please'
'h'

注意:字符串是不可變類型


introduce[0] = 'PP'
TypeError: 'str' object does not support item assignment

3.1.2 布爾值(Bool: True/False)

        Python布爾值一般通過邏輯判斷產生,只有兩個可能結果,True或者False

'a' is 'A'
Out[18]: False

'a' is 'a'
Out[19]: True

1==1
Out[20]: True

True and False
Out[21]: False

True or False
Out[22]: True

not False
Out[23]: True

        布爾值轉換可以使用內置函數bool,除數字0外,其他類型用bool轉換結果都爲True

bool(1)
Out[24]: True

bool(0)
Out[25]: False

bool(10000)
Out[26]: True

bool('a')
Out[27]: True

        布爾型變量本質上是用整型的1、0分佈存儲的。

x = True
y = False
int(x)
Out[143]: 1
int(y)
Out[144]: 0

3.1.3 浮點型

        即數學中的實數,可以類似科學計數法表示,如下面例子中 e3 代表的是 10 的 3 次方。

9.8e3
Out[145]: 9800.0
    
floatdata = -4.78e-2
type(floatdata)
Out[146]: float

3.1.4 複數型

  • 實數+虛數 就是複數,當然複數也可以沒有實部,例如 3j;虛部也可以爲0,例如 6+0j。

  • 虛數部分用標記符 j 表示

  • 複數型就是 complex

#複數型
5.3+4.6j#實數+虛數

3j#沒有實部
type(3j)#類型爲complex
Out[150]: complex
    
9+0j#虛部爲0
type(9+0j)
Out[151]: complex

        複數還可以對它的實數部分和虛數部分進行分離。

  • 實部:複數.real

  • 虛部:複數.imag

  • 共軛複數:複數.conjugate()

    #分離實部和虛部
    a = 5.2+6.3j
    a.real#實部
    Out[153]: 5.2
        
    a.imag#虛部
    Out[154]: 6.3
        
    a.conjugate()#共軛複數
    Out[155]: (5.2-6.3j)
    

3.1.5 特殊的數據類型

        Python中,還有一些特殊的數據類型,例如無窮值、nan(非數值)、None等。

        注意,正負無窮相加返回nan(not a number),表示非數值。

#創建負無窮
float('-inf')
#創建正無窮
float('+inf')

float('-inf')+1000000

float('-inf')+float('+inf')

3.2 Python 的基本數據結構

3.2.1 列表(list)

  1. 創建列表

        列表 list 是 Python 內置的一種數據類型,是一種有序的集合,用來存儲一連串元素的容器,列表用 [ ] 來表示,其中元素的數據類型可不相同除了使用 [ ] ,還可以使用list()函數

list_1 = [1,2,'a',True,3.2] 
list_1
list('abcde')
list([1,2,'a','b'])
#包含多個元組的列表
Plist = [('axp','American','80.6'),
        ('BA','The company','89.1'),
        ('cat','System','33.7')]
Plist

        輸出結果

[1, 2, 'a', True, 3.2]
['a', 'b', 'c', 'd', 'e']
[1, 2, 'a', 'b']
[('axp', 'American', '80.6'),
 ('BA', 'The company', '89.1'),
 ('cat', 'System', '33.7')]
  1. 通過索引對訪問或修改列表的元素

        使用索引時,通過 [ ] 來指定位置。在Python中,索引的起始點位置爲0。

list_1 = [1,2,'a',True,3.2] 
list_1[0]

        輸出結果

Out[40]: 1

        可以通過 :符號選取指定序列位置的元素,例如選取第1個到第4個位置的元素,注意這種索引取數是前包後不包(包括0位置,但不包括4位置,即取0,1,2,3位置的元素)

list_1[0:4]#取0,1,2,3位置的元素

        Python中的負索引表示倒序位置,例如-1表示列表最後一個位置的元素

list_1[-1]#訪問最後一個位置的元素

        列表支持加法運算,表示兩個或多個列表合併爲一個列表

list('abc')+list('de')

        Python中,列表對象內置了一些方法,append方法表示在現有列表中添加一個元素

mylist = ['a','b','c']
mylist.append('d')
mylist

        輸出結果

['a', 'b', 'c', 'd']

        extend方法類似列表加法運算,表示將兩個列表合併爲一個列表

mylist.extend(list('ABCD'))
mylist

        輸出結果

['a', 'b', 'c', 'd', 'A', 'B', 'C', 'D']

        某學校組織一場校園歌手比賽,每個歌手的得分由10名評委和觀衆決定,最終得分的規則是去掉10名評委所打分數的一個最高分和一個最低分,再加上所有觀衆評委分數後的平均值。
        評委打出的10個分數爲:9、9、8.5、10、7、8、8、9、8和10,觀衆評委打出的綜合評分爲9,請計算該歌手的最終得分。

jScores = [9,9,8.5,10,7,8,8,9,8,10]#10名評委的打分
aScore = 9 #觀衆打分
#對評委的十個分數進行排序
#列表的sort()方法
jScores.sort()#若使用sorted(jScores),則需要賦值新的變量
# x = sorted(jScores)
jScores.pop()#去掉一個最高分:10
jScores.pop(0)#去掉一個最低分:7
jScores.append(aScore)#加上觀衆得分:9
jScores
aveScore = sum(jScores)/len(jScores)#最終得分
print(aveScore)

        對評委的十個分數進行排序,使用列表的 sort() 方法。如果用sorted()函數這樣的寫法,其實 jScores 裏面的內容並不會改變,除非我們把他們的結果賦給另外一個變量纔可以;現在要去掉一個最高分和最低分,在列表當中,我們可以通過列表的 pop() 方法彈出最後一個值,或者加上參數,就是它的索引,彈出最低的值;再通過append() 把aScore 這個分數到原始的 jScore 這個列表當中,最後通過 sum() 函數len() 計算它們的平均值,這就是最終的得分。

        輸出結果

#排序後
Out[517]: [7, 8, 8, 8, 8.5, 9, 9, 9, 10, 10]
#去掉一個最高分
Out[519]: [7, 8, 8, 8, 8.5, 9, 9, 9, 10]
#去掉一個最低分
Out[521]: [8, 8, 8, 8.5, 9, 9, 9, 10]
#加上觀衆得分
Out[523]: [8, 8, 8, 8.5, 9, 9, 9, 10, 9]
#最終得分
8.722222222222221   

        將工作日([‘Monday’,‘Tuesday’,‘Wednesday’,‘Thursday’,‘Friday’])和週末([‘Saturday’,‘Sunday’])的表示形式合併,並將它們用序號標出並分行顯示。
        對於一個機要遍歷索引,又要遍歷元素本身的一個功能,我們可以用 enumerate() 這樣的一個函數,因爲最後要輸出序號1到7,默認是0到6,因此我們把 i 增加 1。

week = ['Monday','Tuesday','Wednesday','Thursday','Friday']
weekend = ['Saturday','Sunday']
#合併兩個部分內容的列表合併
week.extend(weekend)
week
#append()
#week.append(weekend)
#week
for i,j in enumerate(week):
    print(i+1,j)

        輸出結果

Out[535]: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday

        注意:week 列表和 weekend 列表的合併,這邊不要使用 append(),否則它的合併結果爲:

Out[530]:
[‘Monday’,
‘Tuesday’,
‘Wednesday’,
‘Thursday’,
‘Friday’,
‘Saturday’,
‘Sunday’,
[‘Saturday’, ‘Sunday’]]

        前面是week列表的元素,後面是一個列表,裏面是’Saturday’和 ‘Sunday’。


列表方法

列表常用的方法
append() copy() count() extend() insert()
pop() remove() reverse() index() sort()

        使用列表函數的方法可以非常高效地解決一些問題,當然在用的時候要注意它們之間的區別,比如 sort() 和sorted() 、reverse() 和 reversed() 。我們在使用的時候,還要注意方法或者函數的參數,有時候參數的使用,也可以讓問題解決變得更加高效。

參數的作用

list.sort(key=None,reverse=False)

        比如我們要逆序去排一個列表,這個時候就需要使用 reverse() 這樣的一個參數,把它置爲 True 即可。又比如我們要按照長度對列表中的元素進行排序,我們就把 key 設爲 len,這樣的一個函數名即可。

numlist = [3,11,5,8,16]
fruitlist = ['apple','banana','pear','lemon','avocado']
#逆序
numlist.sort(reverse=True)
numlist
#key參數爲len
fruitlist.sort(key=len)
fruitlist  

        輸出結果

Out[539]: [16, 11, 8, 5, 3]

Out[540]: ['pear', 'apple', 'lemon', 'banana', 'avocado']

列表解析

        一般什麼時候用到列表解析呢?一般在需要改變列表,而不是需要新建某個列表的時候可以使用它。
        列表解析的基本語法規則是由多個 for 循環以及可迭代的序列構成,另外也可以加條件,其中條件是可選的。它主要有兩種語法:

  • 第一種是不帶 if 條件,首先迭代 sequence 裏面的所有的內容,每一次迭代都把 sequence 裏面的內容放到前面的對象裏面去,然後再在表達式裏面應用這樣的一個對象,形成一個列表;
  • 第二種語法是加入判斷語句,只有滿足條件的內容,才把 sequence 裏面的相應內容放到 expression 這個對象裏面去,再在表達式裏面應用這樣的一個對象,形成一個列表。
[x for x in range(10)]#可迭代的range(10)
[x**2 for x in range(10)]
[x**2 for x in range(10) if x**2<50]
[(x+1,y+1) for x in range(2) for y in range(2)]

        輸出結果

Out[541]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Out[542]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Out[543]: [0, 1, 4, 9, 16, 25, 36, 49]

Out[544]: [(1, 1), (1, 2), (2, 1), (2, 2)]

        我們也可以通過圓括號來創建生成器表達式,注意:生成器表達式並不是創建一個列表,而是返回一個生成器,這個生成器在每次計算出一個條目以後,才把這個條目產生出來,所以在大數據量處理的時候有優勢

>>> sum(x for x in range(10))
45

3.2.2 元組(tuple)

創建元組

        通過()創建元組

tuple_0 = (1,2,3)
tuple_0

        輸出結果

(1, 2, 3)

        創建只有一個元素的元組,可以在元素後面加上一個逗號

2014,
Out[547]: (2014,)

        計算元組的長度

bTuple = (['Monday',1],2,6,['Sunday',6])
len(bTuple)

        輸出結果爲4。

元素不可修改性

        元組與列表類似,區別在於列表中,任意元素可以通過索引進行修改。而元組中,元素不可更改,只能讀取。
        列表可以進行復制,而同樣的操作應用於元組則報錯。

#列表元素可以改變
list_0 = [1,2,3]
list_0[0] = 'a'
list_0[0]

        輸出結果

'a'
#元組元素不可以改變
tuple_0 = (1,2,3)
tuple_0[0] = 'a'

        輸出結果

TypeError: 'tuple' object does not support item assignment

        會產生異常,異常的原因顯示元組對象不支持元素賦值,這就說明了列表可變,而元組不可變。

        在Python中,元組類對象一旦定義,就不能修改

元組的加法運算

        元組中支持加法運算,即合併元組。

(2,3,4)+(5,6,7)

        輸出結果

 (2, 3, 4, 5, 6, 7)

訪問元素

        元組也可以像列表一樣通過索引訪問元素

tuple_3[0]
tuple_3[0:4]

        輸出結果

2
(2, 3, 4, 5)

        獲取二級元素的值

bTuple = (['Monday',1],2,6,['Sunday',6])
bTuple[0][0]
bTuple[1:]

        輸出結果

Out[549]: 'Monday'

Out[550]: (2, 6, ['Sunday', 6])

函數的適用類型

        我們可以利用 sorted() 函數對列表進行排序(這個方法只是新生成一個列表的副本,原始的列表內容沒有改變),我們也可以通過列表的 sort() 方法對列表進行排序,這個排序就是對原始列表的真正的排序,列表內容會改變。
        在元組中,sorted() 函數可以使用,因爲元組也是一種序列,而 sort() 方法因爲要對對象進行修改,因爲原創元組並不能使用 sort() 方法。

#列表
alist = [5,4,8,7,2]
sorted(alist)
alist
alist.sort()
alist

        輸出結果

Out[554]: [2, 4, 5, 7, 8]
Out[555]: [5, 4, 8, 7, 2]
Out[556]: [2, 4, 5, 7, 8]
#元組
aTuple = (5,4,8,7,2)
sorted(aTuple)
aTuple
aTuple.sort()

        輸出結果

Out[558]: [2, 4, 5, 7, 8]
Out[559]: (5, 4, 8, 7, 2)
    
Traceback (most recent call last):

  File "<ipython-input-560-53409d016532>", line 1, in <module>
    aTuple.sort()

AttributeError: 'tuple' object has no attribute 'sort'

        aTuple.sort() 會產生異常,異常的原因是元組對象並沒有 sort() 方法,它的原因就是因爲它不可變

元組的作用

        可變長的函數參數

def foo(args1,*args2):
    print(args1)
    print(args2)

#調用foo函數
foo('Hello','G','U','E','T')

        這裏的實參是五個字符串,第一個字符串"hello"是一個位置參數,傳給 args1 ,後面的四個字符串就傳給了 args2,可以發現這裏的 * 有收集參數的作用。
        輸出結果

Hello
('G', 'U', 'E', 'T')

        從輸出結果我們可以看到,Python 中多個元素可以構成一個元素作爲函數的參數,而元組的個數是不定長的,所以這就是可變長的函數參數。

元組作爲函數特殊返回類型

def fun1():
    return 1,2,3

fun1()

        輸出結果

Out[566]: (1, 2, 3)

        調用之後,可以發現返回值有3個值時程序以一個元組的結果來呈現,Python 這樣的做法讓主調函數和被調函數之間數據的交互功能變得更加強大、靈活和方便,但是又跟原來的程序設計語言返回對象只有一個或者是0個這樣的一種傳統的方法保持一致。

返回對象的個數 返回類型
0 None
1 object
>1 tuple

3.2.3 集合(set)

創建集合

        Python中,集合是一組key的集合,其中key是不能重複。可以通過列表、字典或字符串等創建集合,或者通過{}符號進行創建。集合主要有兩個功能,一個功能是進行集合操作,另一個功能是消除重複元素。

basket = {'apple', 'orange', 'pear'}
basket

        輸出結果

{'apple', 'orange', 'pear'}

集合運算

Python支持數學意義上的集合運算。

A = {1,2,3}
B = {3,4,5}
#A、B的差集,即集合A的元素去除AB共有的元素
A-B
#A、B的交集,即集合A與集合B的全部唯一元素
A|B
#A、B的交集,即集合A和集合B的共同元素
A&B
#A、B的對稱差,即集合A與集合B的全部唯一值
A^B

        輸出結果

Out[69]: {1, 2}
Out[70]: {1, 2, 3, 4, 5}
Out[71]: {3}
Out[72]: {1, 2, 4, 5}

注意:集合不支持通過索引訪問指定元素

3.2.4 字典(dict)

        Python內置字典,在其他語言中也稱爲map,使用鍵-值(key-value)存儲,具有極快的查找速度,其格式是用大括號{}括起來key和value用冒號:進行對應。

3.2.4.1 創建字典

dict_1 = {'a':100,'b':99,'c':98}
dict_1

        字典本身是無序的,可以通過方法keys和values取字典鍵值對應中的鍵和值。items可以把字典中每對鍵和值組成一個元組,然後把他們放到一個列表當中返回。

dict_1.keys()#所有的鍵
dict_1.values()#所有的值
dict_1.items()#字典的所有的鍵的值
#遍歷字典的所有的鍵的值
for k,v in dict_1.items():
    print(k,v)

        輸出結果

Out[75]: dict_keys(['a', 'b', 'c'])
Out[76]: dict_values([100, 99, 98])
Out[77]: dict_items([('a', 100), ('b', 99), ('c', 98)])    
Out[78]:
a 100
b 99
c 98    

3.2.4.2 基礎操作

#鍵值查找
dict_1['a']
#更新
dict_1['a'] = 99.5
#添加
dict_1['d'] = 97
#成員判斷
'd' in dict_1
#刪除字典成員
del dict_1['d']    

字典的內建函數

#dict()
names = ['aa','bb','cc','dd']
salaries = [5000,5001,5002,5003]
alnfo = dict(zip(names,salaries))#創建字典
alnfo
#len()
len(alnfo)#計算字典元素的個數

字典方法

        人事部門有兩份人員和工資信息表,第一份是原有信息,第二份是公司中有工資更改人員和新進人員的信息,獲得完整的信息表。

alnfo = {'W':3000,'N':2000,'L':4500}
blnfo = {'W':4000,'N':9999,'K':6000}
alnfo.update(blnfo)
alnfo

        輸出結果

Out[497]: {'K': 6000, 'L': 4500, 'N': 9999, 'W': 4000}

        我們可以看到,W員工的工資更改爲4000,N員工的工資也更改爲9999,同時K是新進來的員工。

        鍵查找值

#鍵值查找的基本操作
stock = {'AXP':75.1,'AT':89}
stock['AAA']

        輸出結果

Traceback (most recent call last):

  File "<ipython-input-499-a83b89b3e161>", line 1, in <module>
    stock['AAA']

KeyError: 'AAA'

        鍵’AAA’不在字典 stock 裏面,所以會產生異常

#字典的get()方法
print(stock.get('AAA'))

        get()方法可以返回指定鍵的值,如果值不存在,它會返回默認值

        輸出結果

None

        我們可以發現通過鍵查找值,第一種方法在鍵不存在的時候,就會發生異常,而第二種就不會,發生異常的時候我們知道程序會中止,所以在使用的時候我們推薦用第二種get()方法。

刪除字典

        有一個字典astock,通過賦值給bstock,接着清空astock的內容,但是我們可以看到bstock裏面的內容還是原來的值,這是因爲astock和bstock原來都指向這個對象{‘AXP’:78.5,‘AT’:85},而astock = {}這條語句導致astock指向了另外的一個對象,所以astock和bstock之間就沒有什麼聯繫,因此bstock還是原來的值。

astock = {'AXP':78.5,'AT':85}
bstock = astock
astock = {}
bstock

        輸出結果

Out[504]: {'AT': 85, 'AXP': 78.5}

clear()方法

        清空原始對象,且對它複製引用的對象也會被清空

astock = {'AXP':78.5,'AT':85}
bstock = astock
astock.clear()
bstock

        輸出結果

Out[508]: {}

字典常用的方法

字典常用的方法
clear() copy() fromkeys() get() items()
keys() pop() setdefault() update() values()

3.2.4.3 注意事項

        定義字典時,鍵不能重複,否則重複的鍵值會替代原先的鍵值。

dict_2 = {'a':10,'b':11,'c':12,'a':14}

dict_2
Out[79]: {'a': 14, 'b': 11, 'c': 12}

        在使用的時候注意,字典不像序列那樣,通過索引確定對象,而是通過確定對象。

astock = {'AXP':78.5,'AT':85}

        比如我們要彈出astock它的第一個成員

  • ×:astock.pop(0)
  • √:astock.pop(‘AXP’)

3.3 Python 的程序控制

3.3.4 循環結構

        循環語句可用於遍歷枚舉一個可迭代對象的所有取值或其元素,每一個被遍歷到的取值或元素執行指定的程序並輸出。這裏可迭代對象指可以被遍歷的對象,比如列表、元組和字典等。
        循環通常用來解決複雜問題的必備結構,在Python中,有for循環和while循環這兩種結構。

3.3.4.1 for 循環

        for 循環比較適合循環次數確定的情況。
        語法:for 加上一個變量 in 可迭代的對象裏面,可迭代就是說它的值是可以遍歷的,在 Python中可迭代對象主要有:字符串、列表、元組、字典和文件。for 循環常常用來遍歷一個數據集內的成員,另外它在列表解析和生成器表達式當中也使用。

        下面是一個 for 循環的例子,i 用於指代一個可迭代對象a的一個元素,for 循環寫好條件後以冒號結束,並換行縮進 ,第二行是針對每次循環執行的語句,這裏是打印列表 a 中的每一個元素。

a = [2,3,4,5,6]
for i in a:
    print(i)

        輸出結果

2
3
4
5
6

        上述操作也可以通過遍歷一個可迭代對象的索引來完成,b 列表一共 5 個元素,range(len(b)) 表示生成 a 的索引序列,這裏打印索引並打印 b 向量索引下的取值。

b = ['h','e','l','l','o']
for i in range(len(b)):
    print(i,b[i])

        輸出結果

0 h
1 e
2 l
3 l
4 o

        range()函數的返回值是一個range對象,也是一個可迭代的對象

for i in range(3,11,2):
    print(i,end=' ')
3 5 7 9 

列表解析的語法

        假設我們要生成一個0到9的整數序列,首先有一對中括號,在中括號裏面有一個表達式,例如 i for 一個可迭代的變量i in 一個可迭代的對象裏面

[i for i in range(5)]#整數序列
Out[252]: [0, 1, 2, 3, 4]

        列表解析後面還可以加一個條件,例如要生成一個1-10之間的奇數序列

[i+1 for i in range(10) if i % 2 ==0]
Out[253]: [1, 3, 5, 7, 9]

生成器表達式的語法

        語法與列表解析類似,只不過它用的是圓括號

(i for i in range(5))
Out[254]: <generator object <genexpr> at 0x0000028F5BADA2B0>

        返回的是一個生成器,而不是創建一個列表,一般在數據量比較大的時候,我們就用生成器表達式;數據量不大的時候,我們就考慮用列表解析。

猜遊戲(允許猜五次)

        程序隨機產生一個0~300間的整數,玩家競猜,允許猜多次,系統給出“猜中”、“太大了”或“太小了”的提示。

#猜遊戲(多次)
from random import randint
x = randint(0,300)
for count in range(0,5):
    print('Please input a number between 0~300:')
    digit = int(input())
    if digit == x:
        print('Bingo')
    elif digit > x:
        print('Too large,please try again')
    else:
        print('Too small,Please try again')

        測試結果

Please input a number between 0~300:

50
Too small,Please try again
Please input a number between 0~300:

199
Too large,please try again
Please input a number between 0~300:

87
Too small,Please try again
Please input a number between 0~300:

100
Too small,Please try again
Please input a number between 0~300:

150
Too small,Please try again

3.3.4.2 while 循環

        下面的例子中,條件表達式 j<10後面加上一個冒號,當條件表達式爲真的時候,反覆執行下面的兩條語句,注意代碼塊需要縮進,跳出循環即不滿足條件的時候,j=10時。

#while
#賦初值
sumA = 0
j = 1
while j <10:
    sumA +=j
    j+=1

        最終sumA的值爲45(0到9的和),j的值爲10。

        while 循環一般會設定一個終止條件,條件會隨着循環的運行而發生變化,while 是在條件不成立的時候停止,因此 while 的作用概括一句話就是:只要…條件成立,就一直做…
        計數器 count 每循環一次自增 1 ,但 count 爲 5 時,while 條件爲假,終止循環。

count = 1
while count <5:
    count +=1
    print(count)

        輸出結果

2
3
4
5

循環中的break、continue語句

        當條件滿足的時候,跳過continue之後的語句。
        所以continue的功能就是停止當前循環重新進入循環,和break不一樣,break是終止整個循環,而continue只是停止當前的一輪循環。

猜數字遊戲(想停就停,非固定次數)

        程序隨機產生一個0~300間的整數,玩家競猜,允許玩家自己控制遊戲次數,如果猜中系統給出提示並退出程序。如果猜錯給出“太大了”或“太小了”的提示,如果不想繼續玩可以退出。

from random import randint
x = randint(0,300)
go = 'y'
while(go == 'y'):
    digit = int(input('Please input a number between 0~300:'))
    if digit == 'x':
        print('Bingo!')
        break
    elif digit > x:
        print('Too large,please try again')
    else:
        print('Too small,please try again')
    print('Input y if you want to continue.')
    go = input()
    print(go)
else:
    print('Goodbye!')

        注意一下:在Python中,else是可以與while進行搭配,循環中else語句,如果循環代碼從break處終止,那麼就跳出循環,不執行後面else語句中的代碼,如果正常結束循環,則會執行else中的代碼

        測試結果

Please input a number between 0~300:50
Too small,please try again
Input y if you want to continue.

y
y

Please input a number between 0~300:200
Too large,please try again
Input y if you want to continue.

n
n
Goodbye!

        判斷一個數是否爲素數。

from math import sqrt
num = int(input('Please enter a number:'))
j = 2
while j <= int(sqrt(num)):
    if num % j == 0:
        print('{:d} is not a prime.'.format(num))
        break
    j += 1
else:
    print('{:d} is a prime.'.format(num))  

        測試結果

Please enter a number:7
7 is a prime.
  1. continue 控制循環

        continue 滿足條件則繼續進行循環,如下代碼是打印 10 以內能夠被 3 整除的整數

count = 0
while count <10:
    count +=1
    if count % 3 == 0:
        print(count)
        continue

        輸出結果

3
6
9
  1. break 控制循環

        break 表示一旦滿足條件則終止循環。

count = 1
while count < 10:
    count += 1
    if count % 3 == 0:
        print(count)
        break

        輸出結果

3

累加的例子

        如果累加的和大於10,我們就break

sum10 = 0
i = 1
while True:
    sum10 += i
    i += 1
    if sum10 > 10:
        break
print('i={},sum={}'.format(i,sum10))

        輸出結果

i=6,sum=15

        當滿足break的條件的時候,跳出當前它所在的一個循環結構。

#輸出2-100之間的素數
from math import sqrt
j=2
while j <= 100:
    i = 2
    k = sqrt(j)
    while i <= k:
        if j%i==0:break
        i = i + 1
    if i > k:
        print(j,end = ' ')
    j += 1

        輸出結果

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 

        用for語句來求出2-100之間的素數

from math import sqrt
for i in range(2,101):
    flag = 1#賦初值
    k = int(sqrt(i))
    for j in range(2,k+1):
        if i%j == 0:
            flag = 0
            break
    if(flag):
        print(i,end=',')

        輸出結果

2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,
  1. pass 語句

        pass語句一般是爲了保持程序的完整性而作爲佔位符使用,例如以下代碼中 pass 沒有任何操作。

count = 0
while count < 10:
    count += 1
    if count %3 == 0:
        pass
    else:
        print(count)

        輸出結果

1
2
4
5
7
8
10

3.3.4.3 表達式

        在Python中,諸如列表、元組、集合和字典都是可迭代對象,Python爲這些對象的遍歷提供了更加簡潔的寫法。例如如下列表對象 x 的遍歷,且每個元素取值除以 10。

x = [1,2,3,4]
[i/10 for i in x]

        輸出結果

[0.1, 0.2, 0.3, 0.4]

        上述[i/10 for i in x] 的寫法稱爲列表表達式,這種寫法比 for 循環更加簡便,此外對於元組對象、集合對象、字典對象,這種寫法依舊適用,最終產生一個列表對象。

#元組
x1 = (1,2,3)
[i/10 for i in x1]
#集合
x2 = {2,4,5}
[i/10 for i in x2]
#字典
x3 = {'a':3,'b':4,'c':5}
#輸出字典的鍵
[i for i in x3.keys()]
#輸出字典的值
[i for i in x3.values()]

        此外,Python 還支持集合表達式與字典表達式用於創建集合、字典,例如如下形式創建集合。

#創建集合
{i for i in [1,1,1,2,2]}
Out[110]: {1, 2}

        字典表達式可以用如下方式創建

#字典表達式
{key:value for key,value in [('a',1),('b',2),('c',3)]}
Out[111]: {'a': 1, 'b': 2, 'c': 3}

3.3.5 選擇結構

if 語句

        if 後面是一個條件表達式,加一個**冒號**來表示;條件表達式就是由比較運算符、成員運算符 和邏輯運算符加上一些運算對象構成的式子,比如 a > b等,後面接着代碼塊,當表達式的條件爲真的時候,代碼塊纔會執行,並且代碼塊**必須縮進**,通常縮進4個空格。
length1 = 3
length2 = 3
if length1 == length2:
    print("The Square's area is",length1*length2)

        輸出結果

The Square's area is 9

        下面介紹當條件表達式爲真的時候的執行語句,以及條件表達式爲假的時候執行另外的語句,也就是用到 else 語句。

else 語句

        else 語句的語法是與 **if 對齊**,後面同樣有**冒號**,else 其後的代碼塊就是在表達式爲假的時候,執行的部分語句,else 後面的代碼塊同樣**要縮進**。
length1 = int(input("The First Side:"))
length2 = int(input("The Second Side:"))
if length1 == length2:
    print("The Square's Area is",length1*length2)
else:
    print("The Rectangle's Area is",length1*length2)

        輸出結果

The First Side:8

The Second Side:9
The Rectangle's Area is 72

elif 語句

        語法規則

        如果滿足第一個條件,就執行第一個條件的代碼塊;如果第一個條件不滿足,但是滿足第二個條件,那麼就執行第二個條件的代碼塊;以此類推,直到以上所有的條件都不滿足,就執行else語句的代碼塊。

        以下舉出一個小例子:輸入形狀的編號,最後會輸出這個編號對應的形狀。主要是讓大家熟悉語法規則以及對齊的地方和縮進的地方。

k = input('input the index of shape:')
if k == '1':
    print(circle)
elif k == '2':
    print('oval')
elif k == '3':
    print('retangle')
elif k == '4':
    print('triangle')
else:
    print('you input the invalid number')

        我們可以看到,這裏 if 與 elif 、else 都是對齊的;其中 if 、elif 和 else 下面的代碼塊都是要用縮進的方式。

        輸出的結果

input the index of shape:4
triangle

        假設在一個條件裏面我們還要再來分一種或者兩種以上的情況,,比如上面的例子中 k 等於3的情況下,我們又要分它有幾種情況,這就需要下面講的條件嵌套。

條件嵌套

        條件裏面套條件就是條件嵌套

        在 k 等於 3 的時候,我們又再分兩種情況。

k = input('input the index of shape:')
if k == '1':
    print(circle)
elif k == '2':
    print('oval')
#條件嵌套    
elif k == '3':
    length1 = int(input("the first side:"))
    length2 = int(input("the first side:"))
    if length1 == length2:
        print("the square's area is",length1*length2)
    else:
        print("the rectangle's area is",length1*length2)
elif k == '4':
    print('triangle')
else:
    print('you input the invalid number')

        輸出結果

input the index of shape:3

the first side:8

the first side:2
the rectangle's area is 16

猜數字遊戲

        程序隨機產生一個0~300間的整數,玩家競猜,系統給出“猜中”、“太大了”或“太小了”的提示。
        首先需要用到random 模塊的 randint() 這個函數,這個函數的功能就是你後面給出的參數之間的一個數的範圍,就是它產生的結果。比如下面的例子中 x 就會獲得在0~300間隨機產生的一個數,這是系統給出的 x 的值。我們使用內建函數中的 input()函數來獲取用戶的輸入。

#猜數字的遊戲
from random import randint
x = randint(0,300)
print(x)
digit = int(input("Please input a number between 0~300:"))
if digit == x:
    print("x is",x,"  Bingo!")
elif digit > x:
    print("x is",x,"  The number is too large!")
else:
    print("x is",x,"  The number is too small!")

        輸出結果

19
Please input a number between 0~300:50
x is 19   The number is too large!

條件編程練習

        練習題1:編寫一個輸入分數,輸出分數等級的程序,具體爲

Score Grade

90~100 A

70~89 B

60~69 C

0~59 D

others Invalid score

        請添加必要的輸入輸出語句,儘量讓程序友好。

score = eval(input("enter the score: "))
if 0<= score <= 100:
    if 90 <= score <= 100:
        grade = "A"
    elif score >= 70:
        grade = "B"
    elif score >= 60:
        grade = "C"
    elif score >= 0:
        grade = "D"
    #字符串的format()方法中參數輸出的位置和格式等由其前面字符串中的{}位置和格式(d表示十進制)決定   
    print("The grade of {} is {}.".format(score,grade))
else:
    print("Invalid score")

        練習題1的輸出結果

enter the score: 96
The grade of 96 is A.

enter the score: 72
The grade of 72 is B.

enter the score: 65
The grade of 65 is C.

enter the score: 58
The grade of 58 is D.

enter the score: 101
Invalid score

        練習題2:編寫程序,從鍵盤輸入一個二元一次方程 ax^2+bx+c=0 的三個參數a、b、c(均爲整數),求此方程的實根。如果方程有實根,則輸出實根(保留一位小數),如果沒有實根則輸出沒有實根的信息。

from math import sqrt
a,b,c = eval(input())
t = b*b-4*a*c
if t>0:
    x1 = (-b+sqrt(t))/(2*a)
    x2 = (-b-sqrt(t))/(2*a)
    print('x1 = {:.1f},x2 = {:.1f}'.format(x1,x2))
elif t==0:
    x = -b/(2*a)
    print('x = {:.1f}'.format(x))
else:
    print("no real solution")

2,2,3
no real solution

3.4 Python 的函數與模塊

3.4.1 Python 的函數

函數(一)

  • 函數可以看成類似成數學中的函數

  • 完成一個特定功能的一段代碼

  • 類型函數type()

函數(二)

  • 內建函數:不需要另外導入的一些函數

  • str() 和 type() 適用於所有標準類型

數值型內建函數
函數 功能 函數 功能
abs() 絕對值函數 bool() 將變量轉換爲布爾值
oct() 將一個整數轉換成八進制字符串 round() 四捨五入
int() 將其他數據類型轉換爲整型 hex() 將十進制整數轉換爲十六進制,以字符串形式語法
divmod() divmod(a,b)接受兩個數值(非複數),返回兩個數值的相除得到的商,和餘數組成的元組 ord() 返回對應字符的ascii碼
pow() pow(x,y[,z])兩個必需參數和一個可選參數,結果返回x**y,如果可選參數有傳入值,則返回冪乘之後再對z取模(相當於pow(x,y)%z float() 將其他數據類型轉換爲浮點型
chr() range(256)內的(就是0-255)整數做參數,返回一個對應的字符 complex() 創建複數或轉換爲複數
實用函數
函數 功能 函數 功能
dir() 列出對象的所有屬性 input() 獲取用戶輸入的信息
help() 返回對象的幫助信息 open() 打開一個文件,返回一個文件讀寫對象,然後可以對問卷進行相應讀寫操作
len() 返回對象的長度 range() 創建一個整數列表

        Python還有很多的內建函數,感興趣的小夥伴可以在Python中輸入下面一行代碼可以查看從abs開始到zip結束的一些內建函數。不明白的可以使用help()查看幫助。

#查看內建函數
dir(__builtins__)

小練習:編寫一個輸入輸出的程序

        編程實現輸入姓、名的提示語並接受用戶輸入,並單獨顯示姓、名和全名。

surname = input('Input your surname: ')
firstname = input('Input your firstname: ')
print('Your surname is:',surname)
print('Your firstname is:',firstname)
print('Your full name is:',surname,firstname)

        輸出結果

Input your surname: Ma

Input your firstname: Zhongjun
Your surname is: Ma
Your firstname is: Zhongjun
Your full name is: Ma Zhongjun

1. 自定義函數

        函數在調用前必須先定義,可以是系統事先定義好的內置函數,也可以是用戶自定義函數。
        def 後面是函數名,再加一對括號,中間是一個參數,而這個參數是可選的,後面再跟一個冒號,回車之後的是函數體的代碼塊。下面的例子中 ‘apply operation + to argument’ 這是一個字符串,這個在Python當中被稱爲DocString,一個文檔字符串,它是程序的註釋,放在最前面,通常寫函數的時候需要寫DocString,當我們要看其他函數的註釋的時候,使用print後面加一個“函數名.__doc__"(doc左右兩邊是兩個下劃線),進行查看。

def add(x):
    'apply operation + to argument'
    return (x+x)

        測試結果

add('嘻哈')
Out[270]: '嘻哈嘻哈'
    
add()
Traceback (most recent call last):

  File "<ipython-input-271-d5d29de3ed94>", line 1, in <module>
    add()

TypeError: add() missing 1 required positional argument: 'x'   
自定義函數的調用
  • 函數名加上函數運算符,一對小括號
    • 括號之間是所有可選的參數
    • 即使沒有參數,小括號也不能省略

        如果本身帶有參數,調用的時候不寫參數則會報錯

        自定義函數:輸出1-100之間的素數。

from math import sqrt
#判斷素數的自定義函數部分
def isprime(x):
    if x == 1:
        return False
    k = int(sqrt(x))
    for j in range(2,k+1):
        if x % j == 0:
            return False
    return True

for i in range(2,101):
    if isprime(i):
        print(i,end=' ')

        輸出結果

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 

默認參數(一)

        函數的參數可以有一個默認值,如果提供有默認值,在函數定義中,默認參數以賦值語句的形式提供。

f()#使用默認參數
x is a correct word
Ok

默認參數(二)

        默認參數的值可以改變

f(False)#更改默認參數值
Ok

默認參數(三)

        默認參數一般需要放置在參數列表的最後

def f(y = True,x):
    '''x and y both correct words or not'''
    if y:
        print(x,' and y both correct.')
    print(x,' is Ok.')

        測試結果

  File "<ipython-input-277-242bbda05af2>", line 1
    def f(y = True,x):
         ^
SyntaxError: non-default argument follows default argument

        函數執行之後會報錯,比如f(False)的時候,實參False是傳遞給形參y還是x就沒法確定,因此在Python當中就要求默認參數要放到參數列表的最後。

關鍵字參數

  • 關鍵字參數是讓調用者通過參數名區分參數。
  • 允許改變參數列表中的參數順序
def key(x,y):
    if y:
        print(x,' and y both correct')
    print(x,' is OK')

        測試1

key(68,False)
68  is OK

        測試1中函數的參數沒有特殊的形式,通常稱作爲位置參數。如果搞錯位置,結果往往會搞錯。
        如果有多個位置參數的話,參數的順序經常會記錯,記亂。爲了解決這樣的情況,我們可以使用參數的名字進行調用。這就是我們要說的關鍵字參數。
        我們在調用的時候可以直接把參數名寫下來,這樣我們就不用考慮參數的順序。

        測試2

key(y = False,x = 68)
68  is OK

        測試3

key(y = False,68)

        輸出結果

  File "<ipython-input-281-f7dba33b4ee7>", line 1
    key(y = False,68)
                 ^
SyntaxError: positional argument follows keyword argument

        測試4

key(x = 68,False)

        輸出結果


  File "<ipython-input-282-8d72fe9ed389>", line 1
    key(x = 68,False)
              ^
SyntaxError: positional argument follows keyword argument

        出錯的原因是非關鍵字參數跟在關鍵字參數的後面,也就是意味着一旦你使用了關鍵字參數,後面就必須是關鍵字參數。進一步來說:關鍵字參數的使用已經把整個的參數表打亂了,所以一旦它開始,我們後面必須把所有的參數名都寫下來。

傳遞函數

  • 函數可以像參數一樣傳遞給另外一個函數
def add2(x):
    return (x+x)

def self2(f,y):
    print(f(y))
self2(add2,2.5)

        測試結果

5.0

        f(y)中輸送一個函數名給self2函數的第一個參數,把函數名當作一個普通參數一樣傳給另外一個函數,稱爲傳遞函數,self2()函數的調用過程是:print(add2(2.2))

2. lambda 函數

  • 匿名函數
  • 沒有return
  • 不需要有定義函數的過程
def sumx(x):
    return (x+x)
sumx(5)
#lambda
r = lambda x:x+x
r(5)

        輸出的結果都是10。

        在lambda函數中是把右邊的運算結果賦給變量 r ,lambda函數不需要取函數名,但是最後調用的時候還是需要一個變量名。

        lambda函數的編程類似與filter()、reduce()等這些函數結合起來還是非常方便。

g = lambda x:x+1
g(2)

        該函數相當於如下自定義函數

def g(x):
    return (x+1)
g(2)

函數的參數

        函數的參數可以分爲形式參數與實際參數,形式參數作用與函數的內部,其不是一個實際存在的變量,當接受一個具體值時(實際參數),複製將具體值傳遞到函數內部進行運算。實際參數即具體值,通過形式參數傳遞到函數內部參與運算並輸出結果。比如下面自定義的函數avg,形式參數爲x,實際參數爲一個列表。

def avg(x):
    mean_x = sum(x)/len(x)
    return(mean_x)
avg([23,34,12,34,56,23])

        輸出結果

Out[113]: 30.333333333333332

        函數參數的傳遞有兩種方式:按位置和按關鍵字。當函數的形式參數過多時,一般採用按關鍵字傳遞的方式,通過形式參數名 = 實際參數的方式傳遞參數,如下所示,函數 age 有四個參數,可以通過指定名稱的方式使用,也可以按照順序進行匹配。

def age(a,b,c,d):
    print(a)
    print(b)
    print(c)
    print(d)
#關鍵字指定名稱
age(a = 'young',c = 'median',b = 'teenager', d = 'old')
#按位置順序匹配
age('young', 'teenager', 'median',  'old')

        輸出結果

young
teenager
median
old

        函數的參數中,亦可以指定形式參數的默認值,此時該參數稱爲可選參數。

3.4.2 Python 的模塊

模塊(一)

非內建函數如何使用?

        floor()是向下取整函數,如果我們直接使用這個函數,系統會報錯,錯誤信息會提示:name ‘floor’ 沒有定義,這是因爲floor()並不是內建函數,它屬於數學庫函數,它包含在math這個模塊裏面。

floor(5.9)

        報錯信息

Traceback (most recent call last):

  File "<ipython-input-198-6153bec22d88>", line 1, in <module>
    floor(5.9)

NameError: name 'floor' is not defined

        非內建函數,我們在使用時首先要把它的模塊 import 進來,也就是導入,因此,如果要使用floor()這個函數,我們要把它的模塊通過"import math" 這樣的方式進行導入。

from math import *
floor(36.8)
floor(-35.9)

        輸出結果

36
-36

模塊(二)

  • 一個完整的Python文件即是一個模塊

    • 文件:物理上的組織方式 math.py
    • 模塊:邏輯上的組織方式 math
  • Python通常用“import 模塊"的方式將現成模塊中的函數、類等重用到其他代碼中

import math
math.sqrt(9)

        導入成功之後,可以使用裏面的一些變量或者函數

模塊(三)導入多個模塊

        模塊裏導入指定的模塊屬性,也就是把指定名稱導入到當前作用域。

import ModuleName
#導入多個模塊
import ModuleName1,ModuleName2,...#import 加上多個模塊名,用逗號分隔
#在模塊裏面導入指定的模塊屬性
from Module1 import ModuleElement#from 加上模塊名,接着import 加上後面的模塊屬性

        區別:這兩者之間,一個是把模塊中的所有函數和類都導入;另外一個是導入了其中部分的函數或者類。


3.4.3 Python 的包

  • 一個有層次的文件目錄結構
  • 定義了一個由模塊和子包組成的Python應用程序執行環境

        下面的例子中,AAA是最頂層的包,CCC、DDD、EEE這樣的一些是它的子包,而像c1、c2就是它的模塊。如果我們要調用模塊c1中的函數func1(),我們可以通過下面兩種方式進行調用。

(1)import 包名.子包名.模塊名

#第一種方式
import AAA.CCC.c1
AAA.CCC.c1.func1(123)

(2)from import

        直接把 c1 模塊中的函數 func1()導入

#第二種方式
from AAA.CCC.c1 import func1
func1(123)

3.5 Pandas 讀取結構化數據

3.5.1 讀取數據

import os
import pandas as pd
os.chdir("G:\python學習")#設置工作路徑
sample = pd.read_csv('data/sample.csv')
sample

        輸出結果

Out[134]: 
  name  scores
0   小明      79
1   曉東      85
2   小白      95

        按照慣例,Pandas 會以 pd 作爲別名, pd.read_csv 函數可以實現讀取 指定路徑下的 csv 數據,然後返回一個 DataFrame 對象。
        此外,read_csv 函數有很多參數可以設置,這裏列出常用參數。

讀取指定行和指定列

        使用參數usecol和 nrows 讀取指定的列和前 n 行,這樣可以加快數據讀取速度。

#讀取name一列,僅讀取前一行
sample_subset = pd.read_csv('data/sample.csv',\
                     usecols = ['name'],\
                     nrows = 1)
sample_subset

        輸出結果

Out[136]: 
  name
0   小明

第四章 描述性統計分析與繪圖

4.1 描述性統計進行數據探索

4.1.3 連續變量的分佈與集中

        不同的分佈情況下,均值、中位數、衆數差異。

        sndHsPr數據集是一份二手房屋價格的數據

變量名 含義 變量名 含義 變量名 含義
dist 城區(拼音) roomnum 臥室數 halls 廳數
floor 樓層 subway 是否地鐵房 school 是否學區房
AREA 房屋面積 price 單位面積房價 district 城區(中文,衍生)
#設置工作區間
import pandas as pd
import os
os.chdir(r'F:\Python_book\4Describe')
snd = pd.read_csv("sndHsPr.csv")

        求出price的均值、中位數、方差與標準差(其中agg()函數的功能是歸併若干個函數的結果)

#求均值、中位數、方差與標準差
snd.price.agg(['mean','median','sum','var','std'])

        輸出結果

Out[291]: 
mean      6.115181e+04
median    5.747300e+04
sum       9.912709e+08
var       4.969938e+08
std       2.229336e+04
Name: price, dtype: float64

        求出price的四分位數

#求四分位數
snd.price.quantile([0.25,0.5,0.75])

        輸出結果

Out[292]: 
0.25    42812.25
0.50    57473.00
0.75    76099.75
Name: price, dtype: float64

        查看price變量的分佈,這裏的bins參數表示直方圖下的區間個數

#直方圖
snd.price.hist(bins=20)#bins參數表示直方圖下的區間個數

        可以看出單位面積房價略有一些右偏。

4.1.4 連續變量的離散程度

        只描述數據的集中水平是不夠的,因爲這樣會忽視數據的差異情況。這裏需要引入另一種指標或統計量用以描述數據的離散程度。

  • 極差:即變量的最大值與最小值之差
  • 方差與標準差
snd.price.max()-snd.price.min()#極差
snd.price.agg(['var','std'])#var:方差;std:標準差

4.1.5 數據分佈的對稱與高矮

        偏度即數據分佈的偏斜程度;峯度即數據分佈的高矮程度
        標準正態分佈(均值爲0,標準差爲1)的變量,其偏度和峯度都爲0。

  • 偏度大小以及正負取決於分佈偏移的方向及程度

    • 左偏分佈:偏度小於0
    • 對稱分佈:偏度等於0
    • 右偏分佈:偏度大於0
  • 峯度大小以及正負取決於分佈較標準正態分佈的高矮

    • 峯度大於0,說明變量的分佈相比較標準正態分佈更加密集
    • 峯度小於0,說明變量的分佈相比較標準正態分佈更加分散

        在Pandas 中,提供了skew 和 kurtosis 方法實現偏度和峯度。

        例如:模擬100個標準正態分佈的隨機數?

normal = pd.Series(np.random.randn(1000),name = 'normal')
normal.skew()#偏度
normal.kurtosis()#峯度

4.2 製作報表與統計製圖

        獲取每個城區的頻次

snd.district.value_counts()#value_counts()函數獲取每城區出現的頻次

        輸出結果

Out[307]: 
豐臺區     2947
海淀區     2919
朝陽區     2864
東城區     2783
西城區     2750
石景山區    1947
Name: district, dtype: int64

        "kind = "爲圖表類型,柱形圖爲 bar, 餅形圖爲 pie。

#條形圖與餅圖  
#如遇中文顯示問題可加入以下代碼
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示爲方塊的問題
snd.district.value_counts().plot(kind = 'bar')
#snd.district.value_counts().plot(kind = 'pie')  

        表分析:分析兩個分類變量的聯合分佈情況,提供每個單元格的頻次、百分比和邊沿情況。表分析(也稱爲交叉表)用的函數爲pd.crosstab()

pd.crosstab(snd.subway,snd.school)#分析subway與school之間的關係

        輸出結果

Out[312]: 
school     0     1
subway            
0       2378   413
1       8919  4500

        可以使用標準化的堆疊柱形圖對錶分析的結果進行展示。其步驟是先獲取交叉表的結果,然後使用div(sub_sch.sum(1),axis = 0)函數計算交叉表的行百分比,然後做柱形圖。

sub_sch = pd.crosstab(snd.subway,snd.school)
sub_sch = sub_sch.div(sub_sch.sum(1),axis=0)
sub_sch.plot(kind='bar',stacked=True)

        輸出結果

        還可以使用stack2dim()函數製作,snd爲當前所使用的數據框,i、j爲兩個分類變量的變量名稱,要求帶引號。

from stack2dim import *

stack2dim(snd, i="subway", j="school")
  1. 彙總統計量:按照某個分類變量分組,對連續變量進行描述性統計。
snd.price.groupby(snd.district).agg(['mean','max','min'])

        輸出結果

Out[319]: 
                  mean     max    min
district                             
東城區       71883.595041  149254  20089
豐臺區       42500.904309   87838  18348
朝陽區       52800.624651  124800  23011
海淀區       68757.602261  135105  25568
石景山區      40286.889574  100000  18854
西城區       85674.778545  149871  21918

        盒須圖也稱爲箱線圖,能夠提供某變量分佈以及異常值的信息。

import seaborn as sns
sns.boxplot(data = snd,x='district', y='price')

箱線圖提供了識別異常值的一個標準:異常值通常被定義爲大於Q3+1.5IQR或小於Q1-1.5IQR。

  • Q1:下四分位數,表示全部觀測值中有四分之一的數據取值比它小
  • Q3:上四分位數,表示全部觀測值中有四分之一的數據取值比它大
  • IQR: 稱爲四分位數間距,是上四分位數與下四分位數的差,這個範圍代表了數據中50%的數據
  • 中位數:代表變量中位數中總體分佈中的位置

柱形圖

        使用每個城區單位面積房屋均價作爲柱高。

snd.price.groupby(snd.district).mean().plot(kind='bar')

        如果我們關心的是每個城區平均房屋價格的排序情況,則使用條形圖。

#每個城區平均房屋價格的排序情況
snd.price.groupby(snd.district).mean().sort_values(ascending = True).plot(kind='barh')

        條形圖與柱形圖非常相似,按照因子排序展現數據的時候常用柱狀圖(例如使用時間作爲因子);如果因子是無序的,同時按照數據大小進行展示時常用條形圖(例如展示不同地區的房價,房價從高到低排序)。

散點圖

        使用房屋使用面積和單位面積房價這兩個變量之間的關係。

snd.plot.scatter(x = 'AREA', y = 'price')
        可以發現兩者沒有明顯的關係。

第五章 數據整合和數據清洗

5.1 數據整合

5.1.1 行列操作

        Pandas數據框可以方便地選擇指定列、指定行,例如創建一個數據框,產生一組4行5列的正態分佈隨機數,columns參數表示定義相應的列名。

sample = pd.DataFrame(np.random.randn(4,5),columns = ['First','Second','Third','Fourth','Fifth'])
sample

        輸出結果

Out[333]: 
      First    Second     Third    Fourth     Fifth
0 -0.867004 -1.161871 -1.105819  0.943504 -0.552097
1 -0.363334  0.075978 -1.195192  1.119592  1.282178
2 -1.900368  0.239162  0.275959 -0.076309  0.814495
3 -0.707469  1.035810  1.171252  0.299344 -1.349734
  1. 選擇單列
  • 直接以列名選擇列
  • ix、iloc、loc方法
    • iloc方法只能使用數值作爲索引選擇行、列
    • loc方法在選擇列時只能使用字符索引
    • ix方法可以使用兩種索引

        選擇第一列

sample['First']#以列名進行訪問
sample.iloc[:,0]#iloc方法
sample.loc[:,'First']#loc方法
sample.ix[:,'First']#ix方法

        輸出結果

Out[336]: 
0   -0.867004
1   -0.363334
2   -1.900368
3   -0.707469
Name: First, dtype: float64

        注意,選擇單列時,返回的是Pandas序列結構的類,也可以使用以下方法在選擇單列時返回Pandas數據框類。

sample[['First']]
  1. 選擇多行和多列

        數據框選擇行時,可以直接使用行索引進行選擇。


        注意:ix或loc方法選擇時,行索引是前後都包括的,這與列表索引不太一樣;

若習慣使用列表索引那樣前包後不包,可以使用iloc


sample.ix[0:3,1:4]#行索引:前後都包括,四行三列

        輸出結果

Out[337]: 
     Second     Third    Fourth
0 -1.161871 -1.105819  0.943504
1  0.075978 -1.195192  1.119592
2  0.239162  0.275959 -0.076309
3  1.035810  1.171252  0.299344
sample.iloc[0:3,1:4]#行索引:前包括,後不包括,三行三列

        輸出結果

Out[338]: 
     Second     Third    Fourth
0 -1.161871 -1.105819  0.943504
1  0.075978 -1.195192  1.119592
2  0.239162  0.275959 -0.076309
  1. 創建、刪除

        創建新列有兩種方法

  • 直接通過賦值
  • assign方法

        新建列’new_1’,取值由原先兩列計算得出

sample['new_1'] = sample['First'] - sample['Second']
sample

        輸出結果

Out[340]: 
      First    Second     Third    Fourth     Fifth     new_1
0 -0.867004 -1.161871 -1.105819  0.943504 -0.552097  0.294867
1 -0.363334  0.075978 -1.195192  1.119592  1.282178 -0.439313
2 -1.900368  0.239162  0.275959 -0.076309  0.814495 -2.139530
3 -0.707469  1.035810  1.171252  0.299344 -1.349734 -1.743279

        新建列’new_2’和’new_3’,使用assign方法來完成。

sample.assign(new_2 = list('1234'),new_3 = list('4567'))
sample.shape

        輸出結果

Out[342]: 
      First    Second     Third    Fourth     Fifth     new_1 new_2 new_3
0 -0.867004 -1.161871 -1.105819  0.943504 -0.552097  0.294867     1     4
1 -0.363334  0.075978 -1.195192  1.119592  1.282178 -0.439313     2     5
2 -1.900368  0.239162  0.275959 -0.076309  0.814495 -2.139530     3     6
3 -0.707469  1.035810  1.171252  0.299344 -1.349734 -1.743279     4     7


Out[343]: (4, 6)
new_sample = sample.assign(new_2 = list('1234'),new_3 = list('4567'))
new_sample.shape

        輸出結果

Out[346]: (4, 8)

        注意:assign這種方式生成的新變量不會保留在原始表中,需要賦值給新表纔可以。


        刪除列時,可以使用數據框的方法drop。

sample.drop('First',axis=1)#刪除一列:'First'
sample.drop(['Second','Third'],axis=1)#刪除多列:'Second'、'Third'

5.1.2 條件查詢

        首先生成示例數據框

record = pd.DataFrame({'name':['Bob','Lindy','Mark','Miki','Sully','Rose'],
                       'score':['98,78,87,77,65,67'],
                       'group':[1,1,1,2,1,2]})
  1. 單條件

        涉及條件查詢時,一般會使用一些比較運算符,例如”>“ ”==“ ”<=“等,比較運算符產生布爾類型的索引可用於條件查詢,例如 record 數據框查詢score大於70分的人,首先生成bool索引。

record.score >70

        輸出結果

Out[353]: 
0     True
1     True
2     True
3     True
4    False
5    False
Name: score, dtype: bool

        再通過指定索引進行條件查詢,返回bool值爲True的數據。

record[record.score >70]

        輸出結果

Out[354]: 
   group   name  score
0      1    Bob     98
1      1  Lindy     78
2      1   Mark     87
3      2   Miki     77
  1. 多條件

        多條件查詢時,涉及bool運算符,Pandas支持以下bool運算符

bool運算符 意義
&
~
|
record [(record.score >70) & (record.group ==1)]#&:與
record [~(record.group == 2)]#~:非
record [(record.group == 2 ) | (record.group ==1)]#|:或
  1. query

        Pandas數據框提供了方法query,可以完成指定的條件查詢。

#query
record.query('score > 70')
record.query('(score >70) & (group ==1)')
record.query('~(group == 2)')
record.query('(group == 2 ) | (group ==1)')
  1. 其他

        Pandas還提供了一些有用的方法可以更加簡便地完成查詢任務。

方法 示例 對象 解釋
between Df [ Df. col. between (10,20) ] pandas.Series col 在10到20之間的記錄
isin Df [ Df. col. isin (10,20) ] pandas.Series col 等於10或20的記錄
str.contains Df [ Df. col. str.contains ( '[M] + ’ ) ] pandas.Series col匹配以M開頭的記錄

        between方法類似與SQL中between and,例如查record中分數在70到80之間的記錄,這裏70與80的邊界是包含在內的,若不希望包含在內可以將inclusive參數設定爲False。

#between
record[record['score'].between(70,80,inclusive = True)]

        對於字符串來說,可以使用isin方法進行查詢,例如篩選姓名爲’Bob’ 'Lindy’的人的記錄

#isin
record[record['name'].isin(['Bob','Lindy'])]

        此外,還可以使用str.contains來進行正則表達式匹配進行查詢,例如查詢姓名以M開頭的人的所有記錄。

#str.contains
record[record['name'].str.contains('[M]+')]

5.1.3 橫向連接

        Pandas數據框提供了merge方法以完成各種表的橫向連接操作,這種連接操作與SQL語句的連接操作是類似的,包括內連接、外連接。

  1. 內連接

        內連接(inner join):查找結果只包括兩張表中匹配的觀測。

        首先創建示例的兩個數據框

df1 = pd.DataFrame({'id':[1,2,3],'col1':['a','b','c']})
df2 = pd.DataFrame({'id':[3,5],'col2':['d','e']})

        內連接使用merge函數,根據公共字段保留兩表共有的信息,how='inner’參數表示使用內連接,on表示兩表連接的公共字段,若公共字段在兩表名稱不一致時,可以通過left_on和right_on指定。

df1.merge(df2,how='inner',on='id')
df1.merge(df2,how='inner',left_on='id',right_on='id')

        輸出結果

Out[373]: 
  col1  id col2
0    c   3    d
  1. 外連接

        外連接(outer join)包括左連接、右連接和全連接三種連接。

  • 左連接通過公共字段,保留左表的全部信息,右表在左表缺失的信息會以NaN補全;
  • 右連接通過公共字段,保留右表的全部信息,左表在右表缺失的信息會以NaN補全;
  • 全連接通過公共字段,保留兩表的全部信息,兩表互相缺失的信息會以NaN補全。
df1.merge(df2,how='left',on='id')#左連接
df1.merge(df2,how='right',on='id')#右連接
df1.merge(df2,how='outer',on='id')#全連接

        輸出結果

Out[374]: 
  col1  id col2
0    a   1  NaN
1    b   2  NaN
2    c   3    d

Out[375]: 
  col1  id col2
0    c   3    d
1  NaN   5    e

Out[376]: 
  col1  id col2
0    a   1  NaN
1    b   2  NaN
2    c   3    d
3  NaN   5    e
  1. 行索引連接

        除了類SQL連接外,Pandas也提供了直接按照行索引連接,使用pd.concat函數或數據框的join方法。

        示例

df11 = pd.DataFrame({'id1':[1,2,3],'col1':['a','b','c']},index=[1,2,3])
df12 = pd.DataFrame({'id2':[1,2,3],'col2':['aa','bb','cc']},index=[1,3,2])

        上述兩表中,df11索引爲1、2、3,df12行索引爲1、3、2,按照索引連接後,索引行會一一對應。pd.concat可以完成橫向和縱向合併,這可以通過參數”axis=“來控制,當參數”axis=1“時表示進行橫向合併。

pd.concat([df11,df12],axis=1)#axis=1表示橫向合併
df11.join(df12)

        輸出結果

Out[389]: 
  col1  id1 col2  id2
1    a    1   aa    1
2    b    2   cc    3
3    c    3   bb    2

5.1.4 縱向合併

        某公司四個季度的銷售數據分散於四張表上,四張表字段名與含義完全相同,如果彙總全年的數據,按摩需要拼接四張表,此時涉及數據的縱向合併。

df1 = pd.DataFrame({'id':[10,11,12,13,14],'col':['A','B','C','D','E']})
df2 = pd.DataFrame({'id':[15,16,17],'col':['F','G','H']})

        ignore_index = True 表示忽略 df1 和 df2 的原先索引,合併並重新排序索引。

pd.concat([df1,df2],ignore_index = True,axis=0)

        輸出結果

Out[395]: 
  col  id
0   A  10
1   B  11
2   C  12
3   D  13
4   E  14
5   F  15
6   G  16
7   H  17

        注意到這種縱向連接是不去除完全重複的行的,若希望縱向連接並去除重複值,可直接調用數據框的 drop_duplicates 方法,類似於 SQL 中的 UNION 操作。

pd.concat([df1,df2],ignore_index = True,axis=0).drop_duplicates()

        此外,在進行縱向合併時,若連接的表的列名或列個數不一致時,不一致的位置會產生缺失值。

        如下所示,首先將 df1 的列 col 重新命名爲 new_col

df3 = df1.rename(columns = {'col':'new_col'})

        此時再進行縱向合併,不一致處填補爲NaN。

pd.concat([df3,df2],ignore_index = True,axis=0).drop_duplicates()

輸出結果

Out[398]: 
   col  id new_col
0  NaN  10       A
1  NaN  11       B
2  NaN  12       C
3  NaN  13       D
4  NaN  14       E
5    F  15     NaN
6    G  16     NaN
7    H  17     NaN

5.1.5 排序

        在很多分析任務中,需要按照某個或某些指標對數據進行排序。Pandas在排序時,根據排序的對象不同可細分爲以下三種:

  • sort_value:對值進行排序;
  • sort_index:對索引進行排序;
  • sortlevel:對多維索的不同級別 level 進行排序。

        最常見的是按照數值進行排序。

        示例

sample = pd.DataFrame({'name':['Bob','Lindy','Mark','Miki','Sully','Rose'],
                      'score':[98,78,87,77,77,np.nan],
                      'group':[1,1,2,2,3,3]})

        第一個參數表示排序的依據列,此處設爲 score ;ascending = False 代表降序排序,設定爲 True 時表示升序排序(默認);na_position = ‘last’ 表示缺失值數據排序在數據的最後位置(默認),該參數還可以設定爲 ‘first’ 表示缺失值排序在數據的最前面。

#默認爲升序排序,將缺失值數據排序在數據的最後位置(默認),還可以設置爲'first'在最前面
sample.sort_values('score',ascending = False,na_position = 'last') 

        當然,排序的依據變量也可以是多個列,例如按照班級(group)、成績(score)升序。

sample.sort_values(['group','score'])

5.1.6 分組彙總

        公司銷售數據分析中,希望按照銷售區域找到最高銷售量記錄,這就涉及分組彙總,即 SQL 中的 group by 語句。分組彙總操作中,會涉及分組變量、度量變量和彙總統計量。Pandas 提供了 groupby 方法進行分組彙總。

        示例:

變量說明
變量 含義
chinese 語文
class 班級
grade 年級
math 數學
name 姓名

        讀取數據

os.chdir(r'F:\Python_book\5Preprocessing')
st_data = pd.read_csv('sample.csv',encoding = 'gbk')
st_data.head(6)

        輸出結果

Out[408]: 
   chinese  class  grade  math   name
0       88      1      1  98.0    Bob
1       78      1      1  78.0  Lindy
2       86      1      1  87.0   Mark
3       56      2      2  77.0   Miki
4       77      1      2  77.0  Sully
5       54      2      2   NaN   Rose

        年級(grade)爲分組變量,數學成績(math)爲度量變量,現需要查詢年級1和年級2數學最高成績。 groupby 後參數 ‘grade’ 表示數據中的分組變量,max 表示彙總統計量爲 max(最大)。

st_data.groupby('grade')[['math']].max()

        輸出結果

Out[409]: 
       math
grade      
1      98.0
2      77.0
  1. 分組變量

        在進行分組彙總時,分組變量可以有多個,例如,按照 “年級” 、“班級” 順序對數學成績查詢平均值,此時在 groupby 後接多個分組變量,以列表形式寫出。

st_data.groupby(['grade','class'])[['math']].mean()

輸出結果

Out[410]: 
                  math
grade class           
1     1      87.666667
2     1      77.000000
      2      77.000000
  1. 彙總變量

        在進行分組彙總時,彙總變量也可以有多個,例如按照年級彙總數學、語文成績,彙總統計量均爲均值。在 groupby 後直接使用中括號篩選相應列,再接彙總統計量。

st_data.groupby(['grade'])['math','chinese'].mean()
  1. 彙總統計量

        groupby 後可接的彙總統計量如下表

統計量 含義 統計量 含義
mean 均值 mad 平均絕對偏差
max 最大值 count 計數
min 最小值 skew 偏度
median 中位數 quantile 指定分位數
std 標準差

        這些統計量方法可以直接接 groupby 對象使用,此外,agg 方法提供了一次彙總多個統計量的方法,例如,彙總各個班級的數學成績的均值、最大值和最小值。

st_data.groupby('class')['math'].agg(['mean','min','max'])

輸出結果

Out[413]: 
       mean   min   max
class                  
1      85.0  77.0  98.0
2      77.0  77.0  77.0
  1. 多重索引

        我們注意到,在進行分組彙總操作時,產生的結果並不是常見的二維表數據框,而是具有多重索引的數據框,這裏介紹Pandas數據框的多重索引功能。

df = st_data.groupby(['grade','class'])['math','chinese'].agg(['min','max'])
df

        上述以年級、班級對學生的數學、語文成績進行分組彙總,彙總統計量爲均值。此時 df 數據框中有兩個行索引和兩個列索引。

        輸出結果

Out[415]: 
             math       chinese    
              min   max     min max
grade class                        
1     1      78.0  98.0      78  88
2     1      77.0  77.0      77  77
      2      77.0  77.0      54  56

查詢列索引

        當需要篩選列時,第一個中括號篩選第一重列索引,第二個中括號代表篩選第二重列索引。
        例如查詢各個年級、班級的數學成績的最小值。

df['math']['min']

        此外,也可以以 ix 方法查詢指定的列,注意多重列索引以“()”方式寫出。

        輸出結果

Out[416]: 
grade  class
1      1        78.0
2      1        77.0
       2        77.0
Name: min, dtype: float64

查詢行索引

df.ix[(1,1),('chinese','min')]#78
df.ix[(2,1),('chinese','min')]#77
df.ix[(2,2),('chinese','min')]#54

5.1.7 拆分、堆疊列

  1. 拆分列

        在進行數據處理時,有時要將原數據的指定列按照列的內容拆分爲新的列。
        上述數據中,原數據的行由標識變量cust_id,分組變量type,值變量Monetary組成,經過拆分後,相當於原先分組變量type中的每個取值成爲新列,相應值由原數據值變量Monetary的取值填補。

table = pd.DataFrame({'cust_id':[101,102,103,101,102],
                      'type':['Normal','Special_offer','Normal','Special_offer','Special_offer'],
                      'Monetary':[3608,420,1894,3503,4567]})

        現需要將type拆分爲兩列,這裏使用pd.pivot_table函數,第一個參數爲待拆分的表;index表示原數據中的標示列,也可以稱作爲橫向索引;colnumns表示該變量中的取值將會成爲新變量的變量名,在這裏就是把type中的值作爲列;values表示待拆分的列,也就是把values的列的進入函數運算(拆分列後默認彙總函數爲均值,且缺失值情況NaN填補。

pd.pivot_table(table,index = 'cust_id',columns='type',values='Monetary')

        輸出結果

Out[425]: 
type     Normal  Special_offer
cust_id                       
101      3608.0         3503.0
102         NaN         2493.5
103      1894.0            NaN

        此外,pd.pivot_table函數提供了fill_value參數和aggfunc函數用於指定拆分列後的缺失值和分組彙總函數。

缺失值填補爲0,彙總統計量爲求和。

pd.pivot_table(table,index = 'cust_id',columns='type',
               values='Monetary',fill_value=0,aggfunc='sum')
  1. 堆疊列

        堆疊列是拆分列的反操作,當存在表示列中有多個數值變量的時候,可以通過堆疊列將多列的數據堆積成一列

        Pandas提供了pd.melt函數用於完成堆疊列

table1 = pd.pivot_table(table,index='cust_id',
                        columns='type',
                        values='Monetary',
                        fill_value=0,
                        aggfunc=np.sum).reset_index()
table1

        輸出結果

Out[428]: 
type  cust_id  Normal  Special_offer
0         101    3608           3503
1         102       0           4987
2         103    1894              0

        對table1進行堆疊列操作,table1代表待堆疊列的列名,id_vars代表標示變量,value_vars代表待堆疊的變量,value_name爲堆疊後值變量列的名稱,var_name爲堆疊後堆疊變量的名稱。

table1
pd.melt(table1,
        id_vars='cust_id',
        value_vars=['Normal','Special_offer'],
        value_name='Monetary',
        var_name='TYPE')

        輸出結果

Out[429]: 
   cust_id           TYPE  Monetary
0      101         Normal      3608
1      102         Normal         0
2      103         Normal      1894
3      101  Special_offer      3503
4      102  Special_offer      4987
5      103  Special_offer         0

5.18 賦值與條件複製

  1. 賦值

        在一些特定場合,如錯誤值處理、異常值處理,可能會對原數據的某些值進行修改,此時會涉及類似SQL的insert或update操作。Pandas提供了一些方法能夠快速高效地完成賦值操作。

        如下數據中,有學生成績爲999分,希望替換爲缺失值。

sample = pd.DataFrame({'name':['Bob','Lindy','Mark','Miki','Sully','Rose'],
                       'score':[99,78,999,77,77,80],
                       'group':[1,1,1,2,1,2]})

        可使用replace方法替換值

sample.score.replace(999,np.nan)

        輸出結果

Out[431]: 
0    99.0
1    78.0
2     NaN
3    77.0
4    77.0
5    80.0
Name: score, dtype: float64

        遇到一次替換多個值時,還可以寫成字典形式,如下所示,該操作將 sample 數據框中, score 列所有取值爲999的值替換爲NaN,name 列中取值爲 ‘Bob’ 替換爲NaN。

sample.replace({'score':{999:np.nan},
                'name':{'Bob':np.nan}})

        輸出結果

Out[432]: 
   group   name  score
0      1    NaN   99.0
1      1  Lindy   78.0
2      1   Mark    NaN
3      2   Miki   77.0
4      1  Sully   77.0
5      2   Rose   80.0

        如Kaggle 共享單車需求量預測中

dataset['season'] = dataset['season'].map({1:'spring',2:'summer',3:'fall',4:'winter'})
dataset['weather'] = dataset['weather'].map({1:'Good',2:'Normal',3:'Bad',4:'Very Bad'})

        將量化的季節特徵和天氣特徵轉化爲字符串描述的數據。

  1. 條件賦值

        一般在修改數據時,都是進行條件查詢後再進行賦值。這裏以 sample 數據爲例

sample
Out[433]: 
   group   name  score
0      1    Bob     99
1      1  Lindy     78
2      1   Mark    999
3      2   Miki     77
4      1  Sully     77
5      2   Rose     80

        條件賦值可以通過 apply 方法完成,Pandas 提供的 apply 方法可以對一個數據框對象進行行、列的遍歷操作,參數 axis 設定0時代表對class_n循環,axis 設定爲1時代表對循環,且 apply 後接的彙總函數是可以自定義的。
        現需要根據 group 列生成新列 class_n:當 group 爲1時,class_n 列爲 class1,當 group 爲2時,class_n列爲 class2,使用 apply 如下所示:

def transform(row):
    if row['group']==1:
        return ('class1')
    elif row['group']==2:
        return ('class2') 
sample.apply(transform,axis=1)

        輸出結果

Out[436]: 
0    class1
1    class1
2    class1
3    class2
4    class1
5    class2
dtype: object

        apply 產生 pd.Series 類型的對象,進而可以通過 assign 加入到數據中。

sample.assign(class_n = sample.apply(transform,axis=1))  

        輸出結果

Out[435]: 
   group   name  score class_n
0      1    Bob     99  class1
1      1  Lindy     78  class1
2      1   Mark    999  class1
3      2   Miki     77  class2
4      1  Sully     77  class1
5      2   Rose     80  class2

        除了apply方法外,還可以通過條件查詢直接賦值,如下所示,注意第一句“sample = sample.copy()”最好不要省略,否則可能會產生警告信息。

sample = sample.copy()
sample.loc[sample.group==1,'class_n'] = 'class1'
sample.loc[sample.group==2,'class_n'] = 'class2'
sample

        輸出結果

Out[440]: 
   group   name  score class_n
0      1    Bob     99  class1
1      1  Lindy     78  class1
2      1   Mark    999  class1
3      2   Miki     77  class2
4      1  Sully     77  class1
5      2   Rose     80  class2

        如 Kaggle 泰坦尼克號預測乘客的獲救情況

        Sex 有兩個屬性:male 和 female,代表男性和女性。爲了方便分類器處理,我們可以用1和0代替。

titianic_data['Sex'] = titianic_data['Sex'].apply(lambda x:1 if x == "male" else 0)

5.2 數據清洗

        數據清洗是數據分析的必備環節,在進行分析過程中,會有很多不符合分析要求的數據,例如重複、錯誤、缺失、異常類數據。

5.2.1 重複值處理

        Pandas 提供查看、處理重複數據的方法duplicated和drop_duplicate。
        示例

sample = pd.DataFrame({'id':[1,1,1,3,4,9],
                       'name':['Bob','Bob','Mark','Miki','Sully','Rose'],
                       'score':[99,99,87,77,77,np.nan],
                       'group':[1,1,1,2,1,2]})

查看重複值

#查看重複值    
sample[sample.duplicated()]

        輸出結果

Out[442]: 
   group  id name  score
1      1   1  Bob   99.0

刪除重複值

#去重
sample.drop_duplicates()
Out[444]: 
   group  id   name  score
0      1   1    Bob   99.0
2      1   1   Mark   87.0
3      2   3   Miki   77.0
4      1   4  Sully   77.0
5      2   9   Rose    NaN

        drop_duplicates 方法還可以按照某列去重,例如去除 id 列重複的所有記錄:

sample.drop_duplicates('id')
Out[445]: 
   group  id   name  score
0      1   1    Bob   99.0
3      2   3   Miki   77.0
4      1   4  Sully   77.0
5      2   9   Rose    NaN

5.2.2 缺失值處理

        一般地,連續變量可以使用均值或中位數進行填補;分類變量可以使用衆數進行填補;如果缺失變量比較重要,一般採用建模方法進行填補,具體使用哪種方法,根據具體案例和業務要求來使用。

        示例

sample = pd.DataFrame({'id':[1,1,1,3,4,np.nan],
                       'name':['Bob','Bob','Mark','Miki','Sully',np.nan],
                       'score':[99,np.nan,87,77,76,np.nan],
                       'group':[1,1,np.nan,2,1,np.nan]})
sample  

        輸出結果

Out[447]: 
   group   id   name  score
0    1.0  1.0    Bob   99.0
1    1.0  1.0    Bob    NaN
2    NaN  1.0   Mark   87.0
3    2.0  3.0   Miki   77.0
4    1.0  4.0  Sully   76.0
5    NaN  NaN    NaN    NaN 
  1. 查看缺失情況

        在進行數據分析錢,一般需要了解數據的缺失情況,在 Python 中可以構造一個 lambda 函數來查看缺失值, lambda函數中,sum(col.isnull()) 表示當前列有多少缺失,col.size 表示當前列總共有多少行數據。

sample.apply(lambda col:sum(col.isnull())/col.size)

        輸出結果

Out[449]: 
group    0.333333
id       0.166667
name     0.166667
score    0.333333
dtype: float64
  1. 指定值填補

        Pandas 數據框提供了 fillna 方法完成對缺失值的填補,例如對sample表的列score填補缺失值,填補方法爲均值。

sample.score.fillna(sample.score.mean())
#中位數
#sample.score.fillna(sample.score.median())
  1. 缺失值指示變量

        Pandas 數據框對象可以直接調用方法isnull產生缺失值指示變量,例如產生score變量的缺失值指示變量。
        若想轉換爲數值0、1型指示變量,可以使用apply方法,int表示將該列替換爲int類型。

sample.score.isnull()
#sample.score.isnull().apply(int)

        輸出結果

Out[453]: 
0    False
1     True
2    False
3    False
4    False
5     True
Name: score, dtype: bool

5.2.3 噪聲值處理

        噪聲值是指數據中有一個或幾個數值與其他數值相比差異較大的值,又稱爲異常值、離羣值。
        一般異常值的檢測方法有基於統計的方法、基於聚類的方法,以及一些專門檢測異常值的方法等。

  1. 簡單統計

        如果使用Pandas,我們可以直接使用describe()來觀察數據的統計性描述(只是粗略的觀察一些統計量),不過統計數據爲連續型。如學生成績的最大值爲999,這也可以直接判斷爲異常值。

或者使用散點圖也能很清晰地觀察到異常值的存在

  1. 拉依達準則

這個準則有個條件:數據需要服從正態分佈。異常值如超過3倍標準差,那麼可以將其視爲異常值。

正負3σ\sigma的概率爲99.7%,那麼距離平均值3σ\sigma之外的值出現的概率爲

P(xu&gt;3σ)0.003 P(|x-u|&gt;3\sigma)\leq0.003
屬於極小個別的小概率事件,如果數據不服從正態分佈,也可以用遠離平均值的多少倍標準差來描述。

  1. 箱線圖

        這種方法是利用箱線圖的**四分位距(IQR)**對異常值進行檢測。
        四分位距(IQR)就是上四分位距與下四分位距的差值。我們通過IQR的1.5倍爲標準,規定:小於下四分位距-1.5IQR距離,或者大於上四分位距+1.5IQR距離 的點爲異常值。

        使用numpy的percentile方法

Percentile = np.percentile(df['length'],[0,25,50,75,100])
IQR = Percentile[3]-Percentile[1]
Uplimit = Percentile[3]+IQR*1.5
Downlimit = Percentile[1]-IQR*1.5

        也可以使用seaborn的可視化方法boxplot來實現

        紅色箭頭所指就是異常值。

        還有很多方法,這裏給出鏈接,感興趣的讀者可以自行學習。
【Python數據分析基礎】: 異常值檢測和處理

5.3 RFM方法在客戶行爲分析上的運用

5.3.1 行爲特徵提取的RFM方法論

        根據美國數據庫營銷研究所Arthur Hughes的研究,客戶數據庫中有3個重要指標,分佈如下:

  1. 最近一次消費(Recency)

        最近一次消費指的是客戶上一次購買的時間。上一次消費時間越近的客戶,對提供即使的商品或服務也最有可能有所反應。

  1. 消費頻率(Frequency)

        消費頻率是客戶在限定的期間內所購買的次數。最常購買的客戶,也是滿意度最高的客戶。這個指標是“忠誠度”很好的代理變量。

  1. 消費金額(Monetary)

        消費金額是最近消費的平均金額,是體現客戶短期價值的重要變量。

5.3.2 使用RFM方法計算變量

        RFM_TRAD_FLOW爲某一段時間內某零售商客戶的消費記錄

RFM_TRAD_FLOW 表的變量說明
名稱 類型 標籤
transID 數值 記錄ID
cumid 數值 客戶編號
time 日期 收銀時間
amount 數值 銷售金額
type_lable 字符 銷售類型:特價、退貨、贈送、正常
type 字符 銷售類型,同上,顯示爲英文:Special_offer、returned_goods、Presented、Normal

        讀取數據

rfm_traindata = pd.read_csv('RFM_TRAD_FLOW.csv',encoding = 'gbk')
rfm_traindata.head()

輸出結果

Out[456]: 
   transID  cumid              time  amount type_label    type
0     9407  10001  14JUN09:17:58:34   199.0         正常  Normal
1     9625  10001  16JUN09:15:09:13   369.0         正常  Normal
2    11837  10001  01JUL09:14:50:36   369.0         正常  Normal
3    26629  10001  14DEC09:18:05:32   359.0         正常  Normal
4    30850  10001  12APR10:13:02:20   399.0         正常  Normal

        分析客戶對不同品類的購物金額,這需要按照客戶ID和購物類別,對購物金額進行計算購物總花費金額。

M = rfm_traindata.groupby(['cumid','type'])[['amount']].sum()
M.head(7)

        輸出結果

Out[460]: 
                      amount
cumid type                  
10001 Normal          3608.0
      Presented          0.0
      Special_offer    420.0
      returned_goods  -694.0
10002 Normal          1894.0
      Presented          0.0
      returned_goods  -242.0

        按照cumid分組,對amount變量進行拆分,拆分後的變量名由type的不同取值提供。

M_trans = pd.pivot_table(M,index='cumid',columns='type',values='amount')
M_trans.head(7)

        輸出結果

Out[462]: 
type   Normal  Presented  Special_offer  returned_goods
cumid                                                  
10001  3608.0        0.0          420.0          -694.0
10002  1894.0        0.0            NaN          -242.0
10003  3503.0        0.0          156.0          -224.0
10004  2979.0        0.0          373.0           -40.0
10005  2368.0        0.0            NaN          -249.0
10006  2534.0        0.0           58.0          -733.0
10007  4021.0        0.0          179.0          -239.0

        用戶的購買頻次和最近一次消費R的計算方法,按照用戶分組後分別彙總任意變量的頻次以及time變量的最大值。

#RFM的F
f= rfm_traindata.groupby(['cumid','type'])[['amount']].count()
f.head(7)
f_trans = pd.pivot_table(f,index='cumid',columns='type',values='amount')
f_trans.head(7)

##RFM的R
import time
# 先將非標準字符串時間格式化爲時間數組,再轉換爲時間戳便於計算
rfm_traindata['time'] = rfm_traindata['time'].map(lambda x: time.mktime(time.strptime(x, '%d%b%y:%H:%M:%S')))
# 查找每個購物ID每個銷售類型下的最近時間
R= rfm_traindata.groupby(['cumid','type'])[['time']].max()
R.head(7)
# 轉化爲透視表
R_trans = pd.pivot_table(R,index='cumid',columns='type',values='time')
R_trans.head(7)

5.3.3 數據整理與彙報

        我們希望計算特價商品購買比例這樣一個指標,該比例越高,說明這個客戶對打折商品越感興趣。但是不能直接計算,因爲 Special_offer 有大量的缺失值,這是因爲有很多客戶從未購買過打折商品,因此該變量的缺失值需要用“0”替換。

M_trans ['Special_offer'] = M_trans['Special_offer'].fillna(0)

        最後一步:計算購買特價商品的比例,並按照該比例降序排序。

M_trans ['Spe_ratio'] = M_trans ['Special_offer'] / (M_trans ['Special_offer'] + M_trans ['Normal'] )
M_trans.sort_values('Spe_ratio',ascending = False,na_position = 'last').head()

        輸出結果

Out[482]: 
type   Normal  Presented  Special_offer  returned_goods  Spe_ratio
cumid                                                             
10151   765.0        0.0          870.0             NaN   0.532110
40033  1206.0        0.0          761.0          -848.0   0.386884
40236  1155.0        0.0          691.0          -793.0   0.374323
30225  1475.0        0.0          738.0          -301.0   0.333484
20068  1631.0        0.0          731.0          -239.0   0.309483

        排在最前面的就是對打折商品偏好最高的客戶,從上至下依次遞減,基於以上分析,爲了提升打折促銷的效果,只要按照上面的計算結果由上至下篩選客戶,並選定對打折偏好較高的部分用戶進行定向營銷。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章