2. Μονάδες κώδικα#

Οι μονάδες κώδικα (module) είναι βιβλιοθήκες συναρτήσεων και αντικειμένων της Python, οι οποίες δεν φορτώνονται εξ ορισμού. Κάποιες περιλαμβάνονται στην βασική εγκατάσταση της Python, ενώ άλλες παρέχονται από τρίτους συνήθως μέσω του package installer (pip) και του επίσημου ευρετηρίου πακέτων pypi.

Tip

Για την εγκατάσταση πακέτων python (και των συμπεριλαμβανόμενων μονάδων κώδικα) στον υπολογιστή μπορεί να χρησιμοποιηθεί η εντολή pip, η οποία επιπλέον φροντίζει για τον έλεγχο και την εγκατάσταση όλων των προαπαιτούμενων.

pip install <package>

Οι μονάδες κώδικα που χρησιμοποιούνται συχνά στην αριθμητική ανάλυση είναι οι:

  1. NumPy

  2. Matplotlib

  3. SciPy

Στο ευρετήριο διατίθεται πληθώρα πακέτων για εξειδικευμένες επιστημονικές εφαρμογές, τα οποία συχνά χρησιμοποιούν την NumPy.

2.1. Μονάδα κώδικα NumPy#

Η καθιερωμένη Python παρέχει κάποιες βασικές δυνατότητες αριθμητικών υπολογισμών, αλλά δεν είναι κατάλληλη για απαιτητικές περιπτώσεις. Η μονάδα κώδικα NumPy καλύπτει αυτό το κενό, παρέχοντας κατάλληλες δομές δεδομένων για πίνακες και διανύσματα και βελτιστοποιημένες συναρτήσεις υπολογισμού.

H NumPy έχει τους δικούς της τύπους δεδομένων. Για την αριθμητική ανάλυση πιο χρήσιμος είναι ο τύπος αριθμού κινητής υποδιαστολής διπλής ακρίβειας (double). Ο τύπος double κατά κανόνα καταλαμβάνει 64 bit, όπως κι ο τύπος float της καθιερωμένης Python, αλλά έχει ελαφρώς διαφορετική συμπεριφορά σε ειδικές περιπτώσεις. Μια τέτοια περίπτωση είναι η διαίρεση με το μηδέν, κατά την οποία δεν προκύπτει σφάλμα.

Ο τύπος δεδομένων float της C/C++ καταλαμβάνει 32 bit και είναι αντίστοιχος του τύπου numpy.single, όχι του float της καθιερωμένης Python.

Αυστηρά μιλώντας, ο τύπος της numpy που καταλαμβάνει 64 bit είναι ο float64. Στους σύγχρονους υπολογιστές αρχιτεκτονικής 64 bit ταυτίζεται με τον double.

Tip

Εκτός από κάποιες μικρές διαφορές, η NumPy είναι υπερσύνολο της ενσωματωμένης βιβλιοθήκης math, οπότε δεν χρειάζεται να δηλώνονται κι οι δύο.

import numpy as np  #   np is the standard name to reference numpy

a=np.double(0)
print(a.itemsize)   # expecting 8 bytes
1/a # Raises warning, not error
8
/tmp/ipykernel_200691/469193324.py:5: RuntimeWarning: divide by zero encountered in scalar divide
  1/a # Raises warning, not error
inf

2.1.1. Αρχικοποίηση και ορισμός πινάκων#

Η αποτελεσματικότητα της NumPy οφείλεται στην βασική της δομή δεδομένων που είναι ο πίνακας Ν διαστάσεων (ndarray). O πίνακας της NumPy διαχειρίζεται σειριακά διατεταγμένα αριθμητικά δεδομένα ίδιου τύπου. Οι διαστάσεις, το μέγεθος και ο τύπος ενός πίνακα ορίζονται κατά την δημιουργία του.

import numpy as np
N=3            # Uppercase used to denote constants by convention (no real constants in python)
M=2

a=np.empty(N) # uninitialized values (could be anything)
print(a)
a=np.zeros(N) # zero values
print(a)
a=np.ones(N) # unit values
print(a)
a=np.ones((N,M))   #   2d array, N lines x M columns
print(a)
a=np.eye(N)   #   Identity array
print(a)
[4.68675814e-310 0.00000000e+000 2.37151510e-322]
[0. 0. 0.]
[1. 1. 1.]
[[1. 1.]
 [1. 1.]
 [1. 1.]]
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
#Integer vectors
a=np.array([0,1,2,3,4,5,6,7,8,9])
a=np.array([x for x in range(10)])
a=np.arange(0,10)       #from 0 to 10 (10 is excluded) with step 1

#float vectors
a=np.linspace(0,9,10)   #from 0. to 9. in 10 equal steps 
a=np.arange(0.,10.)

Hint

Κατά την δημιουργία πινάκων μπορεί να χρησιμοποιηθεί το προαιρετικό όρισμα dtype για να οριστεί ο τύπος των στοιχείων. Εφόσον δεν οριστεί, η συνάρτηση δημιουργίας πίνακα επιλέγει τον τύπο με βάση τα παρεχόμενα δεδομένα. Ο τύπος δεν αλλάζει κατά την χρήση του πίνακα και σε οποιαδήποτε απόδοση τιμής απαιτείται, γίνεται πρώτα μετατροπή τύπου.

a=np.array([0,1,2,3,4,5,6,7,8,9])
a[0]=3.14   #   Conversion to int

b=np.array([0,1,2,3,4,5,6,7,8,9],dtype=float)
b[0]=3.14   # b[0] is already a float

print(type(a[0]),type(b[0]))
print(a,b)
<class 'numpy.int64'> <class 'numpy.float64'>
[3 1 2 3 4 5 6 7 8 9] [3.14 1.   2.   3.   4.   5.   6.   7.   8.   9.  ]

2.1.2. Ιδιότητες πινάκων#

Η δομή ndarray εκτός από τα δεδομένα καθαυτά εμπεριέχει τις ιδιότητες του πίνακα, όπως τον τύπο των δεδομένων, τον αριθμό των διαστάσεων και το μέγεθος κάθε διάστασης. Οι ιδιότητες είναι απαραίτητες σε ορισμένες πράξεις όπως για παράδειγμα τον πολλαπλασιασμό πινάκων στις οποίες απαιτείται συμβατότητα διαστάσεων.

\[\underbrace{C}_{N\times M}=\underbrace{A}_{N\times M}+ \underbrace{B}_{N\times M}\]
\[\underbrace{C}_{N\times M}=\underbrace{A}_{N\times L}\cdot \underbrace{B}_{L\times M}\]
print(a)
print(type(a),a.dtype,a.shape,a.size,len(a))
[3 1 2 3 4 5 6 7 8 9]
<class 'numpy.ndarray'> int64 (10,) 10 10

2.1.3. Στοιχεία και τμήματα πινάκων#

Τα στοιχεία των πινάκων είναι προσβάσιμα με αγκύλες [], όπως και στις λίστες. Σε ένα πίνακα \(n\) στοιχείων, το πρώτο έχει πάντα δείκτη 0 και το τελευταίο \(n-1\).

Tip

Η διαφορετική προσέγγιση στην αρίθμηση των δεικτών είναι ένα σημείο στο οποίο πρέπει να δώσουν ιδιαίτερη προσοχή οι χρήστες του Matlab και της Fortran.

print(a[0])
print(a[9])
3
9

Με την έκφραση \([n:m:s]\) μπορεί να εξαχθεί το τμήμα ενός πίνακα (τεμαχισμός).

  • Το \(n\) αντιπροσωπεύει το αρχικό στοιχείο του πίνακα που θα περιληφθεί στο τμήμα. Εξ’ ορισμού \(n=0\).

  • Το \(m\) αντιπροσωπεύει το αμέσως επόμενο από το τελικό στοιχείο που θα περιληφθεί στο τμήμα. Εξ’ ορισμού \(m=size\).

  • To \(s\) αντιπροσωπεύει το βήμα. Εξ’ ορισμού \(s=1\).

print(a[5:])    # from 6th element to last element (10th) with step 1
print(a[5:8])   # from 6th element to 8th element with step 1
print(a[5:8:2]) # from 6th element to 8th element with step 2
[5 6 7 8 9]
[5 6 7]
[5 7]
  • Όρια με αρνητικό πρόσημο χρησιμοποιούνται για αντίστροφη μέτρηση από το τέλος.

print(a[-3:-1])
[7 8]

Tip

Όπως και στις υπόλοιπες δομές δεδομένων της python, ο τεμαχισμός πίνακα δημιουργεί μία εικόνα στον ίδιο χώρο μνήμης και όχι ένα αντίγραφο. Για καλύτερη αξιοποίηση της μνήμης, χρησιμοποιήστε την copy σε περιπτώσεις που θέλετε να κρατήσετε ένα μικρό τμήμα ενός μεγαλύτερου πίνακα, ο οποίος δεν χρειάζεται σε κάτι άλλο. Εάν θέλετε να αντιγράψετε τιμές σε υπάρχοντα πίνακα, δηλώστε την περιοχή του πίνακα που θα δεχθεί τις νέες τιμές (π.χ. “[:]”).

b=a[0:4:2]  # view of a
c=a[0:4:2].copy()  # new array, copy of a

a[0]=-1     # changes a and b but not c

print(b,c)

d=np.empty(a.size)  # Create empty array
d[:]=a  #   copy elements to existing array, different from d=a
print(d)
[-1  2] [3 2]
[-1.  1.  2.  3.  4.  5.  6.  7.  8.  9.]

2.1.4. Πράξεις με πίνακες#

  • Οι αριθμητικές πράξεις πίνακα με βαθμωτό μέγεθος (scalar) εφαρμόζονται σε όλα τα στοιχεία ομοίως. Το αποτέλεσμα είναι πίνακας όμοιου μεγέθους.

a=np.arange(0,10)
print(a+1)
[ 1  2  3  4  5  6  7  8  9 10]
  • Οι μαθηματικές συναρτήσεις την numpy με όρισμα εφαρμόζονται σε όλα τα στοιχεία ξεχωριστά και δίνουν πίνακα ομοίου μεγέθους.

print(np.sqrt(np.arange(0,10)))
[0.         1.         1.41421356 1.73205081 2.         2.23606798
 2.44948974 2.64575131 2.82842712 3.        ]
  • Οι αριθμητικές πράξεις πίνακα με πίνακα εφαρμόζονται στοιχείο προς στοιχείο. Οι πίνακες πρέπει να έχουν το ίδιο μέγεθος.

a=np.arange(0,10)
b=np.arange(10,0,-1)
print(a+b)
[10 10 10 10 10 10 10 10 10 10]

Exercise 2.1

Υπολογίστε την έκφραση \(b=Α x\) όπου:

\[\begin{split}Α= \begin{pmatrix} 1 & 2 & 3 \\ 4 & 2 & 1 \\ 1 & 1 & 1 \\ \end{pmatrix}\end{split}\]
\[\begin{split}x= \begin{pmatrix} 1 \\ 4 \\ 2 \\ \end{pmatrix}\end{split}\]
import numpy as np

A=np.array([[1,2,3],[4,2,1],[1,1,1]])
x=np.array([1,4,2])

b=A@x # Equivalent to b=np.dot(A,x)
print(b)

x=np.linalg.inv(A)@b    # Verification using inverse matrix of A to calculate x
print(x)
[15 14  7]
[1. 4. 2.]

2.1.5. Χρήσιμες συναρτήσεις#

Από τους πίνακες μπορούν εύκολα να εξαχθούν αθροίσματα και γινόμενα:

μέγιστα και ελάχιστα:

και η θέση τους στον πίνακα (η πρώτη εμφάνιση αν υπάρχουν πολλές):

import numpy as np

A=np.array([[4.,8.5,9.,7.3,10.5,4.,5.]])
print(np.sum(A))
print(np.prod(A))
print(np.max(A),np.argmax(A))
print(np.min(A),np.argmin(A))
48.3
469097.99999999994
10.5 4
4.0 0

2.2. Μονάδα κώδικα Matplotlib#

H Matplotlib είναι μια βιβλιοθήκη για την δημιουργία γραφικών παραστάσεων. Οι δυνατότητές της περιλαμβάνουν πολλούς τύπους γραφημάτων.

Exercise 2.2

Απεικονίστε σε γραφική παράσταση την συνάρτηση του συνημιτόνου ως προς \(x\), το οποίο μεταβάλλεται από 0 ως 360° με βήμα 10°.

import matplotlib.pyplot as plt
import numpy as np

x=np.arange(0,360+10,10)
y=np.cos(np.radians(x))

fig, ax = plt.subplots()
ax.set(xlabel='x', ylabel='y')
ax.plot(x,y,"^-r")  # continuous red line 
ax.grid()
plt.show()
_images/c8e5b40ccdecbec7256a10c71623fbf5d93742c59051b3f42abaa699107d2958.png

2.3. Μονάδα κώδικα SciPy#

H SciPy παρέχει υλοποιήσεις βασικών αλγορίθμων για επιστημονικούς υπολογισμούς. Στα επόμενα κεφάλαια αναλύονται οι βασικοί αλγόριθμοι αριθμητικών μεθόδων και παρουσιάζονται με παραδείγματα σε Python. Στο τέλος παρουσιάζονται και οι έτοιμες υλοποιήσεις της SciPy, οι οποίες μπορούν να χρησιμοποιηθούν αποτελεσματικότερα από τον χρήστη, εφόσον έχει κατανοήσει τον σχετικό αλγόριθμό. Η SciPy βασίζεται σε κώδικα που έχει αναπτυχθεί και βελτιστοποιηθεί εδώ και δεκαετίες σε χαμηλότερου επιπέδου γλώσσες προγραμματισμού όπως C,C++ και Fortran, έτσι ώστε να είναι αποτελεσματική σε ταχύτητα υπολογισμού.