Capítulo 8
Índice
- 1. O que é Ciência de Dados?
- 2. Causalidade e Experimentos
- 3. Progamando em Python
- 4. Tipos de Dados
- 5. Sequências
- 6. Tabelas
- 7. Visualização
- 8. Funções e Tabelas
- 9. Aleatoriedade
- 10. Amostragem e Distribuições Empíricas
- 11. Testando Hipóteses
- 12. Comparando Duas Amostras
- 13. Estimação
- 14. Por que a Média é Importante
- 15. Previsão
from datascience import *
import matplotlib
path_data = '../../assets/data/'
matplotlib.use('Agg')
%matplotlib inline
import matplotlib.pyplot as plots
plots.style.use('fivethirtyeight')
import numpy as np
Funções e Tabelas
Estamos construindo um inventário útil de técnicas para identificar padrões e temas em um conjunto de dados usando funções já disponíveis em Python. Agora vamos explorar um recurso central da linguagem de programação Python: a definição de funções.
Já usamos funções extensivamente neste texto, mas nunca definimos uma função própria. O objetivo de definir uma função é dar um nome a um processo computacional que pode ser aplicado várias vezes. Existem muitas situações na computação que exigem cálculos repetidos. Por exemplo, frequentemente queremos realizar a mesma manipulação em cada valor em uma coluna de uma tabela.
Definindo uma Função
A definição da função double abaixo simplesmente duplica um número.
# Nossa primeira definição de função
def double(x):
""" Double x """
return 2*x
Começamos qualquer definição de função escrevendo `def`. Aqui está um detalhamento das outras partes (a *sintaxe*) desta pequena função:

Quando executamos a célula acima, nenhum número específico é duplicado, e o código dentro do corpo de double ainda não é avaliado. Nesse aspecto, nossa função é análoga a uma receita. Cada vez que seguimos as instruções em uma receita, precisamos começar com ingredientes. Cada vez que queremos usar nossa função para dobrar um número, precisamos especificar um número.
Podemos chamar double exatamente da mesma forma que chamamos outras funções. Cada vez que fazemos isso, o código no corpo é executado, com o valor do argumento recebendo o nome x.
double(17)
| Out[1]: | 34 |
double(-0.6/4)
| Out[2]: | -0.3 |
As duas expressões acima são ambas expressões de chamada. Na segunda, o valor da expressão -0.6/4 é calculado e então passado como o argumento nomeado x para a função double. Cada expressão de chamada resulta na execução do corpo de double, mas com um valor diferente de x.
O corpo de double tem apenas uma única linha:
return 2*x
Executar esta instrução return completa a execução do corpo da função double e calcula o valor da expressão de chamada.
O argumento para double pode ser qualquer expressão, desde que seu valor seja um número. Por exemplo, pode ser um nome. A função double não sabe ou se importa com a forma como seu argumento é calculado ou armazenado; seu único trabalho é executar seu próprio corpo usando os valores dos argumentos passados para ela.
any_name = 42
double(any_name)
| Out[3]: | 84 |
O argumento também pode ser qualquer valor que possa ser duplicado. Por exemplo, uma matriz inteira de números pode ser passada como argumento para double e o resultado será outra matriz.
double(make_array(3, 4, 5))
| Out[4]: | array([ 6, 8, 10]) |
No entanto, nomes que são definidos dentro de uma função, incluindo argumentos como x de double, têm apenas uma existência passageira. Eles são definidos apenas enquanto a função está sendo chamada e só são acessíveis dentro do corpo da função. Não podemos nos referir a x fora do corpo de double. A terminologia técnica é que x tem escopo local.
Portanto, o nome x não é reconhecido fora do corpo da função, mesmo que tenhamos chamado double nas células acima.
x
| Out[5]: | ————————————————————————— NameError Traceback (most recent call last) <ipython-input-7-6fcf9dfbd479> in <module> —-> 1 xNameError: name ‘x’ is not defined |
**Docstrings.** Embora `double` seja relativamente fácil de entender, muitas funções realizam tarefas complicadas e são difíceis de usar sem explicação. (Você pode ter descoberto isso por si mesmo!) Portanto, uma função bem elaborada tem um nome que evoca seu comportamento, bem como documentação. Em Python, isso é chamado de *docstring* — uma descrição de seu comportamento e expectativas sobre seus argumentos. A docstring também pode mostrar chamadas de exemplo para a função, onde a chamada é precedida por `>>>`.
Uma docstring pode ser qualquer string, desde que seja a primeira coisa no corpo de uma função. Docstrings geralmente são definidas usando aspas triplas no início e no fim, o que permite que uma string abranja várias linhas. A primeira linha é convencionalmente uma descrição completa, mas curta, da função, enquanto as linhas seguintes fornecem orientação adicional para futuros usuários da função.
Aqui está uma definição de uma função chamada `percent` que recebe dois argumentos. A definição inclui uma docstring.
# Uma função com mais de um argumento
def percent(x, total):
"""Converte x em uma porcentagem do total.
Mais precisamente, esta função divide x por total,
multiplica o resultado por 100 e arredonda o resultado
para duas casas decimais.
>>> percent(4, 16)
25.0
>>> percent(1, 6)
16.67
"""
return round((x/total)*100, 2)
percent(33, 200)
| Out[6]: | 16.5 |
Compare a função percent definida acima com a função percents definida abaixo. Esta última toma uma matriz como argumento e converte todos os números da matriz em porcentagens do total dos valores na matriz. As porcentagens são todos arredondados para duas casas decimais, desta vez substituindo round por np.round porque o argumento é uma matriz e não um número.
def percents(counts):
"""Converta os valores em array_x em porcentagens do total de array_x."""
total = counts.sum()
return np.round((counts/total)*100, 2)
A função percents retorna uma matriz de porcentagens que somam 100, sem arredondamento.
some_array = make_array(7, 10, 4)
percents(some_array)
| Out[7]: | array([33.33, 47.62, 19.05]) |
É útil entender as etapas que o Python executa para executar uma função. Para facilitar isso, colocamos uma definição de função e uma chamada para essa função na mesma célula abaixo.
def biggest_difference(array_x):
"""Encontre a maior diferença em valor absoluto entre dois elementos adjacentes de array_x."""
diffs = np.diff(array_x)
absolute_diffs = abs(diffs)
return max(absolute_diffs)
some_numbers = make_array(2, 4, 5, 6, 4, -1, 1)
big_diff = biggest_difference(some_numbers)
print("The biggest difference is", big_diff)
| Out[8]: | The biggest difference is 5 |
Aqui está o que acontece quando executamos aquela célula:

Argumentos Múltiplos
Pode haver várias maneiras de generalizar uma expressão ou bloco de código e, portanto, uma função pode receber vários argumentos, cada um determinando diferentes aspectos do resultado. Por exemplo, a função percents que definimos anteriormente, arredondada para duas casas decimais a cada vez .A definição de dois argumentos a seguir permite que chamadas diferentes sejam arredondadas para valores diferentes.
def percents(counts, decimal_places):
"""Convert the values in array_x to percents out of the total of array_x."""
total = counts.sum()
return np.round((counts/total)*100, decimal_places)
parts = make_array(2, 1, 4)
print("Rounded to 1 decimal place: ", percents(parts, 1))
print("Rounded to 2 decimal places:", percents(parts, 2))
print("Rounded to 3 decimal places:", percents(parts, 3))
| Out[9]: | Rounded to 1 decimal place: [28.6 14.3 57.1] Rounded to 2 decimal places: [28.57 14.29 57.14] Rounded to 3 decimal places: [28.571 14.286 57.143] |
A flexibilidade desta nova definição tem um preço pequeno: cada vez que a função é chamada, o número de casas decimais deve ser especificado. Os valores padrão dos argumentos permitem que uma função seja chamada com um número variável de argumentos; qualquer argumento que seja especificado na expressão de chamada recebe seu valor padrão, que é indicado na primeira linha da instrução def. Por exemplo, nesta definição final de percents, o argumento opcional decimal_places recebe um valor padrão de 2.
def percents(counts, decimal_places=2):
"""Converta os valores em array_x em porcentagens do total de array_x."""
total = counts.sum()
return np.round((counts/total)*100, decimal_places)
parts = make_array(2, 1, 4)
print("Rounded to 1 decimal place:", percents(parts, 1))
print("Rounded to the default number of decimal places:", percents(parts))
| Out[10]: | Rounded to 1 decimal place: [28.6 14.3 57.1] Rounded to the default number of decimal places: [28.57 14.29 57.14] |
Nota: Métodos
As funções são chamadas colocando expressões de argumento entre parênteses após o nome da função. Qualquer função definida isoladamente é chamada desta forma. Você também viu exemplos de métodos, que são como funções, mas são chamados usando notação de ponto, como some_table.sort(some_label). As funções que você define sempre serão chamadas usando o nome da função primeiro, passando todos os argumentos.
| ← Capítulo 7.3 – Gráficos Sobrepostos | Capítulo 8.1 – Aplicando Uma Função a Uma Coluna → |
