Revisão: Listas, Dicionários, Arrays

Este notebook tem o objetivo de revisar os conceitos básicos dos tipos list, dict e numpy.array da linguagem de programação Python, assim como destacar as principais diferenças entre eles.

Tipo list:

O tipo list é definido como um recipiente de valores de diversos tipos, ele pode conter qualquer tipo de dados do Python e, inclusive, mistura de tipos:


In [1]:
lista1 = [1, 2, 3, 4, 5]
lista2 = ['um', 'dois', 'três', 'quatro', 'cinco']
lista3 = [1, 'dois', 3.0, 4, 'cinco']

print('lista1 é uma lista apenas do tipo int: ', lista1)
print('lista2 é uma lista apenas do tipo str: ', lista2)
print('lista3 é uma lista apenas contendo variáveis de tipos int, str e float: ', lista3)


lista1 é uma lista apenas do tipo int:  [1, 2, 3, 4, 5]
lista2 é uma lista apenas do tipo str:  ['um', 'dois', 'três', 'quatro', 'cinco']
lista3 é uma lista apenas contendo variáveis de tipos int, str e float:  [1, 'dois', 3.0, 4, 'cinco']

Para acessar elementos de uma lista utilizamos [indice], com indice sendo um inteiro representando a posição que se encontra o elemento.

Nota: o primeiro índice de uma lista é 0.


In [2]:
print('O primeiro elemento de lista1 é : ', lista1[0])
print('O quarto elemento de lista1 é : ', lista1[3])


O primeiro elemento de lista1 é :  1
O quarto elemento de lista1 é :  4

Também podemos especificar índices negativos para acessar elementos da lista, só que contando de trás para frente:


In [3]:
print('O último elemento da lista1 é: ', lista1[-1])
print('O penúltimo elemento da lista1 é: ', lista1[-2])


O último elemento da lista1 é:  5
O penúltimo elemento da lista1 é:  4

Podemos também acessar um pedaço da lista especificando faixas de valores no seguinte formato:

lista[ pos_inicial : pos_final : passo ]

onde, pos_inicial é a primeira posição da faixa, pos_final é um elemento após a última posição da faixa e passo representa de quanto em quanto queremos pegar os elementos.

Se omitirmos um desses valores o valor padrão é primeiro elemento, último elemento, passo 1, respectivamente.


In [7]:
print('Do segundo ao quarto elemento de 1 em 1: ', lista1[1:4:1])
print('Do segundo até o final de 2 em 2: ', lista1[1::2])
print('Do primeiro elemento até o final de 3 em 3: ', lista1[::3])
print('Do primeiro elemento até o final de trás para frente: ', lista1[::-1])


Do segundo ao quarto elemento de 1 em 1:  [2, 3, 4]
Do segundo até o final de 2 em 2:  [2, 4]
Do primeiro elemento até o final de 3 em 3:  [1, 4]
Do primeiro elemento até o final de trás para frente:  [5, 4, 3, 2, 1]

Para alterar o conteúdo de um elemento da lista basta utilizarmos o operador =:

lista[ indice ] = novo_valor

onde indice é a posição que queremos alterar e novo_valor é o novo valor a ser atribuído nessa posição da lista.

Também podemos calcular o novo valor baseado no valor original:

lista[ indice ] = lista[ indice ] + var
lista[ indice ] = lista[ indice ] * var

onde var pode ser um valor numérico ou uma variável contendo um valor numérico.


In [10]:
lista1[1] = 5
print('O novo valor da segunda posição é: ', lista1[1])

lista1[1] = lista1[1]*2
print('A segunda posição teve seu valor dobrado: ', lista1[1])

lista1[1] = lista1[0] + lista1[2]
print('A segunda posição agora é a soma da posição anterior e da posterior: ', lista1[1])


O novo valor da segunda posição é:  5
A segunda posição teve seu valor dobrado:  10
A segunda posição agora é a soma da posição anterior e da posterior:  4

Para criar uma lista podemos utilizar o comando for em conjunto com o comando append:

lista = []
for i in range(1,101):
    lista.append(i)

lê-se: inicia uma lista vazia; para i na faixa de $1$ até $101$ (não-inclusivo), adiciona o valor de i ao final da lista.


In [11]:
lista = []
for i in range(1,101):
    lista.append(i)

print(lista)


[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

Podemos combinar o append com expressões matemáticas ou funções para criar listas como:

$$\{ x^2 \mid \forall x \in [1,101[ \}$$

$$\{ f(x) \mid \forall x \in [1,101[ \}$$


In [14]:
def f(x):
    return x**2 - 3*x

lista1 = []
lista2 = []
for x in range(1,101):
    lista1.append( x**2 ) # lista1 é composta de i elevado ao quadrado
    lista2.append( f(x) ) # lista2 é composta de f(i) para todo i pertencente a 

print(lista1)
print()
print(lista2)


[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356, 4489, 4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724, 6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409, 9604, 9801, 10000]

[-2, -2, 0, 4, 10, 18, 28, 40, 54, 70, 88, 108, 130, 154, 180, 208, 238, 270, 304, 340, 378, 418, 460, 504, 550, 598, 648, 700, 754, 810, 868, 928, 990, 1054, 1120, 1188, 1258, 1330, 1404, 1480, 1558, 1638, 1720, 1804, 1890, 1978, 2068, 2160, 2254, 2350, 2448, 2548, 2650, 2754, 2860, 2968, 3078, 3190, 3304, 3420, 3538, 3658, 3780, 3904, 4030, 4158, 4288, 4420, 4554, 4690, 4828, 4968, 5110, 5254, 5400, 5548, 5698, 5850, 6004, 6160, 6318, 6478, 6640, 6804, 6970, 7138, 7308, 7480, 7654, 7830, 8008, 8188, 8370, 8554, 8740, 8928, 9118, 9310, 9504, 9700]

Também podemos criar uma lista utilizando uma notação mais próxima da matemática:

lista = [ f(x) for x in range(1,101) ]

lê-se: lista é composta pelos valores retornados da função f() com parâmetro x sendo que x assume valores de 1 até 101 (exclusivo).


In [15]:
lista = [f(x) for x in range(1,101)]
print(lista)


[-2, -2, 0, 4, 10, 18, 28, 40, 54, 70, 88, 108, 130, 154, 180, 208, 238, 270, 304, 340, 378, 418, 460, 504, 550, 598, 648, 700, 754, 810, 868, 928, 990, 1054, 1120, 1188, 1258, 1330, 1404, 1480, 1558, 1638, 1720, 1804, 1890, 1978, 2068, 2160, 2254, 2350, 2448, 2548, 2650, 2754, 2860, 2968, 3078, 3190, 3304, 3420, 3538, 3658, 3780, 3904, 4030, 4158, 4288, 4420, 4554, 4690, 4828, 4968, 5110, 5254, 5400, 5548, 5698, 5850, 6004, 6160, 6318, 6478, 6640, 6804, 6970, 7138, 7308, 7480, 7654, 7830, 8008, 8188, 8370, 8554, 8740, 8928, 9118, 9310, 9504, 9700]

E podemos adicionar condicionais para expressar funções mais interessantes:

$$

f(x) = \begin{cases} 2*x, & \text{se } x \text{ for par} \\ x^2, & \text{caso contrário} \end{cases} $$

$$ lista = \{f(x) \mid \forall x \in [1,101[ \} $$


In [17]:
lista = [ 2*x if x%2==0 else x**2 for x in range(1,101)]
print(lista)


[1, 4, 9, 8, 25, 12, 49, 16, 81, 20, 121, 24, 169, 28, 225, 32, 289, 36, 361, 40, 441, 44, 529, 48, 625, 52, 729, 56, 841, 60, 961, 64, 1089, 68, 1225, 72, 1369, 76, 1521, 80, 1681, 84, 1849, 88, 2025, 92, 2209, 96, 2401, 100, 2601, 104, 2809, 108, 3025, 112, 3249, 116, 3481, 120, 3721, 124, 3969, 128, 4225, 132, 4489, 136, 4761, 140, 5041, 144, 5329, 148, 5625, 152, 5929, 156, 6241, 160, 6561, 164, 6889, 168, 7225, 172, 7569, 176, 7921, 180, 8281, 184, 8649, 188, 9025, 192, 9409, 196, 9801, 200]

que é equivalente a:


In [18]:
lista = []
for x in range(1,101):
    if x%2==0:
        lista.append(2*x)
    else:
        lista.append(x**2)
print(lista)


[1, 4, 9, 8, 25, 12, 49, 16, 81, 20, 121, 24, 169, 28, 225, 32, 289, 36, 361, 40, 441, 44, 529, 48, 625, 52, 729, 56, 841, 60, 961, 64, 1089, 68, 1225, 72, 1369, 76, 1521, 80, 1681, 84, 1849, 88, 2025, 92, 2209, 96, 2401, 100, 2601, 104, 2809, 108, 3025, 112, 3249, 116, 3481, 120, 3721, 124, 3969, 128, 4225, 132, 4489, 136, 4761, 140, 5041, 144, 5329, 148, 5625, 152, 5929, 156, 6241, 160, 6561, 164, 6889, 168, 7225, 172, 7569, 176, 7921, 180, 8281, 184, 8649, 188, 9025, 192, 9409, 196, 9801, 200]

Exercício 01: Faça uma função GeraLista(var) que retorne a seguinte lista:

$$ lista = \{ x^3 - 3x \mid \forall x \in [1,var] \}, $$

onde $var$ é uma variável a ser definida pelo usuário.


In [20]:

O comando for permite percorrer uma lista atribuindo cada valor em uma variável:

lista = [1,2,3]
for x in lista:
    print(x)

In [21]:
lista = [1,2,3]
for x in lista:
    print(x)


1
2
3

Se quisermos calcular a somatória de uma lista precisamos seguir os seguintes passos:

  • ### Criar um acumulador que armazenará o resultado final, quando não existe elementos em uma lista o resultado deverá ser 0
  • ### Percorrer cada elemento da lista somando esse elemento ao acumulador

Exercício 02: Agora faça uma função que retorne a somatória dos elementos de uma lista.


In [ ]:

Com esses dois exercícios você poderia resolver o Ex. A3 da prova prática simplesmente fazendo:

Soma(GeraLista(varA))

Exercício 03: Sequência de Fibonacci.

Sabendo que o $n$-ésimo termo da sequência de Fibonacci é definido por:

$$ F_n = F_{n-1} + F_{n-2} $$

e sabendo que:

$$ F_0 = F_1 = 1 $$

Crie uma função, utilizando listas, para gerar a sequência de Fibonacci até o termo $n$.

Dica: você pode criar uma lista inicial contendo os dois primeiros termos. Em seguida, basta adicionar a lista os próximos valores na sequência, baseado nos valores anteriores.


In [22]:
def Fibonacci(n):
    F = [1,1]
    for i in range(2,n+1):
        F.append(F[i-1] + F[i-2])
    return F

print(Fibonacci(10))


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Exercício 04: Complete a função abaixo, substituindo '???', para que ela retorne o maior e o menor valor de uma lista:


In [ ]:
def MaiorMenor(lista):
    maior = ??? 
    menor = ???
    for x in lista:
        if x > maior:
            maior = ???
        if ???:
            menor = x
    return maior, menor

Tipo dict

Um dicionário, ou tipo dict, no Python representa um mapa de associações entre dois conceitos.

Digamos que queremos armazenar informações sobre os estados brasileiros. Para isso queremos guardar a associação Nome completo => Abreviação, Abreviação => Qtd. de cidades no estado,.

Em outras palavras queremos armazenar:

  • ### 'São Paulo' => 'SP'
  • ### 'Rio de Janeiro' => 'RJ'
  • ### 'SP' => 645
  • ### ...

Para isso utilizamos os dicionários do Python, seguindo a sintaxe:

dicionario = { chave1 : valor, chave2 : valor, ... }

onde chave1, chave2, etc. são as chaves do dicionário e valor é um valor associado a essa chave.


In [24]:
estados = {'São Paulo' : 'SP', 'Rio de Janeiro' : 'RJ', 'Minas Gerais' : 'MG'}
cidades = {'SP' : 645, 'RJ' : 92, 'MG' : 853}

Para acessarmos um valor de um dicionário utilizamos a sintaxe:

dicionario[ chave ]

In [29]:
print('A abreviação de "São Paulo" é', estados['São Paulo'])
print('A abreviação de "Minas Gerais" é', estados['Minas Gerais'])
print('Rio de Janeiro tem', cidades[estados['Rio de Janeiro']], 'cidades')


A abreviação de "São Paulo" é SP
A abreviação de "Minas Gerais" é MG
Rio de Janeiro tem 92 cidades

Podemos inserir uma nova associação ou alterar uma já existente:

dicionario[ chave ] = valor

ou

dicionario[ chave ] = dicionario[chave] + valor

In [31]:
cidades['RJ'] = cidades['RJ'] - 1
print('Rio de Janeiro perdeu uma cidade e agora ele tem: ', cidades['RJ'],'cidades')


Rio de Janeiro perdeu uma cidade e agora ele tem:  91 cidades

Se tentarmos acessar um elemento que não existe, o Python retorna uma mensagem de erro:


In [32]:
print(cidades['RS'])


---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-32-d3bdfe6fdcd6> in <module>()
----> 1 print(cidades['RS'])

KeyError: 'RS'

Para verificar se uma chave existe em um dicionário utilizamos o operador in:


In [34]:
if 'RS' in cidades:
    print(cidades['RS'])
else:
    print('Não consta esse estado!')


Não consta esse estado!

Para os casos que queremos assumir um valor padrão quando a chave não existe, podemos utilizar o método .get():

dicionario.get( chave, valor_padrao )

In [35]:
print('O Rio Grande do Sul tem', cidades.get('RS', 0), 'cidades')


O Rio Grande do Sul tem 0 cidades

Exercício 05: Complete a função abaixo para contar a frequência de palavras da lista passada como parâmetro utilizando o método .get().


In [ ]:
def ContaPalavras(lista):
    frequencia = {}
    for palavra in lista:
        frequencia[palavra] = ???
    return frequencia

print(ContaPalavras(['eu', 'vou', 'tirar', 'dez', 'nessa', 'prova', 'ou', 'eu', 'reprovo']))

Exercício 06: Complete a função GeraCifra(n) para que ela crie um dicionário que associe uma certa letra do alfabeto com a n-ésima letra seguinte. Ex.:

n = 2

'a' => 'c'

'b' => 'd'

...

'x' => 'z'

'y' => 'a'

'z' => 'b'

Sabendo que a variável letras contém todas as letras do alfabeto na sequência e que letra[0] == 'a' e letra[1] == 'b'.


In [ ]:
import string

def GeraCifra(n):
    letras = string.ascii_lowercase
    cifra = {}
    for i in range(len(letras)):
        cifra[letras[i]] = ???
    return cifra
    
print(GeraCifra(2))

Exercício 07: Complete a função Codifica(frase, n) para que ela troque cada letra da string frase pela letra correspondente do mapa gerado pela função GeraCifra(n).


In [ ]:
def Codifica(frase, n):
    cifra = GeraCifra(n)
    codificado = ''
    for i in range(len(frase)):
        codificado = codificado + ???
    return codificado

code = Codifica('eu vou tirar dez', 4)
print('A frase codificada é:', code)
print('Por que consigo decodificar com esse comando?', Codifica(code, -4))

Para percorrer todas as chaves e valores de um dicionário utilizamos o método .items():


In [52]:
dicionario = { 'a':1, 'b':2, 'c':3 }
for chave, valor in dicionario.items():
    print(chave, '=>', valor)


c => 3
a => 1
b => 2

Tipo numpy.array

O tipo numpy.array, proveniente da biblioteca numpy, especifica uma estrutura de vetores e matrizes para cálculos vetoriais, matriciais e de álgebra linear.

Essencialmente ele se comporta como uma lista, porém com os operadores matemáticos se comportando da mesma forma que se espera com vetores e matrizes na matemática.


In [54]:
import numpy as np

v1 = np.array( range(20) )
v2 = np.array( range(100,120) )
print(v1)
print(v2)


[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
[100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
 118 119]

Tente advinhar o que as seguintes operações estão fazendo:


In [56]:
print( v1 + 1 )
print()
print( v1 * 10 )
print()
print( v1 + v2 )
print()
print( v1 * v2 )


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

[  0  10  20  30  40  50  60  70  80  90 100 110 120 130 140 150 160 170
 180 190]

[100 102 104 106 108 110 112 114 116 118 120 122 124 126 128 130 132 134
 136 138]

[   0  101  204  309  416  525  636  749  864  981 1100 1221 1344 1469 1596
 1725 1856 1989 2124 2261]

O acesso aos elementos do vetor é similar ao das listas:


In [58]:
print('O primeiro elemento de v1 é',v1[0])
print('O último elemento de v1 é',v1[-1])
print('Os elementos de 2 até 10 de 2 em 2 de v1 são', v1[2:11:2])


O primeiro elemento de v1 é 0
O último elemento de v1 é 19
Os elementos de 2 até 10 de 2 em 2 de v1 são [ 2  4  6  8 10]

Também é possível acessar faixas de valores utilizando comparações:

vetor[  vetor > 10 ] # apenas valores maiores do que 10
vetor1[ vetor2 == 1 ] # apenas os índices em que o vetor2 tem valor 1

In [84]:
v1 = np.array([1,10,100,1000,10000,5000,500,50,5])
v2 = np.array([1,0,1,0,1,0,1,0,1])
print( v1[ v2==1 ] )


[    1   100 10000   500     5]

Exercício 08: Números primos usando o numpy.

Dada a função VerificaPrimo(x) que retorna True se o número x é primo e False caso contrário.

Faça uma função Primos(n) que retorna os números primos entre 1 e n. Para isso implemente o seguinte algoritmo:

  • ### Crie um vetor do numpy de tamanho n+1 com todos os valores iguais a 1

Esse vetor irá indicar quais elementos são primos (1) e quais não são (0).

  • ### Atribua o valor 0 para a posição 0 e 1, indicando que eles não são primos

  • ### Para todo número de 2 até n, faça:

    • ### Se o número ainda estiver marcado como primo
      • ### Se ele for realmente primo (verique com a função VerificaPrimo)
      • ### marque todos os múltiplos dele como não primos
  • ### Retorna os números primos marcados

In [78]:
def VerificaPrimo(x):
    for i in range(2,x):
        if x%i == 0:        # se encontrar um i que divide x, retorna falso
            return False
    return True             # se não encontrou nenhum divisível, retorna verdadeiro

def Primos(n):
    primos = np.ones(n+1)   # vetor de tamanho n+1 com tudo igual a 1
    numeros = np.arange(n+1)
    primos[???] = 0
    for atual in range(2,n+1):
        if primos[???] == 1:
            if VerificaPrimo(???):
                primos[???] = 0
                
    return numeros[???]

print(Primos(25))


[ 2  3  5  7 11 13 17 19 23]

Matrizes podem ser criadas utilizando a mesma sintaxe:

matriz = np.array( [[a11, a12, a13], [a21, a22, a23]] )

In [59]:
matriz = np.array( [[1, 2, 3], [4, 5, 6], [7, 8, 9]] )
print(matriz)


[[1 2 3]
 [4 5 6]
 [7 8 9]]

In [60]:
print(matriz+1)
print()
print(matriz*2)


[[ 2  3  4]
 [ 5  6  7]
 [ 8  9 10]]

[[ 2  4  6]
 [ 8 10 12]
 [14 16 18]]

O numpy contém todas as funções da biblioteca math e mais algumas, e permite que elas sejam aplicadas elemento-a-elemento em um vetor ou matriz:


In [61]:
matriz2 = np.power(matriz, 2)
print(matriz2)
print()

matriz3 = np.sqrt(matriz)
print(matriz3)


[[ 1  4  9]
 [16 25 36]
 [49 64 81]]

[[ 1.          1.41421356  1.73205081]
 [ 2.          2.23606798  2.44948974]
 [ 2.64575131  2.82842712  3.        ]]

In [62]:
print(matriz + matriz2)
print()
print(matriz * matriz2)


[[ 2  6 12]
 [20 30 42]
 [56 72 90]]

[[  1   8  27]
 [ 64 125 216]
 [343 512 729]]

Temos também operadores específicos de algebra linear e cálculo vetorial:


In [69]:
print(np.dot(v1, v2)) # produto interno
print()
print(np.outer(v1[:3], v2[:3])) # produto externo entre os 3 primeiros elementos de cada vetor


21470

[[  0   0   0]
 [100 101 102]
 [200 202 204]]

In [71]:
print(matriz @ matriz2)  # multiplicação de matriz
print()
print(np.linalg.inv(matriz)) # inversa da matriz
print()
print(matriz.T) # transposta da matriz


[[ 180  246  324]
 [ 378  525  702]
 [ 576  804 1080]]

[[  3.15251974e+15  -6.30503948e+15   3.15251974e+15]
 [ -6.30503948e+15   1.26100790e+16  -6.30503948e+15]
 [  3.15251974e+15  -6.30503948e+15   3.15251974e+15]]

[[1 4 7]
 [2 5 8]
 [3 6 9]]

Para acessar um elemento da matriz basta indicar as coordenadas separadas por vírgula no seguinte formato:

matriz[ i,j ]

Exercício 09: Vamos criar nosso próprio Facebook. Para isso vamos primeiro criar um dicionário contendo a relação Usuários e Número de Identificação. O número de identificação é um número sequencial iniciando em zero.

Faça uma função que receba uma lista de nomes e retorne um dicionário em que a chave é um nome o valor um número identificador.


In [86]:
def GeraID(nomes):
    id = 0
    mapa = {}
    for nome in nomes:
        mapa[???] = ???
        id = ???
    return mapa

Exercício 10: Agora vamos criar uma função que recebe uma lista de relações de amizade no seguinte formato:

[ (nome1, nome2), (nome3, nome4), ... ], indicando que nome1 e nome2 são amigos

e a função deve retornar uma matriz n x n em que o elemento i,j contém o valor 1 caso exista a relação (nome1, nome2) dentro da lista.


In [87]:
def MatrizAmizade( relacoes ):
    n = len(relacoes)
    Amizades = np.zeros( (n,n) )
    for relacao in relacoes:
        Amizades[ ??? ] = 1
    return Amizades

Exercício 11: Uma relação interessante é que se você multiplicar essa matriz de amizade por ela mesma, você descobre quantos amigos em comum duas pessoas possuem. Crie uma lista de nomes, uma lista de relações e descubra quantos amigos em comum cada par de pessoas tem.


In [ ]: