<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 [Py-Intro] Aula 04
Tipos básicos e estruturas de controles
O que você vai aprender nesta aula?
Após o término da aula você terá aprendido
Step1:  Porém podemos especificar explicitamente as posições que queremos substituir
Step2:  Podemos repetir um único argumento
Step3:  Informar uma ordem arbritária
Step4:  Também é possível dar nomes a esses campos
Step5:  Também podemos formatar números
Step6:  format() também permite alinhar elementos dentro de um espaço de 10 caracteres
Step7:  O tamanho do espaço pode ser diferente nos elementos
Step8:  Também é  possível mudar o caracter de preenchimento do espaço dado de caracter em branco para algum outro
Step9:  Também podemos armazenar format e reaplicá-los para valores diferentes
Step10:  Também é possível especificar o tipo dos dados
Step11:  Os formatos possíveis são
Step12:  Diferentemente da função str.format() a função embutida format() não permite substituição de caracteres usando chaves.
Para saber mais sobre o assunto consulte a documentação e este documento feito pelo Luciano Ramalho que explica detalhadamente como usar essa função.
Tipos básicos - conjuntos e mapeamentos
Na aula passada vimos outros tipos básicos
Step13:  Como podemos ver a sintaxe de set - {1}, {1, 2}, etc. - se parece exatamente com a notação matemática, com exceção que não existe uma notação para set vazio. Caso você precise criar um conjunto vazio use
Step14:  Vale lembrar que conjuntos também se comportam como sequências, por conta disso é possível usar neles as funções que aprendemos anteriormente
Step15:  Um ponto importante a se observar é que conjunto não mantém a ordem dos elementos
Step16:  Por isso não é possível acessar os elementos pela posição
Step17:  É possível acessar seus elementos iterando o set
Step18:  Ou convertendo-o para tupla ou lista
Step19:  Assim como listas os conjuntos também possuem um mecanismo simplificado para construir conjuntos o set comprehension
Step20:  Outra característica importante de conjuntos é que eles realizam verificações de pertencimento de forma muito mais eficiente.
Para verificar se um elemento está presente em um conjunto usamos o mesmo operador in que vimos para lista, tuplas e string
Step21:  Para demonstrar que conjuntos verificam se um elemento pertence em uma coleção de maneira mais rápida isso vamos usar o módulo timeit que oferece uma maneira simples de contar o tempo de execução de código Python.
O módulo timeit possui uma função timeit(stmt='pass', setup='pass', number=1000000, ...) que executa um setup e um código (stmt) uma dada quantidade de vezes e contabiliza o tempo levado para executar o código (o tempo de rodar o setup não é incluído). O código e o setup devem ser passados como strings.
Step22:  O código acima executa primeiro o setup import math depois o código [math.exp(x) for x in range(10)], que cria uma lista com os exponencianciais de 0 a 9, 1000000 vezes.
Para sabermos qual a média do tempo de execução desse código fazemos
Step23:  Sabendo disso agora podemos calcular o tempo de verificação de um elemento em lista e set
Step24:  Esse código usa alguns recursos mais avançados de formatação de string. Para entender melhor o que é feito verifique a documentação oficial do assunto.
Conjuntos também oferecem algumas operações interessantes
Step25:  <center>Intersecção $${A \cap B}$$</center>
A intersecção pode ser feita a partir da função A.intersection(B) ou com o operador bitwise e &amp; 
Step26:  <center>Diferença $${A - B}$$</center>
A diferença pode ser feita a partir da função A.difference(B) ou com o operador - 
Step27:  <center>Diferença simétrica $${A \bigtriangleup B}$$</center>
A diferença pode ser feita a partir da função A.difference(B) ou com o operador ^ 
Step28:  Ok, essas funções são legais, mas eu já vi isso no ensino médio e nunca usei na minha vida, como isso vai ser útil para mim? (pelo menos foi isso que eu pensei ao ver isso)
Para testar isso vamos gerar um conjunto de nomes. Vamos usar a biblioteca externa faker que gera dados falsos.
Para usá-la é necessário instalar
Step29:  Agora vamos supor que temos uma lista de nomes e queremos conferir se eles estão no conjunto de nomes. Normalmente faríamos
Step30:  Porém se usarmos operações de conjunto podemos fazer isso de forma mais simples e eficiente.
Para saber quais nomes buscados estão no conjunto de nomes
Step31:  Os nomes que não estão
Step32:  Comparando o tempo de execução das buscas usando for contra buscas usando intersecção obtemos o seguinte resultado
Step33:  dict
Dict, dictnionary ou dicionário é a estrutura padrão de mapeamento e é indexado por chaves compostas por tipos imutáveis (números, strings, tuplas etc.).
Já falamos sobre dicionários anteriormente, nesta aula faremos uma rápida revisão e aprofundremos mais sobre o assunto
Step34:  Como podem ver aqui os dicionários não mantém a ordem de seus elementos. Criamos o dict com os elementos {'joão'
Step35:  Vale notar que ao tentar acessar um elemento não existente é levantada uma exceção
Step36:  As vezes pode ser necessário evitar esse tipo de comportamento. Isso pode ser feito usando a função
Step37:  também é possível estabelecer um valor para ser retornado caso a chave não seja encontrada
Step38:  Por exemplo se você quiser contabilizar os votos de uma eleição em que nem todos os candidatos receberam votos e, portanto, não aparecem no dicionário votos
Step39:  Podemos verificar se alguma chave existe no dicionário com o in
Step40:  Para acessar somente as chaves de um dicionário fazemos
Step41:  Percebe-se que o retorno não é uma lista de chaves, mas sim um dict_keys. Não vou entrar em detalhes, mas esse dict_keys - junto com dict_values e dict_items que serão mostrados mais a frente - se comportam como conjunto, por tanto verificações de pertencimento são muito eficientes, além de suportar algumas operações de conjuntos
Step42:  Para acessar somente os valores do dicionários
Step43:  Como os valores não são únicos o dict_values não pode se comportar como conjunto, por esse motivo ele não possui as operações de conjuntos
Step44:  Porém o dict.items() implementa as operações de conjuntos, pois a dupla chave e valor são únicas
Step45:  Revendo iteração de dicionários
Step46:  Exercício
Calcule a média de votos por candidatos
Step47:  Assim como listas e sets, dicionários também possuem uma maneira de criar dicts com facilidade
Step48:  Acontece que dict comprehension nos dá uma maneira muito bonilinda de inverter as chaves e valores de dicionários
Step49:  Normalmente faríamos
Step50:  Mas com dict comprehension é muito mais fácil
Step51:  Ordenando um dict pelas chaves
Step52:  Não faz snetido obter dict pq dict nào mantém a ordem.
Para ter um objeto do tipo dict é necessário converter o código acima
Step53:  Para ordernar pelos valores precisamos "avisar" a função sorted() para usar o valor como a chave. Isso é feito passando um argumento key que recebe uma função que é usada para pegar o valor que será usado na ordenação. Nesse caso precisamos criar uma função que retorna o segundo elemento de nossa lista de tuplas chave e valor, referente a esse último.
Step54:  Adendo
Step55:  Geralmente o uso de funções anônimas é feito assim
Step56:  Porém, o jeito mais eficiente de realizar esse tipo de operação é utilizando a biblioteca operator que implementa os operadores do python de forma eficiente
Step57:  A função itemgetter() da biblioteca operator faz o mesmo que a função pega_segundo_elemento() mas de forma muito mais rápida. Para saber mais sobre operator veja sua documentação.
Exemplos
Dicionários podem ser utilizados para armazenar matrizes esparsas
Exercícios
Dado uma frase calcule as ocorrências de cada palavra naquela. Armazene cada palavra como chave em um dict e as quantidade seu valor
Step58:  Escreva uma função comprime_chaves_dict() que receba um dict e remova as vogais das chaves de um dicionário. Por exempo o dict {'foo'
<ASSISTANT_TASK:>
Python Code:
'{} + {} = {}'.format(10, 10, 20)
Explanation: [Py-Intro] Aula 04
Tipos básicos e estruturas de controles
O que você vai aprender nesta aula?
Após o término da aula você terá aprendido:
Formatação de strings
Conjuntos: set
Mapeamentos: dicionários
Formatação de strings
Complementando a aula passada será explicado melhor como funciona a função format() e str.format() começando pela última.
A função str.format() converte os campos envolvos por chaves pelos argumentos passados a ela. Caso não seja especificado o nome ou posição dos argumentos a troca de chaves por dados é feita na ordem:
End of explanation
'{0} + {1} = {2}'.format(10, 10, 20)  # esse é o padrão
Explanation: Porém podemos especificar explicitamente as posições que queremos substituir:
End of explanation
'{0} + {0} = {1}'.format(10, 20)
Explanation: Podemos repetir um único argumento:
End of explanation
'{1} + {0} = {2}'.format(30, 20, 10)  # evite fazer isso para não causar confusão
Explanation: Informar uma ordem arbritária:
End of explanation
string = '{cidade} é muito bonito(a) durante o(a) {estação}'
string.format(cidade='Bruxelas', estação='Inverno')
Explanation: Também é possível dar nomes a esses campos:
End of explanation
'O total é R${0}'.format(59.8912313)
'O total é R${0:.2f}'.format(59.8912313)
'A porcentagem é de {0:.2%}'.format(0.8912313)
Explanation: Também podemos formatar números:
End of explanation
'{0:<10} | {1:<10} | {2:<10}'.format('Qtd.', 'Cor', 'Valor')  # alinhado à esquerda
'{0:>10} | {1:>10} | {2:>10}'.format('Qtd.', 'Cor', 'Valor')  # alinhado à direita
Explanation: format() também permite alinhar elementos dentro de um espaço de 10 caracteres:
End of explanation
'{0:^6} | {1:^9} | {2:^10}'.format('Qtd.', 'Cor', 'Valor')  # centralizado
Explanation: O tamanho do espaço pode ser diferente nos elementos:
End of explanation
'{0:+^6} | {1:=^9} | {2:-^10}'.format('Qtd.', 'Cor', 'Valor')  # centralizado
'{0:+^6} | {1:=^9} | {2:-^10}'.format('Qtd.', 'Cor', 'Valor')  # centralizado
Explanation: Também é  possível mudar o caracter de preenchimento do espaço dado de caracter em branco para algum outro:
End of explanation
formato_tabela = '{0:^6} | {1:^9} | {2:^10}'
formato_tabela
produtos = [
    (2, 'Amarelo', 18.50),
    (5, 'Verde', 48.50),
    (2, 'Azul', 78.50),
]
produtos
print(formato_tabela.format('Qtd.', 'Cor', 'Valor R$'))
for qtd, cor, valor in produtos:
    print(formato_tabela.format(qtd, cor, valor))
Explanation: Também podemos armazenar format e reaplicá-los para valores diferentes:
End of explanation
'{0:e} {0:f} {0:%}'.format(.0000031)
Explanation: Também é possível especificar o tipo dos dados:
End of explanation
import math
format(math.pi, '6.3f')
format('Python', '.<12')
format('Python', '.>12')
format('Python', '.^12')
Explanation: Os formatos possíveis são:
Também é possível formatar valores usando a função embutida format():
End of explanation
l = ['spam', 'spam', 'eggs', 'spam']
l
set(l)
Explanation: Diferentemente da função str.format() a função embutida format() não permite substituição de caracteres usando chaves.
Para saber mais sobre o assunto consulte a documentação e este documento feito pelo Luciano Ramalho que explica detalhadamente como usar essa função.
Tipos básicos - conjuntos e mapeamentos
Na aula passada vimos outros tipos básicos: números e sequência.
Agora vamos falar sobre conjuntos.
Set (conjunto)
Conjunto, ou set, é uma ferramenta subutilizada do Python, tanto que muitos cursos introdutórios não passam abordam esse assunto.
Set vem da teoria de conjuntos da matemática. Um conjunto não permite a existência de elementos iguais dentro de si, por conta disso é muito utilizado para remover repetições:
End of explanation
A = set()
len(a)
Explanation: Como podemos ver a sintaxe de set - {1}, {1, 2}, etc. - se parece exatamente com a notação matemática, com exceção que não existe uma notação para set vazio. Caso você precise criar um conjunto vazio use: set().
End of explanation
A = {5, 4, 3, 3, 2, 10}
A
len(A)
sum(A)
max(A)
min(A)
Explanation: Vale lembrar que conjuntos também se comportam como sequências, por conta disso é possível usar neles as funções que aprendemos anteriormente:
End of explanation
A = {4, 5, 1, 3, 4, 5, 7}
A  # ordem diferente da declarada!
Explanation: Um ponto importante a se observar é que conjunto não mantém a ordem dos elementos:
End of explanation
A[0]
Explanation: Por isso não é possível acessar os elementos pela posição:
End of explanation
for num in A:
    print(num)
Explanation: É possível acessar seus elementos iterando o set:
End of explanation
tuple(A)
tuple(A)[0]
list(A)
list(A)[-1]
Explanation: Ou convertendo-o para tupla ou lista:
End of explanation
{letra for letra in 'abrakadabraalakazam'}
{numero for numero in range(30) if numero % 3 != 0}
Explanation: Assim como listas os conjuntos também possuem um mecanismo simplificado para construir conjuntos o set comprehension:
End of explanation
A = {-10, 0, 10, 20, 30, 40}
A
5 in A
-10 in A
Explanation: Outra característica importante de conjuntos é que eles realizam verificações de pertencimento de forma muito mais eficiente.
Para verificar se um elemento está presente em um conjunto usamos o mesmo operador in que vimos para lista, tuplas e string:
End of explanation
import timeit
tempo = timeit.timeit('[math.exp(x) for x in range(10)]', setup='import math')
tempo
Explanation: Para demonstrar que conjuntos verificam se um elemento pertence em uma coleção de maneira mais rápida isso vamos usar o módulo timeit que oferece uma maneira simples de contar o tempo de execução de código Python.
O módulo timeit possui uma função timeit(stmt='pass', setup='pass', number=1000000, ...) que executa um setup e um código (stmt) uma dada quantidade de vezes e contabiliza o tempo levado para executar o código (o tempo de rodar o setup não é incluído). O código e o setup devem ser passados como strings.
End of explanation
tempo / 1000000
Explanation: O código acima executa primeiro o setup import math depois o código [math.exp(x) for x in range(10)], que cria uma lista com os exponencianciais de 0 a 9, 1000000 vezes.
Para sabermos qual a média do tempo de execução desse código fazemos:
End of explanation
import timeit
import random
# PS: esse código demora para ser executado
vezes = 1000
print('tamanho  | tempo da lista | tempo do set | list vs set')
tamanhos = (10 ** i for i in range(3, 8))
for tamanho in tamanhos:  # cria um gerador com os valores 10^3, 10^4, ..., 10^7
    setup_lista = 'l = list(range({}))'.format(tamanho)
    tempo = timeit.timeit('9999999 in l', setup=setup_lista, number=vezes)
    media_lista = tempo / vezes
    
    setup_set = 's = set(range({}))'.format(tamanho)
    tempo = timeit.timeit('9999999 in s', setup=setup_set, number=vezes)
    media_set = tempo / vezes
    
    msg = '{:<9}| {:<15}| {:<13}| set é {:<}x + rápido'
    msg = msg.format(tamanho, round(media_lista, 8), round(media_set, 8),
                     round(media_lista / media_set))
    print(msg)
Explanation: Sabendo disso agora podemos calcular o tempo de verificação de um elemento em lista e set:
End of explanation
A = {2, 3, 4}
B = {3, 5, 7}
A | B
A.union(B)
Explanation: Esse código usa alguns recursos mais avançados de formatação de string. Para entender melhor o que é feito verifique a documentação oficial do assunto.
Conjuntos também oferecem algumas operações interessantes:
<center>União $${(A \cup B)}$$</center>
A união pode ser feita a partir da função A.union(B) ou através da utilização do operador bitwise ou | :
End of explanation
A = {2, 3, 4}
B = {3, 5, 7}
A & B
A.intersection(B)
Explanation: <center>Intersecção $${A \cap B}$$</center>
A intersecção pode ser feita a partir da função A.intersection(B) ou com o operador bitwise e &amp; :
End of explanation
A = {2, 3, 4}
B = {3, 5, 7}
A - B
A = {2, 3, 4}
B = {3, 5, 7}
A.difference(B)
Explanation: <center>Diferença $${A - B}$$</center>
A diferença pode ser feita a partir da função A.difference(B) ou com o operador - :
End of explanation
A = {2, 3, 4}
B = {3, 5, 7}
A ^ B
A.symmetric_difference(B)
Explanation: <center>Diferença simétrica $${A \bigtriangleup B}$$</center>
A diferença pode ser feita a partir da função A.difference(B) ou com o operador ^ :
End of explanation
from faker import Factory
factory = Factory.create('pt_BR')  # criando fábrica de dados falsos português brasileiro
nomes = {factory.name() for _ in range(10000)}
nomes
Explanation: Ok, essas funções são legais, mas eu já vi isso no ensino médio e nunca usei na minha vida, como isso vai ser útil para mim? (pelo menos foi isso que eu pensei ao ver isso)
Para testar isso vamos gerar um conjunto de nomes. Vamos usar a biblioteca externa faker que gera dados falsos.
Para usá-la é necessário instalar:
$ pip install fake-factory
Depois de instalá-la em nosso virtualenv podemos usá-la:
End of explanation
buscas = {'João Silva', 'Ana Ferreira', 'Eduardo Santos', 'Pedro Alves', 'Enzo Correira'}
presentes = [busca for busca in buscas if busca in nomes]
presentes
ausentes = [busca for busca in buscas if busca not in nomes]
ausentes
Explanation: Agora vamos supor que temos uma lista de nomes e queremos conferir se eles estão no conjunto de nomes. Normalmente faríamos:
End of explanation
buscas & nomes
Explanation: Porém se usarmos operações de conjunto podemos fazer isso de forma mais simples e eficiente.
Para saber quais nomes buscados estão no conjunto de nomes:
End of explanation
buscas - nomes
Explanation: Os nomes que não estão:
End of explanation
# tamanho  | tempo set + for | tempo set + & | for vs &
# 100      | 3.945e-05       | 1.86e-06      | & é 21.25x + rápido
# 1000     | 5.844e-05       | 1.751e-05     | & é 3.34x + rápido
# 10000    | 0.00014848      | 5.991e-05     | & é 2.48x + rápido
# 100000   | 0.00015138      | 8.862e-05     | & é 1.71x + rápido
# 1000000  | 0.00014647      | 8.113e-05     | & é 1.81x + rápido
Explanation: Comparando o tempo de execução das buscas usando for contra buscas usando intersecção obtemos o seguinte resultado:
End of explanation
votos = {'joão': 10, 'maria': 25, 'ana': 40, 'pedro': 75}
votos['joão']
votos['joão'] = 11
votos
Explanation: dict
Dict, dictnionary ou dicionário é a estrutura padrão de mapeamento e é indexado por chaves compostas por tipos imutáveis (números, strings, tuplas etc.).
Já falamos sobre dicionários anteriormente, nesta aula faremos uma rápida revisão e aprofundremos mais sobre o assunto:
End of explanation
len(votos)
del votos['joão']
votos
Explanation: Como podem ver aqui os dicionários não mantém a ordem de seus elementos. Criamos o dict com os elementos {'joão': 10, 'maria': 25, 'ana': 40, 'pedro': 75}, porém sua ordem atual é {'ana': 40, 'joão': 11, 'maria': 25, 'pedro': 75} e, futuramente, essa ordem pode ser outra conforme alteramos esse dicionário.
End of explanation
votos['joão']
Explanation: Vale notar que ao tentar acessar um elemento não existente é levantada uma exceção:
End of explanation
print(votos.get('joão'))
Explanation: As vezes pode ser necessário evitar esse tipo de comportamento. Isso pode ser feito usando a função:
End of explanation
votos.get('joão', 0)
Explanation: também é possível estabelecer um valor para ser retornado caso a chave não seja encontrada:
End of explanation
candidatos = list(votos.keys()) + ['joão', 'muriel', 'marcola']
candidatos
for candidato in candidatos:
    print('{} recebeu {} votos.'.format(candidato, votos.get(candidato, 0)))
Explanation: Por exemplo se você quiser contabilizar os votos de uma eleição em que nem todos os candidatos receberam votos e, portanto, não aparecem no dicionário votos:
End of explanation
votos
'ana' in votos
'penélope' in votos
len(votos)  # número de items no dict
outros_votos = {'milena': 100, 'mário': 1}
votos.update(outros_votos)  # atualiza o dicionário votos com os items de outros_votos
votos
Explanation: Podemos verificar se alguma chave existe no dicionário com o in:
End of explanation
votos.keys()
Explanation: Para acessar somente as chaves de um dicionário fazemos:
End of explanation
['maria', 'adelaide'] & votos.keys()
['maria', 'adelaide'] - votos.keys()
Explanation: Percebe-se que o retorno não é uma lista de chaves, mas sim um dict_keys. Não vou entrar em detalhes, mas esse dict_keys - junto com dict_values e dict_items que serão mostrados mais a frente - se comportam como conjunto, por tanto verificações de pertencimento são muito eficientes, além de suportar algumas operações de conjuntos:
Para mais informações verifique a documentação oficial e o PEP 3106 em que essa mudança foi proposta
End of explanation
votos.values()
Explanation: Para acessar somente os valores do dicionários:
End of explanation
votos.items()
Explanation: Como os valores não são únicos o dict_values não pode se comportar como conjunto, por esse motivo ele não possui as operações de conjuntos
End of explanation
[('jean', 50), ('maria', 25)] & votos.items()
[('jean', 50), ('maria', 25)] - votos.items()
Explanation: Porém o dict.items() implementa as operações de conjuntos, pois a dupla chave e valor são únicas:
End of explanation
for nome in votos.keys():
    print(nome)
for qtd_votos in votos.values():
    print(qtd_votos)
for nome, qtd_votos in votos.items():  # atribuição múltipla, lembra?
    print('{} recebeu {} votos.'.format(nome.capitalize(), qtd_votos))
Explanation: Revendo iteração de dicionários:
End of explanation
# não mude esse código, ele que gera os votos para você testar seu programa
from faker import Factory
import random
factory = Factory.create('pt_BR')
# usa distribuição de gauss para gerar quantidade de votos
votos = {factory.name(): abs(round(random.gauss(0, .2) * 10000)) for _ in range(333)}
# deixa nomes completos com somente dois nomes
votos = {nome: votos for nome, votos in votos.items() if len(nome.split()) == 2}
def media(votos):
    ...
Explanation: Exercício
Calcule a média de votos por candidatos:
End of explanation
from faker import Factory
factory = Factory.create('pt_BR')
cpfs = {factory.name(): factory.cpf() for _ in range(10)}
cpfs
{numero: numero ** 2 for numero in range(10)}
Explanation: Assim como listas e sets, dicionários também possuem uma maneira de criar dicts com facilidade: dict comprehension
End of explanation
telefones = {'joão': '9941', 'ana': '9103', 'maria': '9301', 'pedro': '9203'}
telefones
Explanation: Acontece que dict comprehension nos dá uma maneira muito bonilinda de inverter as chaves e valores de dicionários:
End of explanation
nomes = {}
for nome, telefone in telefones.items():
    nomes[telefone] = nome
    
nomes
Explanation: Normalmente faríamos:
End of explanation
{telefone: nome for nome, telefone in telefones.items()}
Explanation: Mas com dict comprehension é muito mais fácil:
End of explanation
sorted(telefones.items())
Explanation: Ordenando um dict pelas chaves:
End of explanation
dict(sorted(telefones.items()))
Explanation: Não faz snetido obter dict pq dict nào mantém a ordem.
Para ter um objeto do tipo dict é necessário converter o código acima:
End of explanation
def pega_segundo_elemento(tupla):
    return tupla[1]
sorted(telefones.items(), key=pega_segundo_elemento)
Explanation: Para ordernar pelos valores precisamos "avisar" a função sorted() para usar o valor como a chave. Isso é feito passando um argumento key que recebe uma função que é usada para pegar o valor que será usado na ordenação. Nesse caso precisamos criar uma função que retorna o segundo elemento de nossa lista de tuplas chave e valor, referente a esse último.
End of explanation
pega_segundo_elemento = lambda x: x[1]
sorted(telefones.items(), key=pega_segundo_elemento)
Explanation: Adendo: no Python funções são objetos de primeira classe. Isso signfica que funções, assim como inteiros, strings e listas, podemos passá-las como parâmetros, atribuí-las a variáveis e outras operações. Esse assunto será abordando com maior profundidade na aula sobre funções.
Um outro jeito comum de realizar essa ordenação é usar funções anônimas:
End of explanation
sorted(telefones.items(), key=lambda x: x[1])
Explanation: Geralmente o uso de funções anônimas é feito assim:
End of explanation
from operator import itemgetter
sorted(telefones.items(), key=itemgetter(1))
Explanation: Porém, o jeito mais eficiente de realizar esse tipo de operação é utilizando a biblioteca operator que implementa os operadores do python de forma eficiente:
End of explanation
from collections  import defaultdict
def conta_palavras(frase):
    contagem = {}
    ...
    return contagem
# rode o código abaixo para testar a corretude de seu programa
assert conta_palavras("quod dolore dolore dolore modi sapiente quod ullam nostrum ullam") == {'ullam': 2, 'sapiente': 1, 'quod': 2, 'nostrum': 1, 'dolore': 3, 'modi': 1}
assert conta_palavras("soluta Soluta sapiente sapiente nostrum Sapiente dolore nostrum modi ullam") == {'ullam': 1, 'sapiente': 3, 'nostrum': 2, 'dolore': 1, 'soluta': 2, 'modi': 1}
assert conta_palavras("quod dolore dolore soluta sapiente sapiente dolore quod sapiente modi") == {'dolore': 3, 'quod': 2, 'soluta': 1, 'modi': 1, 'sapiente': 3}
assert conta_palavras("dolore Dolore quis quod dolore nostrum quod Nostrum sapiente soluta") == {'sapiente': 1, 'quod': 2, 'nostrum': 2, 'soluta': 1, 'dolore': 3, 'quis': 1}
assert conta_palavras("sapiente sapiente quod soluta quis ullam nostrum soluta ullam ullam") == {'ullam': 3, 'sapiente': 2, 'quod': 1, 'nostrum': 1, 'soluta': 2, 'quis': 1}
assert conta_palavras("modi Sapiente dolore Soluta sapiente quis soluta modi dolore ullam") == {'ullam': 1, 'sapiente': 2, 'quis': 1, 'dolore': 2, 'soluta': 2, 'modi': 2}
assert conta_palavras("quis quis nostrum nostrum sapiente quis nostrum quod quis dolore") == {'sapiente': 1, 'quod': 1, 'quis': 4, 'nostrum': 3, 'dolore': 1}
assert conta_palavras("nostrum sapiente quis ullam ullam quod ullam nostrum ullam soluta") == {'ullam': 4, 'sapiente': 1, 'quod': 1, 'nostrum': 2, 'soluta': 1, 'quis': 1}
assert conta_palavras("sapiente ullam quod quis dolore modi Quis quod dolore nostrum") == {'ullam': 1, 'sapiente': 1, 'quod': 2, 'quis': 2, 'nostrum': 1, 'dolore': 2, 'modi': 1}
assert conta_palavras("modi nostrum ullam Quis Soluta modi quis ullam modi ullam") == {'ullam': 3, 'soluta': 1, 'modi': 3, 'nostrum': 1, 'quis': 2}
Explanation: A função itemgetter() da biblioteca operator faz o mesmo que a função pega_segundo_elemento() mas de forma muito mais rápida. Para saber mais sobre operator veja sua documentação.
Exemplos
Dicionários podem ser utilizados para armazenar matrizes esparsas
Exercícios
Dado uma frase calcule as ocorrências de cada palavra naquela. Armazene cada palavra como chave em um dict e as quantidade seu valor:
End of explanation
def comprime_chaves_dict(dicionario):
    ...
assert comprime_chaves_dict({'molestias': 3950, 'tempore': 'possimus', 'rerum': 1200}) == {'tmpr': 'possimus', 'mlsts': 3950, 'rrm': 1200}
assert comprime_chaves_dict({'nam': 5300, 'minus': 3700, 'fugit': 8600}) == {'mns': 3700, 'nm': 5300, 'fgt': 8600}
assert comprime_chaves_dict({'magnam': 2850, 'quam': 2300, 'asperiores': 7750}) == {'qm': 2300, 'sprrs': 7750, 'mgnm': 2850}
assert comprime_chaves_dict({'quos': 'dignissimos', 'qui': 1700, 'repellendus': 'aut'}) == {'rpllnds': 'aut', 'q': 1700, 'qs': 'dignissimos'}
assert comprime_chaves_dict({'quaerat': 9850, 'magni': 8600, 'blanditiis': 'optio'}) == {'mgn': 8600, 'qrt': 9850, 'blndts': 'optio'}
Explanation: Escreva uma função comprime_chaves_dict() que receba um dict e remova as vogais das chaves de um dicionário. Por exempo o dict {'foo': 10, 'bar': 100} deve ser comprimido para {'f': 10, 'br': 100}.
End of explanation
<END_TASK>