Category Archives: Python

NumPy – 29 – sort di arrays – 1

Continuando da qui inizio un nuovo capitolo, qui.

Ordinare arrays
Up to this point we have been concerned mainly with tools to access and operate on array data with NumPy. This section covers algorithms related to sorting values in NumPy arrays. These algorithms are a favorite topic in introductory computer science courses: if you’ve ever taken one, you probably have had dreams (or, depending on your temperament, nightmares) about insertion sorts, selection sorts, merge sorts, quick sorts, bubble sorts, and many, many more. All are means of accomplishing a similar task: sorting the values in a list or array.

For example, a simple selection sort repeatedly finds the minimum value from a list, and makes swaps until the list is sorted. We can code this in just a few lines of Python:

As any first-year computer science major will tell you, the selection sort is useful for its simplicity, but is much too slow to be useful for larger arrays. For a list of N values, it requires N loops, each of which does on order ∼N comparisons to find the swap value. In terms of the “big-O” notation often used to characterize these algorithms (see Big-O Notation [prossimamente]), selection sort averages O[N2]: if you double the number of items in the list, the execution time will go up by about a factor of four.

Even selection sort, though, is much better than my all-time favorite sorting algorithms, the bogosort:

This silly sorting method relies on pure chance: it repeatedly applies a random shuffling of the array until the result happens to be sorted. With an average scaling of O[N×N!], (that’s N times N factorial) this should–quite obviously–never be used for any real computation.

Fortunately, Python contains built-in sorting algorithms that are much more efficient than either of the simplistic algorithms just shown. We’ll start by looking at the Python built-ins, and then take a look at the routines included in NumPy and optimized for NumPy arrays.

Ordinamenti veloci in NumPy: np.sort e np.argsort
Although Python has built-in sort and sorted functions to work with lists, we won’t discuss them here because NumPy’s np.sort function turns out to be much more efficient and useful for our purposes. By default np.sort uses an O[NlogN], quicksort algorithm, though mergesort and heapsort are also available. For most applications, the default quicksort is more than sufficient.

To return a sorted version of the array without modifying the input, you can use np.sort:

If you prefer to sort the array in-place, you can instead use the sort method of arrays:

A related function is argsort, which instead returns the indices of the sorted elements:

The first element of this result gives the index of the smallest element, the second value gives the index of the second smallest, and so on. These indices can then be used (via fancy indexing) to construct the sorted array if desired:

Ordinare per righe o colonne
A useful feature of NumPy’s sorting algorithms is the ability to sort along specific rows or columns of a multidimensional array using the axis argument. For example:

Keep in mind that this treats each row or column as an independent array, and any relationships between the row or column values will be lost!

Pausa 😊 poi continerà.

:mrgreen:

NumPy – 28 – indicizzazione fancy – 3

Continuo copiando qui.

Modificare valori con l’indirizzamento fancy
Just as fancy indexing can be used to access parts of an array, it can also be used to modify parts of an array. For example, imagine we have an array of indices and we’d like to set the corresponding items in an array to some value:

We can use any assignment-type operator for this. For example:

Notice, though, that repeated indices with these operations can cause some potentially unexpected results. Consider the following:

Where did the 4 go? The result of this operation is to first assign x[0] = 4, followed by x[0] = 6. The result, of course, is that x[0] contains the value 6.

Fair enough, but consider this operation:

You might expect that x[3] would contain the value 2, and x[3] would contain the value 3, as this is how many times each index is repeated. Why is this not the case? Conceptually, this is because x[i] += 1 is meant as a shorthand of x[i] = x[i] + 1. x[i] + 1 is evaluated, and then the result is assigned to the indices in x. With this in mind, it is not the augmentation that happens multiple times, but the assignment, which leads to the rather nonintuitive results.

So what if you want the other behavior where the operation is repeated? For this, you can use the at() method of ufuncs (available since NumPy 1.8), and do the following:

Esempio: suddividere dati
You can use these ideas to efficiently bin data to create a histogram by hand. For example, imagine we have 1,000 values and would like to quickly find where they fall within an array of bins. We could compute it using ufunc.at like this:

import numpy as np

np.random.seed(42)
x = np.random.randn(100)

# compute a histogram by hand
bins = np.linspace(-5, 5, 20)
counts = np.zeros_like(bins)

# find the appropriate bin for each x
i = np.searchsorted(bins, x)

# add 1 to each of these bins
np.add.at(counts, i, 1)

# plot the results
import matplotlib.pyplot as plt
plt.plot(bins, counts, linestyle='steps');
plt.show()

Of course, it would be silly to have to do this each time you want to plot a histogram. This is why Matplotlib provides the plt.hist() routine, which does the same in a single line:

plt.hist(x, bins, histtype='step')

This function will create a nearly identical plot to the one seen here. To compute the binning, matplotlib uses the np.histogram function, which does a very similar computation to what we did before. Let’s compare the two here:

Our own one-line algorithm is several times faster than the optimized algorithm in NumPy! How can this be? If you dig into the np.histogram source code (you can do this in IPython by typing np.histogram??), you’ll see that it’s quite a bit more involved than the simple search-and-count that we’ve done; this is because NumPy’s algorithm is more flexible, and particularly is designed for better performance when the number of data points becomes large:

What this comparison shows is that algorithmic efficiency is almost never a simple question. An algorithm efficient for large datasets will not always be the best choice for small datasets, and vice versa. But the advantage of coding this algorithm yourself is that with an understanding of these basic methods, you could use these building blocks to extend this to do some very interesting custom behaviors. The key to efficiently using Python in data-intensive applications is knowing about general convenience routines like np.histogram and when they’re appropriate, but also knowing how to make use of lower-level functionality when you need more pointed behavior.

:mrgreen:

Pythonisti über-nerds


Robe che si possono fare la domenica 😜
Per esempio oggi voglio raccontarvi di un covo di nerds –über– scoperto via repl.it.
Lì si trova il link a Skulpt, un pythonista, anzi tanti, come già detto. Tra l’altro si scopre da dove deriva il nome “skulpt”, facile, logico; come ho fatto a non pensarci. E si trova anche il “padre” di Skulpt, lasciato come esercizio.

Sempre da quelle parti c’è Yehonathan Sharvit, @viebel che dice che A turtle can draw fractals.

Come? con Python? Sì e sì 😄
Il codice si può copiare in un file ed eseguire nel solito modo ma si può fare anche nel browser con Ctrl-Enter sul codice.
È meraviglioso, un esempio. C’è una funzione, ricorsiva, che definisce 5 lambdas che costituiscono il programma. Non so se sono in una fase ‘strologica particolare ma mi sembra davvero bello. No, non sono invidioso, solo un pochino. Ma vorrei essere capace di fare cose simili 🍎
Yehonathan non è solo Python, se non fosse che la vita è troppo breve e non sono più giovincello…
Ma anche restando su Python un’altra chicca: Reverse Polish Evaluator.

Forse vale solo per i vecchi, quelli che usavano le calcolatrici HP prima che i ‘puters diventassero ubiqui 👽 ma –OK, come detto prima 👽

E –devo proprio, non ce la faccio a non farlo– voglio provarlo. Per vedere se mi ricordo bene –niente teoria si trova ontehtoobz solo un esempio. L’espressione

2 * (3 + 4) + 5 * 2 + 3 * 4 + 6

in notazione RPN diventa (se ricordo bene):

2 3 4 + * 5 2 * + 3 4 * + 6 +

verifico, questo è il file rpn.py:

ops = {
  "+": (lambda a, b: a + b),
  "-": (lambda a, b: a - b),
  "*": (lambda a, b: a * b),
  "/": (lambda a, b: a / b)
}

def eval(expression):
  tokens = expression.split()
  stack = []
  for token in tokens:
    if token in ops:
      arg2 = stack.pop()
      arg1 = stack.pop()
      result = ops[token](arg1, arg2)
      stack.append(result)
    else:
      stack.append(int(token))
  return stack.pop()

che eseguo così:

OK! 🚀 bravo Klipse!
Problema per me: ci sono ancora cose di Klipse, valutare se un altro post… forse… prossimamente… chissà… 🐙

:mrgreen:

NumPy – 27 – indicizzazione fancy – 2

Continuo da qui con gli indici fancy copiando qui.

Indici combinati
For even more powerful operations, fancy indexing can be combined with the other indexing schemes we’ve seen:

We can combine fancy and simple indices:

We can also combine fancy indexing with slicing:

And we can combine fancy indexing with masking:

All of these indexing options combined lead to a very flexible set of operations for accessing and modifying array values.

Esempio: selezionare punti random
One common use of fancy indexing is the selection of subsets of rows from a matrix. For example, we might have an N by D matrix representing N points in D dimensions, such as the following points drawn from a two-dimensional normal distribution (file srp.py):

import numpy as np

mean = [0, 0]
cov = [[1, 2],
       [2, 5]]
X = np.random.multivariate_normal(mean, cov, 100)
X.shape

import matplotlib.pyplot as plt
import seaborn; seaborn.set()  # for plot styling

plt.scatter(X[:, 0], X[:, 1]);

plt.show()

Sì, ho fatto qualche modifica al codice di Jake 😊

Let’s use fancy indexing to select 20 random points. We’ll do this by first choosing 20 random indices with no repeats, and use these indices to select a portion of the original array (srp-sel.py):

import numpy as np

mean = [0, 0]
cov = [[1, 2],
       [2, 5]]
X = np.random.multivariate_normal(mean, cov, 100)
X.shape

import matplotlib.pyplot as plt
import seaborn; seaborn.set()  # for plot styling

indices = np.random.choice(X.shape[0], 20, replace=False)

selection = X[indices]  # fancy indexing here
selection.shape

plt.scatter(X[:, 0], X[:, 1], alpha=0.3)
plt.scatter(selection[:, 0], selection[:, 1],
            facecolor='red', s=20);  #mod

plt.show()

Al solito qualche modifica 😊

This sort of strategy is often used to quickly partition datasets, as is often needed in train/test splitting for validation of statistical models, and in sampling approaches to answering statistical questions.

Pausa 😊 poi si continua.

:mrgreen:

NumPy – 26 – indicizzazione fancy – 1

Proseguo da qui, capitolo nuovo, qui.

Fancy non so come tradurlo, in questo contesto, lascio la versione originale.

In the previous  sections  [posts], we saw how to access and modify portions of arrays using simple indices (e.g., arr[0]), slices (e.g., arr[:5]), and Boolean masks (e.g., arr[arr > 0]). In this section, we’ll look at another style of array indexing, known as fancy indexing. Fancy indexing is like the simple indexing we’ve already seen, but we pass arrays of indices in place of single scalars. This allows us to very quickly access and modify complicated subsets of an array’s values.

Esplorare l’indirizzamento fancy
Fancy indexing is conceptually simple: it means passing an array of indices to access multiple array elements at once. For example, consider the following array:

Suppose we want to access three different elements. We could do it like this:

Alternatively, we can pass a single list or array of indices to obtain the same result:

When using fancy indexing, the shape of the result reflects the shape of the index arrays rather than the shape of the array being indexed:

Fancy indexing also works in multiple dimensions. Consider the following array:

Like with standard indexing, the first index refers to the row, and the second to the column:

Notice that the first value in the result is X[0, 2], the second is X[1, 1], and the third is X[2, 3]. The pairing of indices in fancy indexing follows all the broadcasting rules that were mentioned in Computation on Arrays: Broadcasting [qui]. So, for example, if we combine a column vector and a row vector within the indices, we get a two-dimensional result:

Here, each row value is matched with each column vector, exactly as we saw in broadcasting of arithmetic operations. For example:

It is always important to remember with fancy indexing that the return value reflects the broadcasted shape of the indices, rather than the shape of the array being indexed.

Pausa 😀 prossimamente l’applicazione di questa roba 😉

:mrgreen:

NumPy – 25 – Comparazioni, maschere e logica booleana – 2

Coninuo da qui continuando a copiare qui.

Arrays booleani come maschere
In the preceding section [post precedente, quello linkato] we looked at aggregates computed directly on Boolean arrays. A more powerful pattern is to use Boolean arrays as masks, to select particular subsets of the data themselves. Returning to our x array from before, suppose we want an array of all values in the array that are less than, say, 5:

We can obtain a Boolean array for this condition easily, as we’ve already seen:

Now to select these values from the array, we can simply index on this Boolean array; this is known as a masking operation:

What is returned is a one-dimensional array filled with all the values that meet this condition; in other words, all the values in positions at which the mask array is True.

We are then free do operate on these values as we wish. For example, we can compute some relevant statistics on our Seattle rain data. Lo faccio costruendo lo script rainydays.py e eseguendolo con python3 rainydays.py:

import numpy as np
import pandas as pd

# use pandas to extract rainfall inches as a NumPy array
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254  # 1/10mm -> inches
inches.shape

# construct a mask of all rainy days
rainy = (inches > 0)

# construct a mask of all summer days (June 21st is the 172nd day)
days = np.arange(365)
summer = (days > 172) & (days < 262)

print("Median precip on rainy days in 2014 (inches):   ",
      np.median(inches[rainy]))
print("Median precip on summer days in 2014 (inches):  ",
      np.median(inches[summer]))
print("Maximum precip on summer days in 2014 (inches): ",
      np.max(inches[summer]))
print("Median precip on non-summer rainy days (inches):",
      np.median(inches[rainy & ~summer]))

By combining Boolean operations, masking operations, and aggregates, we can very quickly answer these sorts of questions for our dataset.

Inoltre: usare le keywords and e or invece degli operatori & e |
One common point of confusion is the difference between the keywords and and or on one hand, and the operators & and | on the other hand. When would you use one versus the other?

The difference is this: and and or gauge the truth or falsehood of entire object, while & and | refer to bits within each object.

When you uae and or or, it’s equivalent to asking Python to treat the object as a single Boolean entity. In Python, all nonzero integers will evaluate as True. Thus:

When you use & and | on integers, the expression operates on the bits of the element, applying the and or the or to the individual bits making up the number:

Notice that the corresponding bits of the binary representation are compared in order to yield the result.

When you have an array of Boolean values in NumPy, this can be thought of as a string of bits where 1 = True and 0 = False, and the result of & and | operates similarly to above:

Using or on these arrays will try to evaluate the truth or falsehood of the entire array object, which is not a well-defined value:

Similarly, when doing a Boolean expression on a given array, you should use | or & rather than or or and:

Trying to evaluate the truth or falsehood of the entire array will give the same ValueError we saw previously:

So remember this: and and or perform a single Boolean evaluation on an entire object, while & and | perform multiple Boolean evaluations on the content (the individual bits or bytes) of an object. For Boolean NumPy arrays, the latter is nearly always the desired operation.

:mrgreen:

NumPy – 24 – Comparazioni, maschere e logica booleana – 1

Continuo da qui a copiare qui.

This section covers the use of Boolean masks to examine and manipulate values within NumPy arrays. Masking comes up when you want to extract, modify, count, or otherwise manipulate values in an array based on some criterion: for example, you might wish to count all values greater than a certain value, or perhaps remove all outliers that are above some threshold. In NumPy, Boolean masking is often the most efficient way to accomplish these types of tasks.

Esempio: contare i giorni di pioggia
Imagine you have a series of data that represents the amount of precipitation each day for a year in a given city. For example, here we’ll load the daily rainfall statistics for the city of Seattle in 2014, using Pandas (which is covered in more detail [prossimamente]):

Devo recuperare il file di dati questo, che metto nella sub-dir data.
Ho il solito problema con %inline di Matplotlib per cui creo lo script rain.py:

import numpy as np
import pandas as pd

# use pandas to extract rainfall inches as a NumPy array
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254  # 1/10mm -> inches
inches.shape

import matplotlib.pyplot as plt
import seaborn; seaborn.set()  # set plot styles

plt.hist(inches, 40);

plt.show()

The array contains 365 values, giving daily rainfall in inches from January 1 to December 31, 2014. Ecco le prime righe:

STATION,STATION_NAME,DATE,PRCP,SNWD,SNOW,TMAX,TMIN,AWND,WDF2,WDF5,WSF2,WSF5,WT01,WT05,WT02,WT03
GHCND:USW00024233,SEATTLE TACOMA INTERNATIONAL AIRPORT WA US,20140101,0,0,0,72,33,12,340,310,36,40,-9999,-9999,-9999,-9999
GHCND:USW00024233,SEATTLE TACOMA INTERNATIONAL AIRPORT WA US,20140102,41,0,0,106,61,32,190,200,94,116,-9999,-9999,-9999,-9999
GHCND:USW00024233,SEATTLE TACOMA INTERNATIONAL AIRPORT WA US,20140103,15,0,0,89,28,26,30,50,63,72,1,-9999,-9999,-9999
GHCND:USW00024233,SEATTLE TACOMA INTERNATIONAL AIRPORT WA US,20140104,0,0,0,78,6,27,40,40,45,58,1,-9999,-9999,-9999
GHCND:USW00024233,SEATTLE TACOMA INTERNATIONAL AIRPORT WA US,20140105,0,0,0,83,-5,37,10,10,67,76,-9999,-9999,-9999,-9999

As a first quick visualization, let’s look at the histogram of rainy days, which was generated using Matplotlib (we will explore this tool more fully [prossimamente]).

Eseguo lo script con python3 rain.py:

This histogram gives us a general idea of what the data looks like: despite its reputation, the vast majority of days in Seattle saw near zero measured rainfall in 2014. But this doesn’t do a good job of conveying some information we’d like to see: for example, how many rainy days were there in the year? What is the average precipitation on those rainy days? How many days were there with more than half an inch of rain?

Estrarre dati
One approach to this would be to answer these questions by hand: loop through the data, incrementing a counter each time we see values in some desired range. For reasons discussed throughout this chapter, such an approach is very inefficient, both from the standpoint of time writing code and time computing the result. We saw [qui] that NumPy’s ufuncs can be used in place of loops to do fast element-wise arithmetic operations on arrays; in the same way, we can use other ufuncs to do element-wise comparisons over arrays, and we can then manipulate the results to answer the questions we have. We’ll leave the data aside for right now, and discuss some general tools in NumPy to use masking to quickly answer these types of questions.

Comparare gli operatori come ufuncs
[Nel post indicato] we introduced ufuncs, and focused in particular on arithmetic operators. We saw that using +, -, *, /, and others on arrays leads to element-wise operations. NumPy also implements comparison operators such as < (less than) and > (greater than) as element-wise ufuncs. The result of these comparison operators is always an array with a Boolean data type. All six of the standard comparison operations are available:

It is also possible to do an element-wise comparison of two arrays, and to include compound expressions:

As in the case of arithmetic operators, the comparison operators are implemented as ufuncs in NumPy; for example, when you write x < 3, internally NumPy uses np.less(x, 3). A summary of the comparison operators and their equivalent ufunc is shown here:

Op. Eq. ufunc
==  np.equal 
!=  np.not_equal
<  np.less 		
<= np.less_equal 
>  np.greater 		
>= np.greater_equal

Just as in the case of arithmetic ufuncs, these will work on arrays of any size and shape. Here is a two-dimensional example:

Lavorare con arrays booleani
Given a Boolean array, there are a host of useful operations you can do. We’ll work with x, the two-dimensional array we created earlier.

Contare i dati
To count the number of True entries in a Boolean array, np.count_nonzero is useful:

We see that there are eight array entries that are less than 6. Another way to get at this information is to use np.sum; in this case, False is interpreted as 0, and True is interpreted as 1:

The benefit of sum() is that like with other NumPy aggregation functions, this summation can be done along rows or columns as well:

This counts the number of values less than 6 in each row of the matrix.
If we’re interested in quickly checking whether any or all the values are true, we can use (you guessed it) np.any or np.all:

np.all and np.any can be used along particular axes as well. For example:

Here all the elements in the first and third rows are less than 8, while this is not the case for the second row.

Finally, a quick warning: as mentioned [qui], Python has built-in sum(), any(), and all() functions. These have a different syntax than the NumPy versions, and in particular will fail or produce unintended results when used on multidimensional arrays. Be sure that you are using np.sum(), np.any(), and np.all() for these examples!

Operatori booleani
We’ve already seen how we might count, say, all days with rain less than four inches, or all days with rain greater than two inches. But what if we want to know about all days with rain less than four inches and greater than one inch? This is accomplished through Python’s bitwise logic operators, &, |, ^, and ~. Like with the standard arithmetic operators, NumPy overloads these as ufuncs which work element-wise on (usually Boolean) arrays.

For example, we can address this sort of compound question as follows:

So we see that there are 29 days with rainfall between 0.5 and 1.0 inches.

Note that the parentheses here are important–because of operator precedence rules, with parentheses removed this expression would be evaluated as follows, which results in an error: inches > (0.5 & inches) < 1. Using the equivalence of A AND B and NOT (NOT A OR NOT B) we can compute the same result in a different manner:

Combining comparison operators and Boolean operators on arrays can lead to a wide range of efficient logical operations.

The following table summarizes the bitwise Boolean operators and their equivalent ufuncs:

Op. Eq. ufunc
&   np.bitwise_and
|   np.bitwise_or
^   np.bitwise_xor 		
~   np.bitwise_not

Using these tools, we might start to answer the types of questions we have about our weather data. Here are some examples of results we can compute when combining masking with aggregations:

😯 il post sta diventando troppo lungo; continua prossimamente 😉

:mrgreen:

NumPy – 23 – Calcoli con arrays: broadcasting – 3

Continuo da qui a copiare qui.

Broadcasting in pratica
Broadcasting operations form the core of many examples we’ll see throughout this book. We’ll now take a look at a couple simple examples of where they can be useful.

Centrare un array
In the previous section [posts], we saw that ufuncs allow a NumPy user to remove the need to explicitly write slow Python loops. Broadcasting extends this ability. One commonly seen example is when centering an array of data. Imagine you have an array of 10 observations, each of which consists of 3 values. Using the standard convention (see Data Representation in Scikit-Learn [prossimamente]), we’ll store this in a 10×3 array:


We can compute the mean of each feature using the mean aggregate across the first dimension:

And now we can center the X array by subtracting the mean (this is a broadcasting operation):

To double-check that we’ve done this correctly, we can check that the centered array has near zero mean:

To within machine precision, the mean is now zero.

Grafico di una funzione bidimensionale
One place that broadcasting is very useful is in displaying images based on two-dimensional functions. If we want to define a function z = f(x, y), broadcasting can be used to compute the function across the grid:

We’ll use Matplotlib to plot this two-dimensional array (these tools will be discussed in full in Density and Contour Plots [prossimamente]):

Non sono riuscito a far funzionare Matplotlib con %inline, creo lo script esterno:

import numpy as np

x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 50)[:, np.newaxis]
z = np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)

import matplotlib.pyplot as plt

plt.imshow(z, origin='lower', extent=[0, 5, 0, 5], cmap='viridis')
plt.colorbar();
plt.show()

che eseguo con python3 fz.py ottenendo

The result is a compelling visualization of the two-dimensional function.

OK 😄 pronto per il prossimo capitolo.

:mrgreen:

NumPy – 22 – Calcoli con arrays: broadcasting – 2

f0m

Continuo da qui copiando qui.

Regole del broadcasting
Broadcasting in NumPy follows a strict set of rules to determine the interaction between the two arrays:

  • Rule 1: If the two arrays differ in their number of dimensions, the shape of the one with fewer dimensions is padded with ones on its leading (left) side.
  • Rule 2: If the shape of the two arrays does not match in any dimension, the array with shape equal to 1 in that dimension is stretched to match the other shape.
  • Rule 3: If in any dimension the sizes disagree and neither is equal to 1, an error is raised.

To make these rules clear, let’s consider a few examples in detail.

Esempio #1
Let’s look at adding a two-dimensional array to a one-dimensional array:

np146

Let’s consider an operation on these two arrays. The shape of the arrays are

    M.shape = (2, 3)
    a.shape = (3,)

We see by rule 1 that the array a has fewer dimensions, so we pad it on the left with ones:

    M.shape -> (2, 3)
    a.shape -> (1, 3)

By rule 2, we now see that the first dimension disagrees, so we stretch this dimension to match:

    M.shape -> (2, 3)
    a.shape -> (2, 3)

The shapes match, and we see that the final shape will be (2, 3):

np147

Esempio #2
Let’s take a look at an example where both arrays need to be broadcast:

np148

Again, we’ll start by writing out the shape of the arrays:

    a.shape = (3, 1)
    b.shape = (3,)

Rule 1 says we must pad the shape of b with ones:

    a.shape -> (3, 1)
    b.shape -> (1, 3)

And rule 2 tells us that we upgrade each of these ones to match the corresponding size of the other array:

    a.shape -> (3, 3)
    b.shape -> (3, 3)

Because the result matches, these shapes are compatible. We can see this here:

np149

Esempio #3
Now let’s take a look at an example in which the two arrays are not compatible:

np150

This is just a slightly different situation than in the first example: the matrix M is transposed. How does this affect the calculation? The shape of the arrays are

    M.shape = (3, 2)
    a.shape = (3,)

Again, rule 1 tells us that we must pad the shape of a with ones:

    M.shape -> (3, 2)
    a.shape -> (1, 3)

By rule 2, the first dimension of a is stretched to match that of M:

    M.shape -> (3, 2)
    a.shape -> (3, 3)

Now we hit rule 3–the final shapes do not match, so these two arrays are incompatible, as we can observe by attempting this operation:

np151

Note the potential confusion here: you could imagine making a and M compatible by, say, padding a’s shape with ones on the right rather than the left. But this is not how the broadcasting rules work! That sort of flexibility might be useful in some cases, but it would lead to potential areas of ambiguity. If right-side padding is what you’d like, you can do this explicitly by reshaping the array (we’ll use the np.newaxis keyword introduced in The Basics of NumPy Arrays, [qui]):

np152

Also note that while we’ve been focusing on the + operator here, these broadcasting rules apply to any binary ufunc. For example, here is the logaddexp(a, b) function, which computes log(exp(a) + exp(b)) with more precision than the naive approach:

np153

For more information on the many available universal functions, refer to Computation on NumPy Arrays: Universal Functions, [qui].

:mrgreen:

NumPy – 21 – Calcoli con arrays: broadcasting – 1

c53

Continuo da qui per un nuovo capitolo, qui.

We saw in the previous section how NumPy’s universal functions can be used to vectorize operations and thereby remove slow Python loops. Another means of vectorizing operations is to use NumPy’s broadcasting functionality. Broadcasting is simply a set of rules for applying binary ufuncs (e.g., addition, subtraction, multiplication, etc.) on arrays of different sizes.

Introduzione di broadcasting
Forse c’è un equivalente italiano ma non lo so; a me nessuno dice mai niente (auto-cit.) 😜
Recall that for arrays of the same size, binary operations are performed on an element-by-element basis:

np141

Broadcasting allows these types of binary operations to be performed on arrays of different sizes–for example, we can just as easily add a scalar (think of it as a zero-dimensional array) to an array:

np142

We can think of this as an operation that stretches or duplicates the value 5 into the array [5, 5, 5], and adds the results. The advantage of NumPy’s broadcasting is that this duplication of values does not actually take place, but it is a useful mental model as we think about broadcasting.

We can similarly extend this to arrays of higher dimension. Observe the result when we add a one-dimensional array to a two-dimensional array:

np143

Here the one-dimensional array a is stretched, or broadcast across the second dimension in order to match the shape of M.

While these examples are relatively easy to understand, more complicated cases can involve broadcasting of both arrays. Consider the following example:

np144

Just as before we stretched or broadcasted one value to match the shape of the other, here we’ve stretched both a and b to match a common shape, and the result is a two-dimensional array! The geometry of these examples is visualized in the following figure (Code to produce this plot can be found in the appendix, and is adapted from source published in the astroML documentation).

np145

The light boxes represent the broadcasted values: again, this extra memory is not actually allocated in the course of the operation, but it can be useful conceptually to imagine that it is.

Continua 😉

:mrgreen: