Διάλεξη 24ης Μαρτίου 2015
Θέμα της σημερινής διάλεξης είναι η βιβλιοθήκη NumPy (Numeric Python) η οποία παρέχει τον τύπο ndarray
καθώς
και μεθόδους για την δημιουργία και επξεργασία πολυδιάστατων πινάκων. Ο ενδιαφερόμενος αναγνώστης μπορεί να βρει περισσότερες πληροφορίες
για την συγκεκριμένη βιβλιοθήκη στην ιστοσελίδα www.numpy.org.
Ο προτεινόμενος τρόπος για τη χρήση της βιβλιοθήκης NumPy είναι με την οδηγία import numpy as np
. Η συνάρτηση
array()
μετατρέπει κάθε (φωλιασμένη) ακολουθία σε αντικείμενο τύπου array
. Αν a
είναι
ένα αντικείμενο τύπου array
τότε a.ndim
είναι ο αριθμός των διαστάσεων ή αξόνων (axes) του πίνακα.
a.shape
είναι ένα tuple ακεραίων που δηλώνουν το μέγεθος του πίνακα σε κάθε διάσταση. Για παράδειγμα, ένας πίνακας
με m γραμμές και n στήλες έχει διαστάσεις (m, n). Τέλος, a.dtype
επιστρέφει τον τύπο των στοιχείων του πίνακα,
π.χ. int, float ή complex.
>>> import numpy as np
>>> np.array([1, 2, 3])
array([1, 2, 3])
>>> a = np.array([ (1, 2, 3), (4, 5, 6) ])
array([[1, 2, 3],
[4, 5, 6]])
>>> a.ndim
2
>>> a.shape
(2, 3)
>>> a = np.array([1, 2, 3], dtype=float)
>>> a.dtype
dtype('float64')
Οι συναρτήσεις zeros
, ones
και empty
, με όρισμα ένα tuple ακεραίων που δηλώνουν
το μέγεθος του πίνακα σε κάθε διάσταση, κατασκευάζουν ένα μηδενικό πίνακα, ένα πίνακα όλα τα στοιχεία του οποίου είναι
ίσα με 1 και ένα πίνακα τα στοιχεία του οποίου είναι τυχαία υπό την έννοια ότι εξαρτώνται από την κατάσταση της μνήμης
του υπολογιστή:
>>> np.zeros( (2, 3) )
array([[ 0., 0., 0.],
[ 0., 0., 0.]])
>>> np.ones( (2, 2) )
array([[ 1., 1.],
[ 1., 1.]])
>>> np.empty((2))
array([1.72723371e-77, 2.00389811e+00])
>>> np.ones((2,3), dtype=int) # δείτε ότι τώρα τα στοιχεία του πίνακα είναι ακέραιοι
array([[1, 1, 1],
[1, 1, 1]])
Μπορούμε ακόμα να κατασκευάσουμε πίνακες τα στοιχεία των οποίων είναι ακολουθίες αριθμών. Η συνάρτηση arange
είναι ανάλογη της
range
. Η συνάρτηση linspace
είναι χρήσιμη για την κατασκευή διαμερίσεων με ομοιόμορφο βήμα αλλά και τον υπολογισμό
συναρτήσεων σε μεγάλο πλήθος σημείων:
>>> a=np.arange(6)
>>> print(a)
[0 1 2 3 4 5]
>>> a=np.arange(9,1,-2)
>>> print(a)
[9 7 5 3]
>>> np.linspace(0,1,5)
array([ 0., 0.25, 0.5, 0.75, 1. ])
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
Το "σχήμα" (shape) ενός πίνακα μπορεί να αλλάξει εύκολα με τις μεθόδους reshape
, ravel
, transpose
και
resize
:
>>> a=np.arange(12)
>>> print(a)
[ 0 1 2 3 4 5 6 7 8 9 10 11]
>>> a.reshape(3,4)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> a.reshape(2,-1)
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
>>> a.resize(6,2)
>>> print(a)
[[ 0 1]
[ 2 3]
[ 4 5]
[ 6 7]
[ 8 9]
[10 11]]
>>> a.transpose()
array([[ 0, 2, 4, 6, 8, 10],
[ 1, 3, 5, 7, 9, 11]])
>>> a.ravel()
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> print(a) # Το shape εξακολουθεί να είναι (6,2)
[[ 0 1]
[ 2 3]
[ 4 5]
[ 6 7]
[ 8 9]
[10 11]]
Πράξεις με πίνακες
Οι αριθμητικοί τελεστές αλλά και οι συνηθισμένες μαθηματικές συναρτήσεις εφαρμόζονται σε ένα πίνακας dot
:
>>> a = arange(4).reshape(2,2)
>>> b = arange(4,8).reshape(2,2)
>>> c = a+b
print(c)
[[ 4 6]
[ 8 10]]
>>> d = a*b
print(d)
[[ 0 5]
[12 21]]
>>> np.sin(a)
array([[ 0. , 0.84147098],
[ 0.90929743, 0.14112001]])
>>> a**2+b**2
array([[16, 26],
[40, 58]])
>>> d > 12
array([[False, False],
[False, True]], dtype=bool)
>>> np.dot(a,b)
array([[ 6, 7],
[26, 31]])
>>> np.any(a < 3)
True
>>> np.all(b > 3)
True
>>> a += b
print(a)
[[ 4 6]
[ 8 10]]
>>> np.exp(a*1j)
array([[-0.65364362-0.7568025j , 0.96017029-0.2794155j ],
[-0.14550003+0.98935825j, -0.83907153-0.54402111j]])
Οι μέθοδοι sum
, min
και max
δέχονται ένα προαιρετικό όρισμα, την διάσταη (axis) του
πίνακα κατά την οποία εφαρμόζονται:
>>> a = np.arange(12).reshape(3,4)
>>> print(a)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> a.sum()
66
>>> a.sum(axis=0)
array([12, 15, 18, 21])
>>> a.min(axis=1)
array([0, 4, 8])
Εφαρμογές στη γραμμική άλγεβρα
Το πακέτο linalg
της βιβλιοθήκης NumPy έχει παρέχει ένα μεγάλο πλήθος συναρτήσεων
χρήσιμων στην αριθμητική γραμμική άλγεβρα. Για παράδειγμα, η συνάρτηση inner(a,b)
υπολογίζει το εσωτερικό γινόμενο δύο διανυσμάτων ενώ η συνάρτηση norm
υπολογίζει
τη νόρμα ενός διανύσματος ή ενός πίνακα:
>>> a = np.array( [1, 2, 3] )
>>> b = np.array( [2, 0, 1] )
>>> np.inner(a,b)
5
>>> np.linalg.norm(a, 1)
6
>>> np.linalg.norm(a, 2)
3.7416573867739413
>>> np.linalg.norm(a, np.inf)
3
Η συνάρτηση det
υπολογίζει την ορίζουσα ενός τετραγωνικού πίνακα και η συνάρτηση inv
τον
αντίστροφό του. Για τη λύση γραμμικών συστημάτων η βιβλιοθήκη NumPy παρέχει τη συνάρτηση solve
και για
τον υπολογισμό ιδιοτιμών και ιδιοδιανυσμάτων τη συνάρτηση eig
:
>>> A = np.array([ (1, 3, 2), (4, 5, 1), (2, 0, 1) ])
>>> np.linalg.det(A)
-21.000000000000011
>>> B = np.linalg.inv(A)
>>> B
array([[-0.23809524, 0.14285714, 0.33333333],
[ 0.0952381 , 0.14285714, -0.33333333],
[ 0.47619048, -0.28571429, 0.33333333]])
>>> np.dot(A,B)
array([[ 1.00000000e+00, -1.11022302e-16, 0.00000000e+00],
[ 0.00000000e+00, 1.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, 5.55111512e-17, 1.00000000e+00]])
>>> A = np.array([[3,1,5],[1,0,8],[2,1,4]])
>>> b = np.array([6,7,8])
>>> x = np.linalg.solve(A,b)
>>> print(x)
[-3.28571429 9.42857143 1.28571429]
>>> np.dot(A,x)
array([ 6., 7., 8.])
w, v = np.linalg.eig(A)
>>> w
array([ 7.78377088, 0.63421458, -1.41798546]) # eigenvalues
>>> v
array([[-0.63927142, -0.61967268, 0.02953761],
[-0.5895579 , 0.77250748, -0.9848506 ],
[-0.49371402, 0.13870102, 0.17087078]])
Τυχαίοι αριθμοί
Αρκετή από την αρχική δουλειά στην θεωρία των πιθανοτήτων αφιερώθηκε στην ανάλυση τυχερών παιχνιδιών, κυρίως παιχνιδιών με ζάρια. Το ενδιαφέρoν ενός εκ των πρωτοπόρων στη θεωρία των πιθανοτήτων, του Γάλλου μαθηματικού Blaise Pascal, κινήθηκε από την ερώτηση ενός φίλου κατά πόσο θα ήταν επικερδές να στοιχηματίσει ότι δεν θα φέρει εξάρες αν ρίξει ένας ζεύγος από ζάρια 24 φορές. Σήμερα γνωρίζουμε ότι η πιθανότητα να μην εμφανιστούν εξάρες είναι (35/36)24, δηλαδή περίπου 0.51. Μπορούμε να γράψουμε ένα πικρό πρόγραμμα για να προσομειώσουμε το συγκεκριμένο παιχνίδι και να προσεγγίσουμε πειραματικά την παραπάνω πιθανότητα.
def rollDie():
return np.random.random_integers(1,6)
def playGame(numTrials):
yes = 0.0
for i in range(numTrials):
for j in range(24):
d1 = rollDie()
d2 = rollDie()
if d1 == 6 and d2 == 6:
yes += 1
break
print 'Probability of losing = ' + str(1.0 - yes / numTrials)