numpy

Es un módulo Python para el manejo de arrays (vectores) y matrices. El componente esencial es el tipo array. Este notebook es un resumen de http://goo.gl/fiLYpb.

Cuando uses numpy/matplotlib en IPython, evitar usar pylab


In [1]:
import numpy as np
a1 = np.array([])
a1


Out[1]:
array([], dtype=float64)

A diferencia de la lista estándar de Python, todos los elementos de un array deben ser del mismo tipo.


In [2]:
a1.dtype


Out[2]:
dtype('float64')

Pero se puede convertir a otro:


In [3]:
aux = a1.astype(int)
print(aux.dtype)
print(aux)


int64
[]

Para especificar el tipo de los datos se pueden utilizar los tipos de Python, sus nombres como cadenas o variables definidas en el módulo numpy:


In [4]:
np.array([1], dtype=int)
np.array([1], dtype='float64')
np.array([1], dtype=np.complex128)


Out[4]:
array([ 1.+0.j])

Los operadores están sobrecargados con su semántica matemática:


In [5]:
a1 = np.array([1, 2, 3])

print(a1 + [4, 5, 6])
print(a1 * 3)
print(a1 * [4, 5, 6])
print(a1 >= 2)


[5 7 9]
[3 6 9]
[ 4 10 18]
[False  True  True]

Y además son multidimencionales:


In [6]:
a2 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
a2


Out[6]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [7]:
print("El array tiene {} dimensiones".format(a2.ndim))
print("Su tamaño es {0[0]} filas y {0[1]} columnas".format(a2.shape))


El array tiene 2 dimensiones
Su tamaño es 3 filas y 4 columnas

Es posible ver un array con un estructura diferente. Es importante señalar que esta operación no crea un array nuevo ni mueve los datos, solo los accede de un modo diferente.


In [8]:
a3 = np.array(range(8))
a3


Out[8]:
array([0, 1, 2, 3, 4, 5, 6, 7])

In [9]:
a3.reshape(2, 4)


Out[9]:
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

In [10]:
x = np.arange(1,5).reshape(2,2)
y = np.arange(5,9).reshape(2,2)
print(x * y)
print(np.dot(x, y))


[[ 5 12]
 [21 32]]
[[19 22]
 [43 50]]

Construir arrays

La función arange es similar al range estándar, pero devuelve un numpy.array.


In [11]:
np.arange(10)


Out[11]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

La función linspace() es similar, pero es más adecuada para valores flotantes. Se puede indicar la cantidad de valores a crear entre los límites indicados.


In [12]:
x = np.linspace(0, 10, num=20)
x


Out[12]:
array([  0.        ,   0.52631579,   1.05263158,   1.57894737,
         2.10526316,   2.63157895,   3.15789474,   3.68421053,
         4.21052632,   4.73684211,   5.26315789,   5.78947368,
         6.31578947,   6.84210526,   7.36842105,   7.89473684,
         8.42105263,   8.94736842,   9.47368421,  10.        ])

In [13]:
np.zeros((2,3))


Out[13]:
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

In [14]:
np.ones(6)


Out[14]:
array([ 1.,  1.,  1.,  1.,  1.,  1.])

In [15]:
np.identity(5)


Out[15]:
array([[ 1.,  0.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  1.]])

In [16]:
np.diag([1, 2, 3])


Out[16]:
array([[1, 0, 0],
       [0, 2, 0],
       [0, 0, 3]])

In [17]:
np.random.rand(5)


Out[17]:
array([ 0.01921827,  0.93944384,  0.68174967,  0.04461026,  0.64737613])

In [18]:
np.random.rand(3, 4)


Out[18]:
array([[ 0.85878249,  0.38752053,  0.49107638,  0.67692526],
       [ 0.53540974,  0.31223857,  0.62396065,  0.2384783 ],
       [ 0.18899872,  0.57617881,  0.89084486,  0.80863298]])

In [19]:
np.empty((2,3))


Out[19]:
array([[  6.93918287e-310,   6.93918287e-310,   0.00000000e+000],
       [  1.27319747e-313,   1.27319747e-313,   1.27319747e-313]])

Las funciones matemáticas de numpy aceptan y devuelven arrays en lugar de valores escalares.


In [20]:
sin_x = np.sin(x)
print(sin_x)

abs_sin_x = abs(sin_x)
print(abs_sin_x)


[ 0.          0.50235115  0.86872962  0.99996678  0.86054034  0.48818921
 -0.01630136 -0.5163796  -0.87668803 -0.99970104 -0.85212237 -0.47389753
  0.03259839  0.53027082  0.88441346  0.99916962  0.84347795  0.4594799
 -0.04888676 -0.54402111]
[ 0.          0.50235115  0.86872962  0.99996678  0.86054034  0.48818921
  0.01630136  0.5163796   0.87668803  0.99970104  0.85212237  0.47389753
  0.03259839  0.53027082  0.88441346  0.99916962  0.84347795  0.4594799
  0.04888676  0.54402111]

Por tanto, linspace nos da un espacio unidimensional discreto. mgrid ofrece algo parecido para un espacio bidimensional. Devuelve dos arrays bidimensionales, uno creciente por columnas y otro por filas.


In [21]:
a_2D = np.mgrid[0:5,0:5]
a_2D


Out[21]:
array([[[0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1],
        [2, 2, 2, 2, 2],
        [3, 3, 3, 3, 3],
        [4, 4, 4, 4, 4]],

       [[0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4]]])

In [22]:
x, y = a_2D
z = (x+y) ** 2
z


Out[22]:
array([[ 0,  1,  4,  9, 16],
       [ 1,  4,  9, 16, 25],
       [ 4,  9, 16, 25, 36],
       [ 9, 16, 25, 36, 49],
       [16, 25, 36, 49, 64]])

Indexación


In [23]:
a2


Out[23]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [24]:
a2[1,2]


Out[24]:
7

In [25]:
a2[:,2]


Out[25]:
array([ 3,  7, 11])

In [26]:
a2[1,1:]


Out[26]:
array([6, 7, 8])

In [27]:
a4d = np.arange(256).reshape(4,4,4,-1)
a4d[1,:,:,2]


Out[27]:
array([[ 66,  70,  74,  78],
       [ 82,  86,  90,  94],
       [ 98, 102, 106, 110],
       [114, 118, 122, 126]])

Es posible indexar un array usando una lista:


In [28]:
a = np.array(list(reversed(range(20))))
indexes = [2, 4, 8, 12]
a[indexes]


Out[28]:
array([17, 15, 11,  7])

También con un array de booleanos:


In [69]:
a = np.arange(20)
indexes = np.array([i > 16 or not i%3 for i in a], dtype=bool)
indexes = np.array([True] * 15)
print(indexes)
a[indexes]


[ True  True  True  True  True  True  True  True  True  True  True  True
  True  True  True]
Out[69]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

Y estas indexación se pueden usar en una asignación múltiple:


In [30]:
a[indexes] = -1
a


Out[30]:
array([-1,  1,  2, -1,  4,  5, -1,  7,  8, -1, 10, 11, -1, 13, 14, -1, 16,
       -1, -1, -1])

Iteración

Un array de dos dimensiones se itera por filas:


In [31]:
a25 = np.arange(25).reshape(5, 5)
for row in a25:
    print(row)


[0 1 2 3 4]
[5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]

Se puede iterar como un array plano (el array no es modificado):


In [32]:
for x in a25.flat:
    print(x)


0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Vistas

Los array's son vistas de otros arrays (a menos que sean copias explícitas):


In [70]:
b = a2.copy()

b1 = b[:,2]
print(b1)

b1[2]= 3000
print(b)

b[1,:] = 666
print(b)

print(b1)


[ 3  7 11]
[[   1    2    3    4]
 [   5    6    7    8]
 [   9   10 3000   12]]
[[   1    2    3    4]
 [ 666  666  666  666]
 [   9   10 3000   12]]
[   3  666 3000]

In [34]:
a2


Out[34]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

Añadir elementos crea un array nuevo:


In [35]:
np.append(a2, 3)


Out[35]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,  3])

In [36]:
a2


Out[36]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

Se puede crear una vista de un array completo:


In [37]:
aux = a2.view()
aux.shape = 2,6
print(aux)
print(a2)


[[ 1  2  3  4  5  6]
 [ 7  8  9 10 11 12]]
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]

Interface de la clase array


In [38]:
a2


Out[38]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [39]:
a2.flags


Out[39]:
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

In [40]:
for attr in ['shape', 'strides', 'ndim', 'size', 'dtype', 'itemsize', 'nbytes']:
    print("- {:10} {}".format(attr, getattr(a2, attr)))


- shape      (3, 4)
- strides    (32, 8)
- ndim       2
- size       12
- dtype      int64
- itemsize   8
- nbytes     96

In [41]:
a2.item((1,0))


Out[41]:
5

In [42]:
a2.tolist()


Out[42]:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

In [43]:
a2.tostring()


Out[43]:
'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00'

In [44]:
a2.copy()


Out[44]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [45]:
a2.view(dtype=np.int16)


Out[45]:
array([[ 1,  0,  0,  0,  2,  0,  0,  0,  3,  0,  0,  0,  4,  0,  0,  0],
       [ 5,  0,  0,  0,  6,  0,  0,  0,  7,  0,  0,  0,  8,  0,  0,  0],
       [ 9,  0,  0,  0, 10,  0,  0,  0, 11,  0,  0,  0, 12,  0,  0,  0]], dtype=int16)

In [46]:
a2.fill(1)
a2


Out[46]:
array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]])

In [47]:
a2 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
a2.transpose()


Out[47]:
array([[ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11],
       [ 4,  8, 12]])

In [48]:
aux = a2.copy()
aux.resize(5,5)
aux


Out[48]:
array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12,  0,  0,  0],
       [ 0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0]])

In [49]:
a2


Out[49]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [50]:
aux = np.array([[3, 2], [1, 6]])
aux.sort(axis=1)
aux


Out[50]:
array([[2, 3],
       [1, 6]])

In [51]:
aux.sort(axis=0)
aux


Out[51]:
array([[1, 3],
       [2, 6]])

In [52]:
a2.diagonal()


Out[52]:
array([ 1,  6, 11])

Operaciones como métodos


In [53]:
a2


Out[53]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [54]:
print(a2.min())
print(a2.max())
print(a2.sum())
print(a2.mean())
print(a2.var())

print(a2.sum(axis=0))
print(a2.sum(axis=1))


1
12
78
6.5
11.9166666667
[15 18 21 24]
[10 26 42]

Resolver sistema de ecuaciones


In [55]:
from numpy.linalg import solve

# The system of equations we want to solve for (x0,x1,x2):
# 3 * x0 + 1 * x1 + 5 * x2 = 6
# 1 * x0 + 8 * x2 = 7
# 2 * x0 + 1 * x1 + 4 * x2 = 8

a = np.array([[3, 1, 5], [1, 0, 8],[2, 1, 4]])
b = np.array([6, 7, 8])
solution = solve(a, b)
print(solution)

print(np.dot(a, solution)) # Just checking if we indeed obtain the righthand side


[-3.28571429  9.42857143  1.28571429]
[ 6.  7.  8.]

grid

Usando mesh-grids, como con mgrid y ogrid.


In [56]:
a = np.arange(4)
b = np.array([[x] for x in range(4)])
b = a.reshape(4,1)
print(a)
print(b)
a * b


[0 1 2 3]
[[0]
 [1]
 [2]
 [3]]
Out[56]:
array([[0, 0, 0, 0],
       [0, 1, 2, 3],
       [0, 2, 4, 6],
       [0, 3, 6, 9]])

Cargar y guardar np.array


In [57]:
np.fromstring("1 2 3 4 5", sep=" ", dtype=int)


Out[57]:
array([1, 2, 3, 4, 5])

In [58]:
a2 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

In [59]:
np.array2string(a2)


Out[59]:
'[[ 1  2  3  4]\n [ 5  6  7  8]\n [ 9 10 11 12]]'

In [60]:
with file('tmp/array', 'w') as fd:
    a2.tofile(fd)
    
fd = file('tmp/array')
np.fromfile(fd, np.int64)
fd.close()


Out[60]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

In [61]:
!hexdump tmp/array


0000000 0001 0000 0000 0000 0002 0000 0000 0000
0000010 0003 0000 0000 0000 0004 0000 0000 0000
0000020 0005 0000 0000 0000 0006 0000 0000 0000
0000030 0007 0000 0000 0000 0008 0000 0000 0000
0000040 0009 0000 0000 0000 000a 0000 0000 0000
0000050 000b 0000 0000 0000 000c 0000 0000 0000
0000060

In [62]:
with file('tmp/array_pickle', 'w') as fd:
    a2.dump(fd)
    
np.load(file('tmp/array_pickle'))


Out[62]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

In [63]:
!hexdump -C tmp/array_pickle


00000000  80 02 63 6e 75 6d 70 79  2e 63 6f 72 65 2e 6d 75  |..cnumpy.core.mu|
00000010  6c 74 69 61 72 72 61 79  0a 5f 72 65 63 6f 6e 73  |ltiarray._recons|
00000020  74 72 75 63 74 0a 71 01  63 6e 75 6d 70 79 0a 6e  |truct.q.cnumpy.n|
00000030  64 61 72 72 61 79 0a 71  02 4b 00 85 55 01 62 87  |darray.q.K..U.b.|
00000040  52 71 03 28 4b 01 4b 03  4b 04 86 63 6e 75 6d 70  |Rq.(K.K.K..cnump|
00000050  79 0a 64 74 79 70 65 0a  71 04 55 02 69 38 4b 00  |y.dtype.q.U.i8K.|
00000060  4b 01 87 52 71 05 28 4b  03 55 01 3c 4e 4e 4e 4a  |K..Rq.(K.U.<NNNJ|
00000070  ff ff ff ff 4a ff ff ff  ff 4b 00 74 62 89 55 60  |....J....K.tb.U`|
00000080  01 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00000090  03 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
000000a0  05 00 00 00 00 00 00 00  06 00 00 00 00 00 00 00  |................|
000000b0  07 00 00 00 00 00 00 00  08 00 00 00 00 00 00 00  |................|
000000c0  09 00 00 00 00 00 00 00  0a 00 00 00 00 00 00 00  |................|
000000d0  0b 00 00 00 00 00 00 00  0c 00 00 00 00 00 00 00  |................|
000000e0  74 62 2e                                          |tb.|
000000e3

In [64]:
!head -n8 graphs/dodecahedral.edgelist


0 1
0 10
0 19
1 8
1 2
2 3
2 6
3 19

In [65]:
np.loadtxt('graphs/dodecahedral.edgelist')[:8,:]


Out[65]:
array([[  0.,   1.],
       [  0.,  10.],
       [  0.,  19.],
       [  1.,   8.],
       [  1.,   2.],
       [  2.,   3.],
       [  2.,   6.],
       [  3.,  19.]])

Cargar datos de un CSV a partir de una URL


In [73]:
# url = 'https://gist.github.com/chriddyp/8818473/raw/d8c73ff66a190a84eb8c6c19df4d8865673234ca/2007gapminder.csv'
url = 'http://arco.esi.uclm.es/~david.villa/puff/2007gapminder.csv'
data = np.genfromtxt(url, delimiter=',', dtype=None, names=True)
print("ndim: {}".format(data.ndim))
print("shape: {}".format(data.shape))
print("dtype: {}".format(data.dtype))


ndim: 1
shape: (142,)
dtype: [('Country', 'S24'), ('Population', '<i8'), ('Continent', 'S8'), ('lifeExp', '<f8'), ('gdpPercap', '<f8')]

In [74]:
data[:10]


Out[74]:
array([('Afghanistan', 31889923, 'Asia', 43.828, 974.5803384),
       ('Albania', 3600523, 'Europe', 76.423, 5937.029526),
       ('Algeria', 33333216, 'Africa', 72.301, 6223.367465),
       ('Angola', 12420476, 'Africa', 42.731, 4797.231267),
       ('Argentina', 40301927, 'Americas', 75.32, 12779.37964),
       ('Australia', 20434176, 'Oceania', 81.235, 34435.36744),
       ('Austria', 8199783, 'Europe', 79.829, 36126.4927),
       ('Bahrain', 708573, 'Asia', 75.635, 29796.04834),
       ('Bangladesh', 150448339, 'Asia', 64.062, 1391.253792),
       ('Belgium', 10392226, 'Europe', 79.441, 33692.60508)], 
      dtype=[('Country', 'S24'), ('Population', '<i8'), ('Continent', 'S8'), ('lifeExp', '<f8'), ('gdpPercap', '<f8')])

In [75]:
data['Country'][:30]


Out[75]:
array(['Afghanistan', 'Albania', 'Algeria', 'Angola', 'Argentina',
       'Australia', 'Austria', 'Bahrain', 'Bangladesh', 'Belgium', 'Benin',
       'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil',
       'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon',
       'Canada', 'Central African Republic', 'Chad', 'Chile', 'China',
       'Colombia', 'Comoros', 'Congo Dem. Rep.', 'Congo Rep.', 'Costa Rica'], 
      dtype='|S24')

Media de la rentas per cápita de cada pais:


In [78]:
np.mean(data['gdpPercap'])


Out[78]:
11680.071819878172

Media de renta per cápita en Europa


In [79]:
europe_index = data['Continent'] == 'Europe'
europe_countries = data[europe_index]
europe_countries['Country']


Out[79]:
array(['Albania', 'Austria', 'Belgium', 'Bosnia and Herzegovina',
       'Bulgaria', 'Croatia', 'Czech Republic', 'Denmark', 'Finland',
       'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland',
       'Italy', 'Montenegro', 'Netherlands', 'Norway', 'Poland',
       'Portugal', 'Romania', 'Serbia', 'Slovak Republic', 'Slovenia',
       'Spain', 'Sweden', 'Switzerland', 'Turkey', 'United Kingdom'], 
      dtype='|S24')

In [80]:
np.mean(europe_countries['gdpPercap'])


Out[80]:
25054.481635933331

Población mundial


In [81]:
population = data['Population']
np.sum(population)


Out[81]:
6251013179

Población media (por páis):


In [82]:
int(np.mean(population))


Out[82]:
44021219

Renta per cápita media mundial


In [77]:
pib = np.sum(data['Population'] * data['gdpPercap'])
pob_total = np.sum(data['Population'])
print(pib/pob_total)


9295.98659448

Desviación típica de población:


In [83]:
np.std(population)


Out[83]:
147100685.88281167

País con mayor población:


In [84]:
data[np.argmax(population)][0]


Out[84]:
'China'

Los cinco países con mayor renta per cápita:


In [89]:
percap_sorted = np.sort(data, order='gdpPercap')
percap_sorted[-5:][::-1]


Out[89]:
array([('Norway', 4627926, 'Europe', 80.196, 49357.19017),
       ('Kuwait', 2505559, 'Asia', 77.588, 47306.98978),
       ('Singapore', 4553009, 'Asia', 79.972, 47143.17964),
       ('United States', 301139947, 'Americas', 78.242, 42951.65309),
       ('Ireland', 4109086, 'Europe', 78.885, 40675.99635)], 
      dtype=[('Country', 'S24'), ('Population', '<i8'), ('Continent', 'S8'), ('lifeExp', '<f8'), ('gdpPercap', '<f8')])

PIB de todos los países:


In [104]:
import numpy.lib.recfunctions as rfn

pibs = population * data['gdpPercap']
arrays = data['Country'], pibs
country_pibs = rfn.merge_arrays(arrays, flatten = True, usemask = False)
country_pibs[:20]


Out[104]:
array([('Afghanistan', 31079291948.889942), ('Albania', 21376411360.0421),
       ('Algeria', 207444851958.21744), ('Angola', 59583895818.22309),
       ('Argentina', 515033625356.5662), ('Australia', 703658358893.6295),
       ('Austria', 296229400691.0841), ('Bahrain', 21112675360.41882),
       ('Bangladesh', 209311822133.85147), ('Belgium', 350141166520.1081),
       ('Benin', 11643151767.544123), ('Bolivia', 34854649033.83277),
       ('Bosnia and Herzegovina', 33897026518.41899),
       ('Botswana', 20603633701.61187), ('Brazil', 1722598680331.3838),
       ('Bulgaria', 78213929148.27957),
       ('Burkina Faso', 17435461729.74178), ('Burundi', 3608510288.223258),
       ('Cambodia', 24218877033.97859), ('Cameroon', 36137515700.94532)], 
      dtype=[('f0', 'S24'), ('f1', '<f8')])

Los cinco paises con el PIB más alto


In [111]:
np.sort(country_pibs, order='f1')[-5:][::-1]


Out[111]:
array([('United States', 12934458535084.986), ('China', 6539500929092.309),
       ('Japan', 4035134797102.1743), ('India', 2722925438772.817),
       ('Germany', 2650870893900.9224)], 
      dtype=[('f0', 'S24'), ('f1', '<f8')])