pandas e SQL comparados


SQL (Structured Query Language) é uma linguagem de programação de uso específico utilizada para consultar, extrair e gerenciar bancos de dados relacionais. Pandas é uma biblioteca do Python especializada para o tratamento e análise de dados estruturados, incluindo uma gama de formas de extrair dados.

Esse artigo faz uma comparação entre as consultas feitas a dataframes do pandas e as consultas SQL, explorando similaridades e diferenças entre os dois sistemas. Ele serve para descrever as funcionalidades de busca e edição do pandas e pode ser particularmente útil para aqueles que conhecem SQL e pretendem usar o pandas (ou vice-versa).

Para realizar os experimentos abaixo usamos o Jupyter Notebook, um aplicativo que roda dentro de um navegador, que pode ser facilmente instalado e permite a reprodução se todo o código aqui descrito. Você pode ler mais sobre Jupyter Notebook e Linguagem de Consultas SQL nesse site.

Esse texto é baseado em parte do manual do pandas e expandido. Ele usa um conjunto de dados baixados do github renomeado aqui para dfGorjeta. Nomes e valores dos campos foram traduzidos para o português.

# importar as bibliotecas necessárias
import pandas as pd
import numpy as np

url = "https://raw.github.com/pandas-dev/pandas/master/pandas/tests/io/data/csv/tips.csv"

dfGorjeta = pd.read_csv(url)
dfGorjeta.head()
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

Para efeito de testar os comandos do dataframe vamos alterar os nomes dos campos e traduzir os conteúdos dos dados. Para descobrir quais são os valores dos campos, sem repetições, transformamos as séries em sets, uma vez que valores de um set (conjunto) não se repetem.

print(set(dfGorjeta["sexo"]))
print(set(dfGorjeta["fumante"]))
print(set(dfGorjeta["dia"]))
print(set(dfGorjeta["hora"]))
{‘Male’, ‘Female’}
{‘No’, ‘Yes’}
{‘Sat’, ‘Sun’, ‘Fri’, ‘Thur’}
{‘Lunch’, ‘Dinner’}

No código seguinte alteramos os nomes de campos e traduzimos o conteúdo. A sintaxe da operação de edição do dataframe será discutida mais tarde no artigo:

# muda os nomes dos campos
dfGorjeta.rename(columns={"total_bill":"valor_conta", "tip":"gorjeta",
                        "smoker":"fumante", "sex":"sexo","day":"dia",
                        "time":"hora","size":"pessoas"}, inplace=True)

# traduzindo os valores dos campos:
dfGorjeta.loc[dfGorjeta["fumante"] == "No", "fumante"] = "não"
dfGorjeta.loc[dfGorjeta["fumante"] == "Yes", "fumante"] = "sim"
dfGorjeta.loc[dfGorjeta["sexo"] == "Female", "sexo"] = "mulher"
dfGorjeta.loc[dfGorjeta["sexo"] == "Male", "sexo"] = "homem"
dfGorjeta.loc[dfGorjeta["hora"] == "Dinner", "hora"] = "jantar"
dfGorjeta.loc[dfGorjeta["hora"] == "Lunch", "hora"] = "almoço"
dfGorjeta.loc[dfGorjeta["dia"] == "Fri", "dia"] = "sex"
dfGorjeta.loc[dfGorjeta["dia"] == "Sat", "dia"] = "sab"
dfGorjeta.loc[dfGorjeta["dia"] == "Sun", "dia"] = "dom"
dfGorjeta.loc[dfGorjeta["dia"] == "Thur", "dia"] = "qui"

# Temos agora o seguinte dataframe
dfGorjeta
valor_conta gorjeta sexo fumante dia hora pessoas
0 16.99 1.01 mulher não dom jantar 2
1 10.34 1.66 homem não dom jantar 3
2 21.01 3.50 homem não dom jantar 3
3 23.68 3.31 homem não dom jantar 2
4 24.59 3.61 mulher não dom jantar 4
239 29.03 5.92 homem não sab jantar 3
240 27.18 2.00 mulher sim sab jantar 2
241 22.67 2.00 homem sim sab jantar 2
242 17.82 1.75 homem não sab jantar 2
243 18.78 3.00 mulher não qui jantar 2

As consultas SQL realizadas a seguir pressupõe a existência de um banco de dados com o mesmo nome, a mesma estrutura e dados que o dataframe dfGorjetas.

SELECT

Nas consultas SQL as seleções são feitas com uma lista de nomes de campos que se deseja retornar, separados por vírgula, ou através do atalho * (asterisco) para selecionar todas as colunas. No pandas a seleção de colunas é feita passando-se uma lista de nomes de campos para o DataFrame. Uma chamada ao dataframe sem uma lista de nomes de colunas resulta no retorno de todas as colunas, da mesma forma que usar * no SQL.

–– sql: consulta (query) usando select
SELECT valor_conta, gorjeta, fumante, hora FROM dfGorjeta LIMIT 5;
# pandas:
dfGorjeta[["valor_conta", "gorjeta", "hora"]].head()
valor_conta gorjeta hora
0 16.99 1.01 jantar
1 10.34 1.66 jantar
2 21.01 3.50 jantar
3 23.68 3.31 jantar
4 24.59 3.61 jantar

O método head(n) limita o retorno do dataframe às n primeiras linhas. n = 5 é o default. Para listar as últimas linhas usamos tail(n). Linhas também podem ser selecionadas por chamadas ao sei indice.

# Para acessar as últimas linhas do dataframe podemos usar
# dfGorjeta[["valor_conta", "gorjeta", "hora"]].tail()

# selecionando linhas por meio de seu índice.
dfGorjeta.iloc[[1,239,243]]
valor_conta gorjeta sexo fumante dia hora pessoas
1 10.34 1.66 homem não dom jantar 3
239 29.03 5.92 homem não sab jantar 3
243 18.78 3.00 mulher não qui jantar 2

Os dataframes possuem a propriedade shape que contém sua dimensionalidade. No nosso caso temos

dfGorjeta.shape
(244, 7)

o que significa que são 244 linhas em 7 campos.

No SQL você pode retornar uma coluna resultado de um cálculo usando elementos de outras colunas. No pandas podemos usar o método assign() para inserir uma coluna calculada:

–– sql:
SELECT *, gorjeta/valor_conta*100 as percentual FROM dfGorjeta LIMIT 4;
# pandas: método assign()
dfGorjeta.assign(percentual = dfGorjeta["gorjeta"] / dfGorjeta["valor_conta" *100]).head(4)
valor_conta gorjeta sexo fumante dia hora pessoas percentual
0 16.99 1.01 mulher não dom jantar 2 5.944673
1 10.34 1.66 homem não dom jantar 3 16.054159
2 21.01 3.50 homem não dom jantar 3 16.658734
3 23.68 3.31 homem não dom jantar 2 13.978041

Essa coluna é retornada mas não fica anexada ao dataframe. Para anexar uma coluna ao dataframe podemos atribuir o resultado do cálculo a uma nova coluna:

dfGorjeta["percentual"] = dfGorjeta["gorjeta"] / dfGorjeta["valor_conta"] * 100
print("Nessa estapa temos as colunas:\n", dfGorjeta.columns)

# Vamos apagar a coluna recém criada para manter a simplicidade da tabela
dfGorjeta.drop(["percentual"], axis=1, inplace=True)
Nessa estapa temos as colunas:
Index([‘valor_conta’, ‘gorjeta’, ‘sexo’, ‘fumante’, ‘dia’, ‘hora’, ‘pessoas’, ‘percentual’],
dtype=’object’)

WHERE


Filtragem de dados em consultas SQL são feitas através da cláusula WHERE. DataFrames podem ser filtrados de várias formas diferentes. O indexamento com valores booleanos é provavelmente o mais simples:

–– cláusula WHERE do sql
SELECT * FROM dfGorjeta WHERE hora = "jantar" LIMIT 5;
# filtragem por indexamento no pandas
dfGorjeta[dfGorjeta["hora"] == "jantar"].head(5)
valor_conta gorjeta sexo fumante dia hora pessoas
0 16.99 1.01 mulher não dom jantar 2
1 10.34 1.66 homem não dom jantar 3
2 21.01 3.50 homem não dom jantar 3
3 23.68 3.31 homem não dom jantar 2
4 24.59 3.61 mulher não dom jantar 4

A consulta acima funciona da seguinte forma:

# is_jantar é uma série contendo True e False (True para jantares, False para almoços)
is_jantar = dfGorjeta["hora"] == "jantar"
# usamos display para exibir a contagem de falsos/verdadeiros
display("Quantos jantares/almoços:", is_jantar.value_counts())

# para negar a série inteira, invertendo True ↔ False usamos ~ (til)
# a linha abaixo imprime o número de almoços na tabela
print("A lista contém %d almoços" % dfGorjeta[~is_jantar]["hora"].count())

# também podemos obter a lista das entradas que não correspondem a "jantar" usando
# dfGorjeta[dfGorjeta["hora"] != "jantar"]
‘Quantos jantares/almoços:’
True 176
False 68
Name: hora, dtype: int64A lista contém 68 almoços

Quando essa série é passada para o dataframe apenas as linhas correspondentes à True são retornados. A última consulta é equivalente à dfGorjeta[~is_jantar].head().

No SQL podemos procurar por partes de uma string com a cláusula LIKE. No pandas transformamos o campo dfGorjeta["sexo"]em uma string que possui o método startswith("string").

–– sql: SELECT TOP 2 sexo, valor_conta FROM dfGorjeta WHERE sexo LIKE 'ho%';
dfGorjeta.loc[dfGorjeta['sexo'].str.startswith('ho'),['sexo','valor_conta']].head(2)

que retorna as 2 primeiras linhas em que o campo sexo começa com o texto “ho”.

Também podemos procurar por campos que estão incluidos em um conjunto de valores:

–– sql:
SELECT * FROM dfGorjeta WHERE dia IN ('sab', 'dom');
dfGorjeta.loc[dfGorjeta['dia'].isin(["dom", "sab"])]

que retorna todas as linhas em que o campo dia é “dom” ou “sab”.

Assim como se pode usar operadores lógicos AND e OR nas consultas SQL para inserir múltiplas condições, o mesmo pode ser feito com dataframes usando | (OR) e & (AND). Por ex., para listar as gorjetas com valor superior à $5.00 dadas em jantares:

–– SQL: múltiplas condições em WHERE
SELECT * FROM dfGorjeta WHERE hora = 'jantar' AND gorjeta > 6.00;
# no pandas
dfGorjeta[(dfGorjeta["hora"] == "jantar") & (dfGorjeta["gorjeta"] > 6.00)]
valor_conta gorjeta sexo fumante dia hora pessoas
23 39.42 7.58 homem não sab jantar 4
59 48.27 6.73 homem não sab jantar 4
170 50.81 10.00 homem sim sab jantar 3
183 23.17 6.50 homem sim dom jantar 4
212 48.33 9.00 homem não sab jantar 4
214 28.17 6.50 mulher sim sab jantar 3

Podemos obter uma lista dos dados correspondentes a gorjetas dadas por grupos com 5 ou mais pessoas ou com contas de valor acima de $45.00, limitada aos 4 primeiros registros:

–– SQL:
SELECT * FROM dfGorjeta WHERE pessoas >= 5 OR valor_conta > 45 LIMIT 4;
# pandas
dfGorjeta[(dfGorjeta["pessoas"] >= 5) | (dfGorjeta["valor_conta"] > 45)].head(4)
valor_conta gorjeta sexo fumante dia hora pessoas
59 48.27 6.73 homem não sab jantar 4
125 29.80 4.20 mulher não qui almoço 6
141 34.30 6.70 homem não qui almoço 6
142 41.19 5.00 homem não qui almoço 5

Dados ausentes são representados por NULL no, uma marca especial para indicar que um valor não existe no banco de dados. Nos dataframes do pandas o mesmo papel é desempenhado por NaN (Not a Number). Esses marcadores podem surgir, por ex., na leitura de um arquivo csv (valores separados por vírgulas) quando um valor está ausente ou não é um valor numérico em uma coluna de números. Para verificar o comportamento do pandas com NaN criamos um dataframe com valores ausentes. Verificações de nulos é feita com os métodos notna() e isna().

frame = pd.DataFrame({"col1": ["A", "B", np.NaN, "C", "D"], "col2": ["F", np.NaN, "G", "H", "I"]})
frame
col1 col2
0 A F
1 B NaN
2 NaN G
3 C H
4 D I

Se temos um banco de dados SQLcom essa estrutura e conteúdo podemos extrair as linhas onde col2 é NULL usando a consulta:

–– sql
SELECT * FROM frame WHERE col2 IS NULL;
# no case do pandas usamos
frame[frame["col2"].isna()]
col1 col2
1 B NaN

De forma análoga, podemos extrair as linhas para as quais col1 não é NULL. No pandas usamos notna().

–– sql
SELECT * FROM frame WHERE col1 IS NOT NULL;
# pandas: linhas em que col1 não é nula
frame[frame["col1"].notna()]
col1 col2
0 A F
1 B NaN
3 C H
4 D I

GROUP BY


No SQL consultas com agrupamentos são feitas usando-se as operações GROUP BY. No pandas existe o método groupby() que tipicamente particiona o conjunto de dados em grupos e aplica alguma função (em geral de agregamento), combinando depois os grupos resultantes.

Um exemplo comum é o de particionar os dados em grupos menores e contar os elementos desses grupos. Voltando ao nosso dataframe dfGorjeta podemos consultar quantas gorjetas foram dadas por grupos de cada sexo:

–– sql
SELECT sexo, count(*) FROM dfGorjeta GROUP BY sexo;
# o equivalente em pandas seria
dfGorjeta.groupby("sexo").size()
sexo
mulher 87
homem 157
dtype: int64

O resultado é uma series cujos valores podem ser retornados por seu nome de index ou pelo número desse indice.

print("A lista contém %d homens" % dfGorjeta.groupby("sexo").size()[0])
print("\t\t e %d mulheres" % dfGorjeta.groupby("sexo").size()["mulher"])
A lista contém 157 homens
e 87 mulheres

É possível aplicar o método count() para cada coluna, individualmente:

dfGorjeta.groupby("sexo").count()
valor_conta gorjeta fumante almoço hora pessoas
sexo
mulher 87 87 87 87 87 87
homem 157 157 157 157 157 157

Observe que no código do pandas usamos size() e não count(). Isso foi feito porque o método count() é aplicado sobre cada coluna e retorna tantos valores quantas colunas existem, com valores não null.

Também se pode aplicar o método count() para uma coluna específica:

# para contar valores em uma única coluna primeiro ela é selecionada, depois contada
dfGorjeta.groupby("sexo")["valor_conta"].count()
sexo
mulher 87
homem 157
Name: valor_conta, dtype: int64

Existem diversas funções de agregamento. São elas:

função descrição
mean() calcula médias para cada grupo
sum() soma dos valores do grupo
size() *tamanhos dos grupos
count() número de registros no grupo
std() desvio padrão dos grupos
var() variância dos grupos
sem() erro padrão da média dos grupos
describe() gera estatísticas descritivas
first() primeiro valor no grupo
last() último valor no grupo
nth() n-ésimo valor (ou subconjunto se n for uma lista)
min() valor mínimo no grupo
max() valor máximo no grupo

* A função size() retorna o número de linhas em uma serie e o número de linhas × colunas em dataframes.

Para obter um resumo estatístico relativo ao campo gorjeta, agrupado pelo campo sexo podemos usar:

dfGorjeta.groupby("sexo")["gorjeta"].describe()
count mean std min 25% 50% 75% max
sexo
homem 157.0 3.089618 1.489102 1.0 2.0 3.00 3.76 10.0
mulher 87.0 2.833448 1.159495 1.0 2.0 2.75 3.50 6.5

Múltiplas funções podem ser aplicadas de uma vez. Suponha que queremos determinar como os valores das gorjetas variam por dia da semana. O método agg() (de agregar) permite que se passe um dicionário para o dataframe agrupado, indicando que função deve ser aplicada a cada coluna.

–– sql (agrupe os dados por dia, calcule a média para cada dia e o número de entradas contadas)
SELECT dia, AVG(gorjeta), COUNT(*) FROM dfGorjeta GROUP BY dia;
# na pandas, use mean no campo gorjeta, size no campo dia
dfGorjeta.groupby("dia").agg({"gorjeta": np.mean, "dia": np.size})
gorjeta dia
dia
dom 3.255132 76
qui 2.771452 62
sab 2.993103 87
sex 2.734737 19

Também é possível realizar o agrupamento por mais de uma coluna. Para fazer isso passamos uma lista de colunas para o método groupby().

–– agrupe primeiro por "fumante", depois por "dia"
–– realize a contagem dos registros e a média das gorjetas
SELECT fumante, dia, COUNT(*), AVG(gorjeta) FROM dfGorjeta GROUP BY fumante, dia;
# no pandas
dfGorjeta.groupby(["fumante", "dia"]).agg({"gorjeta": [np.size, np.mean]})
gorjeta
size mean
fumante dia
não dom 57.0 3.167895
qui 45.0 2.673778
sab 45.0 3.102889
sex 4.0 2.812500
sim dom 19.0 3.516842
qui 17.0 3.030000
sab 42.0 2.875476
sex 15.0 2.714000

JOIN

No SQL tabelas podem ser juntadas ou agrupadas através da cláusula JOIN. Junções podem ser LEFT, RIGHT, INNER, FULL. No pandas se usa os métodos join() ou merge(). Por defaultjoin() juntará os DataFrames por seus índices. Cada método tem parâmetros que permitem escolher o tipo da junção (LEFT, RIGHT, INNER, FULL), ou as colunas que devem ser juntadas (por nome das colunas ou índices). [Linguagem de Consultas SQL]

# para os exercícios que se seguem criamos os dataframes
df1 = pd.DataFrame({"key": ["A", "B", "C", "D"], "value":  [11, 12, 13, 14]})
df2 = pd.DataFrame({"key": ["B", "D", "D", "E"], "value":  [21, 22, 23, 24]})
# para exibir esses dataframes com formatação usamos display()
display(df1)
display(df2)
key value
0 A 11
1 B 12
2 C 13
3 D 14
key value
0 B 21
1 D 22
2 D 23
3 E 24

Como antes supomos a existência de duas tabelas de dados sql como as mesmas estruturas e dados para considerarmos as várias formas de JOINs.

INNER JOIN

–– junção das duas tabelas ligadas por suas chaves - key
SELECT * FROM df1 INNER JOIN df2 ON df1.key = df2.key;
# por default merge() faz um INNER JOIN
pd.merge(df1, df2, on="key")
key value_x value_y
0 B 12 21
1 D 14 22
2 D 14 23

O método merge() também oferece parâmetros para que sejam feitas junções de uma coluna de um dataframe com o índice de outro dataframe. Para ver isso vamos criar outro dataframe a partir de df2, usando o campo key como índice.

# novo dataframe tem campo "key" como índice
df2_indice = df2.set_index("key")
display(df2_indice)
pd.merge(df1, df2_indice, left_on="key", right_index=True)
value
key
B 21
D 22
D 23
E 24
key value_x value_y
1 B 12 21
3 D 14 22
3 D 14 23

LEFT OUTER JOIN

A junção LEFT OUTER JOIN recupera todos as campos à esquerda, existindo ou não uma linha correspondente à direita. O parâmetro how="left" é o equivalente no pandas.

–– sql: recupera todos os valores de df1 existindo ou não correspondente em df2
SELECT * FROM df1 LEFT OUTER JOIN df2 ON df1.key = df2.key;
# pandas: how="left" equivale a LEFT OUTER JOIN
pd.merge(df1, df2, on="key", how="left")
key value_x value_y
0 A 11 NaN
1 B 12 21
2 C 13 NaN
3 D 14 22
4 D 14 23

Observe que df2 não possui campos com key = "A" ou key = "C" e, por isso o dataframe resultante tem NaN nessas entradas. key = "A". Como df2 tem 2 linhas para key = "D" a linha aparece duplicada para essa key em df1.

RIGHT JOIN

A junção RIGH OUTER JOIN recupera todos as campos à direita, existindo ou não uma linha correspondente à esquerda. O parâmetro how="right" é o equivalente no pandas.

–– sql: recupera todos os registros em df2
SELECT * FROM df1 RIGHT OUTER JOIN df2 ON df1.key = df2.key;
# pandas: how="right" equivale a RIGHT OUTER JOIN
pd.merge(df1, df2, on="key", how="right")
key value_x value_y
0 B 12 21
1 D 14 22
2 D 14 23
3 E NaN 24

FULL JOIN

A junção FULL OUTER JOIN recupera todos as campos à direita ou à esquerda, representando como NaN os valores ausentes em uma ou outra. Todos as linhas das duas tabelas são retornadas com junção onde a campo key existe em ambas. O parâmetro how="outer" é o equivalente no pandas. Observe que nem todos os gerenciadores de bancos de dados permitem essa operação.

–– sql: retorna todos os registros em ambas as tabelas
SELECT * FROM df1 FULL OUTER JOIN df2 ON df1.key = df2.key;
# pandas: how="outer" é o equivalente em dataframes
pd.merge(df1, df2, on="key", how="outer")
key value_x value_y
0 A 11 NaN
1 B 12 21
2 C 13 NaN
3 D 14 22
4 D 14 23
5 E NaN 24

UNION

Para os exemplos seguintes definimos mais 2 dataframes:

df3 = pd.DataFrame({"cidade": ["Rio de Janeiro", "São Paulo", "Belo Horizonte"], "nota": [1, 2, 3]})
df4 = pd.DataFrame({"cidade": ["Rio de Janeiro", "Curitiba", "Brasília"], "nota": [1, 4, 5]})

No SQL a clásula UNION ALL é usada para juntar as linhas retornadas em dois (ou mais) instruções de SELECT. Linhas duplicadas são mantidas. O mesmo efeito pode ser conseguido no pandas usando-se o método concat().

–– sql: UNION ALL
SELECT city, rank FROM df3 UNION ALL SELECT cidade, nota FROM df4;
# pandas: concat
pd.concat([df3, df4])
cidade nota
0 Rio de Janeiro 1
1 São Paulo 2
2 Belo Horizonte 3
0 Rio de Janeiro 1
1 Curitiba 14
2 Brasília 5

No SQL a cláusula UNION tem o mesmo efeito que UNION ALL mas remove as linhas duplicadas. No pandas isso pode ser conseguido se fazendo a conactenação concat() seguida de drop_duplicates().

–– SQL UNION
SELECT city, rank FROM df1 UNION SELECT city, rank FROM df2;
–– o registro duplicado no Rio de Janeiro fica excluído
# pandas: concat() seguido de drop_duplicates()
pd.concat([df1, df2]).drop_duplicates()
cidade nota
0 Rio de Janeiro 1
1 São Paulo 2
2 Belo Horizonte 3
1 Curitiba 14
2 Brasília 5

Outras funções analíticas e de agregamento

Para os próximos exemplos vamos retornar ao nosso dataframe dfGorjeta: para listar as 5 gorjetas mais altas, no MySQL (a sintaxe varia de um para outro gerenciador).

–– MySQL: retorna todos os campos em ordem decrescente, 5 linhas
SELECT * FROM dfGorjeta ORDER BY gorjeta DESC LIMIT 10 OFFSET 5;
# pandas: seleciona 15 maiores e exibe as 10 de menor valor
dfGorjeta.nlargest(15, columns="gorjeta").tail(10)
valor_conta gorjeta sexo fumante dia hora pessoas
183 23.17 6.50 homem sim Dom jantar 4
214 28.17 6.50 mulher sim sab jantar 3
47 32.40 6.00 homem não Dom jantar 4
239 29.03 5.92 homem não sab jantar 3
88 24.71 5.85 homem não Thur almoço 2
181 23.33 5.65 homem sim Dom jantar 2
44 30.40 5.60 homem não Dom jantar 4
52 34.81 5.20 mulher não Dom jantar 4
85 34.83 5.17 mulher não Thur almoço 4
211 25.89 5.16 homem sim sab jantar 4

UPDATE

Há muitas formas de alterar um valor em um campo de um dataframe. Por exemplo, abaixo realizamos uma alteração em todos os valores de gorjeta sempre que gorjeta < 2.

–– sql: em todas as linhas duplique a gorjeta se gorjeta for menor que 1.1
UPDATE dfGorjeta SET gorjeta = gorjeta*2 WHERE gorjeta < 1.1;
# pandas: o mesmo resultado pode ser obtido da aseguinte forma
# dfGorjeta.loc[dfGorjeta["gorjeta"] < 1.1, "gorjeta"] *= 2

Para explicar com mais detalhes o funcionamento deste código, armazenamos abaixo a lista dos índices das linhas de gorjetas mais baixas e exibimos essas linhas. Em seguida multiplicamos apenas as gorjetas dessas linhas por 2 e examinamos o resultado:

indices = dfGorjeta[dfGorjeta["gorjeta"] < 1.1].index
print("Índices de gorjetas < 1.1:", indices)
display("Lista de gorjetas < 1.1", dfGorjeta.iloc[indices])
# multiplica essas gorjetas por 2
dfGorjeta.loc[dfGorjeta["gorjeta"] < 1.1, "gorjeta"] *= 2
# lista as mesmas linhas após a operação
display("Gorjetas após a operação:", dfGorjeta.iloc[indices])
Índices de gorjetas < 1.1: Int64
Index([0, 67, 92, 111, 236], dtype=’int64′)
‘Lista de gorjetas < 1.1’

valor_conta gorjeta sexo fumante dia hora pessoas
0 16.99 1.01 mulher não dom jantar 2
67 3.07 1.00 mulher sim sab jantar 1
92 5.75 1.00 mulher sim sex jantar 2
111 7.25 1.00 mulher não sab jantar 1
236 12.60 1.00 homem sim sab jantar 2

‘Gorjetas após a operação:’

valor_conta gorjeta sexo fumante dia hora pessoas
0 16.99 2.02 mulher não dom jantar 2
67 3.07 2.00 mulher sim sab jantar 1
92 5.75 2.00 mulher sim sex jantar 2
111 7.25 2.00 mulher não sab jantar 1
236 12.60 2.00 homem sim sab jantar 2
–– sql: alterar um campo de uma linha específica (supondo a existência de um campo id)
UPDATE dfGorjeta SET sexo = 'NI' WHERE id = 239
# para alterar o campo sexo para 'NI' (não informado)
dfGorjeta.loc[239, 'sexo'] ='NI'

DELETE

Existem muitas formas de se excluir linhas de um dataframe mas é comum a prática de selecionar as linhas que devem ser mantidas e copiar para um novo dataframe.

–– sql: linhas são apagadas sob um certo critério
DELETE FROM dfGorjeta WHERE gorjeta > 9;
# pandas: como novo dataframe tem o mesmo nome do original, o antigo é sobrescrito e perdido
dfTop = dfGorjeta.loc[dfGorjeta["gorjeta"] > 9]
dfTop
valor_conta gorjeta sexo fumante dia hora pessoas
170 50.81 10.0 homem sim sab jantar 3

Também é possível apagar linhas usando seu índice:

# apagar linha com index = 4, inplace para substituir o dataframe
dfGorjeta.drop(index=4, inplace=True)
# apagar linhas com index = 0 até 3
dfGorjeta.drop(index=[0, 1, 2, 3], inplace=True)
dfGorjeta.head()
valor_conta gorjeta sexo fumante dia hora pessoas
5 25.29 4.71 homem não dom jantar 4
6 8.77 12.00 homem não dom jantar 2
7 26.88 3.12 homem não dom jantar 4
8 15.04 1.96 homem não dom jantar 2
9 14.78 3.23 homem não dom jantar 2
🔺Início do artigo

Bibliografia

  • McKinney, Wes: Python for Data Analysis, Data Wrangling with Pandas, NumPy,and IPython
    O’Reilly Media, 2018.
  • Pandas: página oficial, acessada em janeiro de 2021.

Biblioteca FactorAnalyser



FactorAnalyzer é um módulo Python para realizar análise fatorial exploratórias (AFE) e confirmatória (AFC). Na análise exploratória se pode usar várias técnicas de estimativa. É possível executar AFE usando (1) solução residual mínima (MINRES), (2) uma solução de máxima verossimilhança ou (3) uma solução de fator principal. A análise confirmatória só pode ser executada usando uma solução de máxima verossimilhança.

As classes da biblioteca são compatíveis com scikit-learn. Partes do código são portadas da biblioteca psych do R, e a classe AFC foi baseada na biblioteca sem. A classe factor_analyzer.factor_analyzer.FactorAnalyzer se utiliza de sklearn.base.BaseEstimator e sklearn.base.TransformerMixin.

Na AFE as matrizes de carga fatorial são geralmente submetidas a rotações após o modelo de análise fatorial ser estimado, buscando-se produzir uma estrutura mais simples e de fácil interpretação na identificação das variáveis que estão carregando em um determinado fator.

Os dois tipos mais comuns de rotações são:

  • Rotação ortogonal varimax, que gira a matriz de cargas fatoriais de forma a maximizar a soma da variância das cargas quadradas, preservando a ortogonalidade da matriz de carga.
  • A rotação oblíqua promax, baseada na rotação varimax, mas buscando maior correlaçãp dos fatores.

Essa biblioteca inclui o módulo analisador de fatores com a classe FactorAnalyzer. Essa classe inclui os métodos fit() e transform() para realizar a análise e pontuar novos dados usando o modelo de fatores ajustados. Rotações opcionais podem ser realizadas sobre a matriz de cargas fatoriais com a classe Rotator.

Os seguintes métodos de rotação estão disponíveis tanto para FactorAnalyzer quanto para Rotator:

  • varimax (rotação ortogonal),
  • promax (oblíqua),
  • oblimin (oblíqua),
  • oblimax (ortogonal),
  • quartimin (oblíqua),
  • quartimax (ortogonal),
  • equamax (ortogonal),
  • geomin_obl (oblíqua),
  • geomin_ort (ortogonal).

A biblioteca também inclui o módulo confirmatory_factor_analyzer com a classe ConfirmatoryFactorAnalyzer. A classe contém os métodos fit() e transform() que realizam análises fatoriais confirmatórias e pontuam novos dados usando o modelo ajustado. A execução do CFA requer que os usuários especifiquem previamente o modelo com as relações de carga fatorial esperadas, o que pode ser feito usando a classe ModelSpecificationParser.

# A biblioteca pode ser instalada usando pip:
$ pip install factor_analyzer

# ou conda:
$ conda install -c ets factor_analyzer

Ele depende de Python 3.4 (ou superior), numpy, pandas, scipy, scikit-learn.

A classe factor_analyzer possui um método para ajustar um modelo de análise e fazer a análise fatorial usando solução residual mínima (MINRES), solução de máxima verossimilhança ou (3) solução de fator principal. Retorna a matriz de cargas fatoriais. Por default não realiza nenhuma rotação. Opcionalmente pode realizar uma rotação usando os métodos citados abaixo.

Análise Fatorial Exploratória

Módulo factor_analyzer.factor_analyzer

class
factor_analyzer.factor_analyzer.FactorAnalyzer(n_factors=3, rotation='promax', method='minres',
                                               use_smc=True, is_corr_matrix=False, bounds=(0.005, 1),
                                               impute='median', rotation_kwargs=None)
Parâmetros:
n_factors
(int, opcional) – Número de fatores a ajustar. Default: 3.
rotation
(str, opcional) – Default: None.
Define o tipo de rotação a realizar após o ajuste do modelo. Nenhuma rotação será realizada se rotation = None e nenhuma normalização de Kaiser será aplicada. Os seguintes métodos de rotação estão disponíveis:

  • varimax (rotação ortogonal),
  • promax (oblíqua),
  • oblimin (oblíqua),
  • oblimax (ortogonal),
  • quartimin (oblíqua),
  • quartimax (ortogonal),
  • equamax (ortogonal),

sendo rotation = ‘promax’ o default.

method
({‘minres’, ‘ml’, ‘principal’}, opcional) – Método de ajuste a ser usado: solução residual mínima (MINRES), uma solução de máxima verossimilhança ou de fator principal. Default: minres.
use_smc
(bool, opcional) – Uso de correlação multipla quadrada (SMC, square multiple correlation) como ponto de partida para a análise. Default: True.
bounds
(tuple, opcional) – Limites inferior e superior das variáveis para a otimização “L-BFGS-B”. Default: (0.005, 1).
impute
({‘drop’, ‘mean’, ‘median’}, opcional) – Se existem valores faltantes nos dados usar apagamento na lista inteira (‘drop’) ou substituir valores usando a mediana (‘median’) ou a média (‘mean’) da coluna.
use_corr_matrix
(bool, opcional) – Ajuste para Verdadeiro se os dados de entrada já são a matriz de correlação. Default: False.
rotation_kwargs
(opcional) – argumentos adicionais passados para o método de rotação.
Propriedades:
loadings
A matriz de cargas fatoriais. Default: None se o método fit() não foi chamado.
Tipo: numpy array
corr
A matriz original de correlação. Default: None se o método fit() não foi chamado.
Tipo: numpy array
rotation_matrix
A matriz de rotação, se uma rotação não foi executada.
Tipo: numpy array
structure
A estrutura da matriz de cargas fatoriais. (Só existe se rotação for promax).
Tipo: numpy array ou None
psi
A matriz de fatores de correlação. (Só existe se rotação for oblíqua).
Tipo: numpy array ou None
Métodos:
fit(X, y=None)
Faz a análise fatorial sobre os dados de X usando ‘minres’, ‘ml’, ou ‘principal solutions’. Default: SMC (correlação múltipla quadrada)
Parâmetros:
     X (variável tipo array) – Dados originais a serem analisados.
     y (não é utilizado).
get_communalities()
Calcula as comunalidades, dada uma matriz de cargas fatoriais.
Retorna: communalities (numpy array), as comunalidades.
get_eigenvalues()
Calcula os autovalores, dada uma matriz de correlações fatoriais.
Retorna: original_eigen_values (numpy array), os autovalores originais.
    common_factor_eigen_values(numpy array), os autovalores dos fatores comuns.
get_factor_variance()
Calcula informações de variância, inclusive a variância, variância proporcional e cumulativa para cada fator.
Retorna: variance (numpy array) – A variância dos fatores.
    proportional_variance (numpy array) – A variância proporcional dos fatores.
    cumulative_variances (numpy array) – A variância cumulativa dos fatores.
get_uniquenesses()
Calcula a unicidade, dada uma matriz de cargas fatoriais.
Retorna: uniquenesses (numpy array) – A unicidade da matriz de cargas fatoriais.
transform(X)
Retorna a pontuação de fatores para um novo conjunto de dados.
Parâmetros: X (variável de array, shape (n_amostras, n_características)) – Os dados a pontuar usando o modelo de fatores ajustado.
Retorna: X_new – As variáveis latentes de X. (um numpy array, shape (n_amostras, n_componentes))

Testes de Bartlett e KMO

factor_analyzer.factor_analyzer.calculate_bartlett_sphericity(x)

Testa a hipótese de que a matriz de correlação é igual à matriz identidade (o que significa que não há correlação entre as variáveis).

Parâmetro
X (variável de array) – Dados sobre os quais calcular a esfericidade.
Retorna:
statistic (float) – O valor de chi-quadrado, \chi^2.
p_value (float) – O valor de p-value associado ao teste.
factor_analyzer.factor_analyzer.calculate_kmo(x)

Calcula o critério de Kaiser-Meyer-Olkin para cada fator individual e para o conjunto fatores. Essa estatística representa o grau com que cada variável observada pode ser predita, sem erro, pelas demais variáveis. Na maior parte dos casos KMO < 0.6 é considerado inadequado.

Parâmetros:
x (variável de array) – Dados sobre os quais calcular coeficientes KMOs.
Retorna:
kmo_per_variable (array numpy) – O KMO para cada item.
kmo_total (float) – A pontuação KMO geral.

Análise Fatorial Confirmatória

Módulo factor_analyzer.confirmatory_factor_analyzer

class
factor_analyzer.confirmatory_factor_analyzer.ConfirmatoryFactorAnalyzer(specification=None,
                                n_obs=None, is_cov_matrix=False, bounds=None, max_iter=200,
                                tol=None, impute='median', disp=True)

A classe ConfirmatoryFactorAnalyzer ajusta um modelo de análise fatorial confirmatória usando o modelo de máxima verosimelhança (maximum likelihood).

Parâmetros:
specification
(ModelSpecification object ou None, opcional) – Um modelo de especificação ModelSpecification ou None. Se None for fornecido o ModelSpecification será gerado supondo que o número de fatores n_factors é o mesmo que o número de variáveis, n_variables, e que todas as variáveis carregam em todos os fatores. Note que isso pode significa que o modelo não foi identificado e a otimização poderá falhar. Default: None.
n_obs
(int ou None, opcional) – Número de observações no conjunto original de dados. Se não for passado e is_cov_matrix=True um erro será lançado. Default: None.
is_cov_matrix
(bool, opcional) – Informa se a matriz de entrada X é a matriz de covariância. Se False o conjunto completo de dados é considerado. Default: False.
bounds
(lista de tuplas ou None, opcional) – Uma lista de limites mínimos e máximos para cada elemento do array de entrada. Deve ser igual a x0 que é o array de entrada do modelo de especificação.

O comprimento é: ((n_factors * n_variables) + n_variables + n_factors + (((n_factors * n_factors) – n_factors) // 2)

Se bounds=None nenhum limite será tomado. Default: None.

max_iter
(int, opcional) – Número máximo de iterações na rotina de otimização. Default: 200.
tol
(float ou None, opcional) – Tolerância para a convergência. Default: None.
disp
(bool, opcional) – Se a mensagem da otimização do scipy fmin é enviado para o standard output. Default: True.
Exceção levantada
ValueError – Se is_cov_matrix = True e n_obs não é fornecido.
Propriedades:
model
Objeto modelo de especificação.
Tipo: ModelSpecification.
loadings_
A Matriz da Cargas fatoriais.
Tipo: numpy array
error_vars_
A matriz de variância de erros (Tipo: numpy array).
factor_varcovs_
A Matriz de covariância. (Tipo: numpy array).
log_likelihood_
O log de verossimilhança da rotina de otimização. (Tipo: float).
aic_
O critério de informação de Akaike. (Tipo: float).
bic_
O critério de informação Bayesiano. (Tipo: float).
Métodos:
fit(X, y=None)
realiza a análise fatorial confirmatória sobre os dados de X usando máxima verossimilhança.
Parâmetros:
X (variável tipo array) – Dados originais a serem analisados. Se X for a matriz de covariância é necessário marcar is_cov_matrix = True.
y (não é utilizado)
Exceção levantada:
ValueError – Se a especificação não é um objeto ModelSpecification nem None.
AssertionError – Se is_cov_matrix = True e a matriz fornecida não é quadrada.
AssertionError – If len(bounds) != len(x0)
get_model_implied_cov()
Retorna a matriz de covariância implícita no modelo, se o modelo foi estimado. (numpy array)
get_standard_errors()
Lê os erros padrão da matriz de covariância implícita e das médias implícitas.
Retorna:
loadings_se (numpy array) – Os erros padrões para os cargas fatoriais.
error_vars_se (numpy array) – Os erros padrões para as variâncias de erros.
transform(X)
Lê as pontuações para os fatores do novo conjunto de dados.
Parâmetros: X (tipo array, shape (n_amostras, n_características)) – Os dados a serem pontuados usando o modelo fatorial ajustado.
Retorna: pontuações – As variáveis latentes de X (um numpy array, shape (n_amostras, n_componentes))
class
factor_analyzer.confirmatory_factor_analyzer.ModelSpecification(loadings, n_factors, n_variables,
                                                                factor_names=None, variable_names=None)

Uma classe para encapsular a especificação do modelo para a análise confirmatória. Esta classe contém várias propriedades de especificação que são usadas no procedimento AFC.

Parâmetros:
loadings
(tipo array) – Especificação de cargas fatoriais.
error_vars (array-like) – Especificação de variância do erro
factor_covs (tipo array) – Especificação da covariância do fator.
factor_names (lista de str ou None) – uma lista de nomes de fatores, se disponível. Default: None.
variable_names (lista de str ou None) – Uma lista de nomes de variáveis, se disponível. Default: None.
Propriedades:
loadings
Especificação das cargas fatoriais. (Tipo: numpy array)
error_vars
Especificação da variância de erros. (Tipo: numpy array)
factor_covs
Especificação do fator de covariância. (Tipo: numpy array)
n_factors
Número de fatores. (Tipo: int).
n_variables
Número de variáveis. (Tipo: int).
n_lower_diag
Número de elementos no array factor_covs, igual à diagonal inferior da matriz de covariância fatorial. (Tipo: int).
loadings_free
Índices dos parâmetros de cargas fatoriais “livres”. (Tipo: numpy array)
error_vars_free
Índices dos parâmetros de variância de erros “livres”. (Tipo: numpy array)
factor_covs_free
Índices dos parâmetros de covariância de fator “livre”. (Tipo: numpy array)
factor_names
Lista dos nomes dos fatores, se disponível. (Tipo: lista de str ou None).
variable_names
Lista dos nomes das variáveis, se disponível. (Tipo: lista de str ou None).
error_vars
error_vars_free
factor_covs
factor_covs_free
factor_names
Métodos:
get_model_specification_as_dict()
Retorna model_specification – O modelo de especificações, chaves e valores, como um dicionario (dict).
copy()
Retorna uma “deep copy” do objeto.
loadings
loadings_free
n_factors
n_lower_diag
n_dtiables
dtiable_names

Analisador de especificação de modelo

class factor_analyzer.confirmatory_factor_analyzer.ModelSpecificationParser

Uma classe para gerar a especificação do modelo para Análise Fatorial Confirmatória. Inclui dois métodos estáticos para gerar o objeto ModelSpecification a partir de um dicionário ou de um array numpy.

static parse_model_specification_from_array(X, specification=None)

Gera a especificação do modelo a partir de um array. As colunas devem corresponder aos fatores e as linhas às variáveis. Se este método for usado para criar a ModelSpecification nenhum nome de fator ou nome de variável serão adicionadoa como propriedades desse objeto.

Parâmetros:
X (tipo array) – O conjunto de dados será usado para a AFC.
specification (tipo array or None) – Array com os detalhes das cargas.
Se specification = None a matriz será criada supondo-se que todas as variáveis carregam em todos os fatores. Default: None.
Retorna:
Objeto modelo de especificação, ModelSpecification.
Exceção levantada:
ValueError se especificação não está no formato esperado.
static parse_model_specification_from_dict(X, specification=None)

Gere a especificação do modelo a partir de um dicionário. As chaves no dicionário devem ser os nomes dos fatores e os valores devem ser os nomes das características (features). Se esse método for usado para criar uma ModelSpecification os nomes dos fatores e das variáveis serão adicionados como propriedades a esse objeto.

Parâmetros:
X (tipo array) – O conjunto de dados será usado para a AFC.
specification (tipo array or None) – Dicionário com os detalhes das cargas. Se None a matriz será criada supondo-se que todas as variáveis carregam em todos os fatores. Defaults = None.
Retorna: Objeto modelo de especificação, ModelSpecification.
Exceção levantada: ValueError se especificação não está no formato esperado.

Módulo factor_analyzer.rotator

Classe para executar diversas rotações nas matrizes de cargas fatoriais. Os métodos de rotação listados abaixo estão disponíveis tanto para FactorAnalyzer quanto para Rotator.

class
factor_analyzer.rotator.Rotator(method='varimax', normalize=True, power=4,
                                kappa=0, gamma=0, delta=0.01, max_iter=500, tol=1e-05)
Parâmetros:
method
(str, opcional). Métodos de rotação disponíveis: Default: ‘varimax’.

  • varimax (rotação ortogonal),
  • promax (oblíqua),
  • oblimin (oblíqua),
  • oblimax (ortogonal),
  • quartimin (oblíqua),
  • quartimax (ortogonal),
  • equamax (ortogonal),
  • geomin_obl (oblíqua),
  • geomin_ort (ortogonal).
normalize
(bool ou None, opcional) – Informa se uma normalização de Kaiser e de-normalização devem ser efetuadas antes e após a rotação. Usada para as rotações varimax e promax. Se None o default para promax é False, default para varimax é True. Default: None.
power
(int, opcional) – Potência das gargas de promax (menos 1). Geralmente na faixa entre 2 e 4. Default: 4.
kappa
(int, opcional) – Valor kappa para o objetivo equamax. Ignorado se método não for ‘equamax’. Default: 0.
gamma
(int, opcional) – Nível gamma para o objetivo ‘oblimin’. Ignorado se método não for ‘oblimin’. Default: 0.
delta
(float, opcional) – Nível delta level para o objetivo ‘geomin’. Ignorado se método não for ‘geomin_*’. Default: 0.01.
max_iter
(int, opcional) – Número máximo de iterações. Usado para método ‘varimax’ e rotações oblíquas. Default: 1000.
tol
(float, opcional) – Limite (threshold) de convergência. Usado para método ‘varimax’ e rotações oblíquas. Default: 1e-5.
Propriedades:
loadings_
A matriz de carregamentos. (Tipo: numpy array, shape (n_características, n_fatores)

rotation_

Matriz de rotação. (Tipo: numpy array, shape (n_fatores, n_fatores))

psi_

Matriz de correlações fatoriais. Existe apenas se a rotação é oblíqua. (Tipo: numpy array ou None)

Métodos:
fit(X, y=None)
Computa o fator de rotação model_specification – O modelo de especificações, chaves e valores, como um dicionario (dict).

Parâmetros
X (tipo array) – A matriz (n_características, n_fatores) de cargas fatoriais
y (não utilizado)
Retorna
self
fit_transform(X, y=None)
Computa o fator de rotação e retorna a nova matriz de cargas fatoriais.

Parâmetros
X (tipo array) – A matriz (n_características, n_fatores) de cargas fatoriais
y (não utilizado)
Retorna
loadings_, a matriz (n_características, n_fatores) de cargas fatoriais (numpy array)
Erro levantado
ValueError – se o método não está na lista de métodos aceitáveis.

Bibliografia

Jupyter Notebook


O que é o Jupyter Notebook?

Jupyter Notebook é uma ferramenta poderosa para o desenvolvimento projetos de ciência de dados de forma interativa que é executada de dentro de seu browser. Além da execução interativa de código ele pode ser usado para a preparação de apresentações dinâmicas onde texto pode ser bem formatado contendo imagens, gráficos interativos e fórmulas matemáticas. O conteúdo das linhas de código e seus outputs (as respostas produzidas pelo código) ficam bem delimitadas.

Ele faz parte do Projeto Jupyter de código aberto e free software. É possível baixar e instalar gratuitamente o Jupyter Notebooks separadamente ou como parte do kit de ferramentas de ciência de dados Anaconda. Com ele se pode usar vários ambientes de execução (kernels ou núcleos) com linguagens de programação como o Python, R, Julia, C++, Scheme, Haskell e Ruby.

O Jupyter Notebook está se tornando uma interface muito popular para a computação na nuvem e muitos dos provedores desse tipo de serviço o adotaram como interface front-end para seus usuários. Entre eles estão o SageMaker Notebooks da Amazon, o Colaboratory do Google e o Azure Notebook da Microsoft. O Colaboratory (também chamado de Colab) é um ambiente gratuito que armazena e roda notebooks no Google Drive.

Os arquivos no Notebook são armazenados no formato JSON com a extensão *.ipynb. Eles podem ser lidos pelo aplicativo instalado (que usa o browser para sua exibição), e exportado para várias formatos, entre eles: html, markdown, pdf, latex e slides Reveal.js. O arquivo consiste de várias células que são exibidas como uma página usual de html. Algumas células são de código e seus resultados, outras de texto, imagens ou outras mídias. Alternativamente eles podem ser lidos e exibidos (de forma não interativa) pelo nbviewer (na web).

Instalação

A forma mais simples de instalação do Jupyter Notebooks consiste em instalar o Anaconda, a distribuição Python mais usada para ciência de dados. Esta instalação inclui as princiais bibliotecas e ferramentas mais populares, como NumPy, Pandas e Matplotlib.

Para obter o Anaconda:

  • Baixe a versão mais recente do Anaconda aqui. Existem instaladores gráficos para Linux, Windows e MacOS.
  • Instale seguindo as instruções na página de download ou contidas no arquivo instalador executável.
  • ou … usuários que conhecem Python e que preferem gerenciar seus pacotes manualmente, pode apenas usar:
    pip3 install jupyter.

Executando e Criando um Notebook

No Windows na MacOS ou Linux um atalho para Jupyter Notebook é criado no menu do sistema. Caso nenhum atalho tenha sido criado você pode criar um ou iniciar usando o console. No Linux se digita jupyter-notebook no console. Uma janela com a seguinte aparência é aberta no browser padrão:

A primeira página exibe o painel de controle do Jupyter (dashboard). Ele dá acesso aos arquivos e subpastas que estão na pasta de inicialização do Jupyter. Esta pasta, no entanto, pode ser alterada. O painel de controle tem uma interface amigável por onde você pode controlar suas pastas e notebooks. Toda a descrição que usamos aqui se refere à aparência default dos notebooks. Essa aparência pode ser alterada modificando-se o arquivo css em /home/nome-usuario/.jupyter/custom/custom.css (no Linux).

O Jupyter Notebook fica aberto no navegador usando uma URL especial, do tipo “http://localhost:8888/tree”. Isso indica que o servidor dessa página esta em sua máquina local.

Você pode criar uma pasta nova para seus projetos clicando em New Folder. Depois pode renomear sua pasta selecionado e clicando em Rename. Clique na pasta para abrí-la e depois em New: Python 3 (notebook). Um notebook vazio e sem nome é criado. Clique nele e em File | Rename e dê um nome apropriado para o seu notebook. Vários notebooks podem ser abertos ao mesmo tempo.

Um notebook aberto apresenta a seguinte interface:

Os menus superiores dão acesso as funcionalidades de gravar arquivo, importar e exportar para diferentes formatos, etc. O item File | Save and Checkpoint (Ctrl-S) permite guardar o estado as sessão no momento de forma a poder ser recuperada mais tarde por meio de File | Revert to Checkpoint. Para fechar uma sessão não basta fechar a aba do navegador pois isso deixaria ativa a tarefa na memória do computador. Para isso você pode usar File | Close and Halt ou Kernel | Shutdown. Também se pode clicar em LOGOUT no canto superior direito.

Ainda na barra de menus você pode clicar em Help | User interface tour para ver alguns pontos relevantes no notebook. Help | Keyboard Shortcuts exibe os atalhos de teclado predefinidos, observando que eles podem ser modificados pelo usuário. O menu de ajuda também contém links diretos para as documentações do Python, IPython, NumPy, SciPy, Matplotlib, SymPy, pandas e markdown. O ícone de teclado abre uma ajuda com os atalhos mais usados.

Notebooks são iniciados com uma célula vazia. Cada célula possui dois modos de uso: modo de edição e modo de comando. Clicando dentro da célula ela fica com borda verde e entra no modo de edição onde se pode inserir texto e códigos. Use a caixa de seleção na caixa de ferramentas do notebook para selecionar entre ‘code’, ‘markdown’ ou ‘Raw NBConvert’. (A opção ‘heading’ está em desuso e é desnecessária). Se ‘code’ está selecionado você pode digitar um comando como, por ex.,

print('Olá galera!')
Olá galera!

Comandos dentro de uma célula de código são executados todos de uma vez. Para isso pressione:
shift-enter: o código é executado e uma nova célula é aberta no mode de edição, ou
ctrl-enter: o código é executado e a mesma célula fica selecionada no modo de comando.

Em ambos os casos o output, se existir, é mostrado abaixo da célula. O botão ▶ Run na barra de ferramentas tem o mesmo efeito que shift-enter.

Os modos de edição e de comando são alternados com o uso de ESC (passa de comando para edição) e enter (de edição para comando).

Quando no modo de comando (a célula fica com a borda azul) os seguintes atalhos ficam habilitados:

Comando Ação
ESC/enter Alterna entre modos de edição / comando
Barra de espaço Rolar tela para baixo
Shift-Barra de espaço Rolar tela para cima
A Inserir célula acima
B Inserir célula abaixo
D, D Apagar células selecionadas
Z Desfazer apagamento
Shift-L Exibir/ocultar numeração das linhas de código
Shift-Tab Exibir docstring de objeto
Y Alterar célula para código
M Alterar célula para markdown
R Alterar célula para raw
H Exibir Help | keyboard shortcuts
Teclas 🔼 ou 🔽 Rolar para cima e para baixo nas células
Shift 🔼 ou 🔽 Selecionar várias células de uma vez.
Shift M Mesclar várias células selecionadas em uma única.
Ctrl Shift – Dividir célula ativa na posição do cursor (no modo de edição).
Shift clique na margem esquerda da célula Selecionar várias células de uma vez.

Um lista completa desses atalhos pode ser vista no menu Help | keyboard shortcuts.

Células no formato de texto

Células podem ser marcadas como texto puro, sem qualquer formatação, no modo Raw NBConvert. Esse texto é exibido como está e não passará por renderização de nenhuma forma nem na exibição dentro do notebook nem na exportação para outros formatos, como o pdf.

Markdown

Markdown é uma linguagem de marcação limitada mas bastante simples de se aprender e usar. Ela tem sintaxe diferente de html mas guarda uma correspondência com suas tags. Você pode encontrar uma descrição mais completa de Markdown na página Marcação de Texto com Markdown.

Além da marcação markdown se pode usar um conjunto restrito de tags html nas caixas de texto. Por exemplo, o código seguinte:

 <div style = "border:1px solid black; padding:10px; color:white; background-color:red;" >
parei aqui ↷ 
</div>

produzirá a barra vermelha abaixo, útil para marcar um ponto específico dentro de um notebook.

parei aqui ↷

O Jupyter Notebook pode renderizar Latex (para isso usa a biblioteca javascript Mathjax) em suas células de Markdown.


A identidade de Euler é \(e^{i \pi} + 1 = 0\).
A definição da derivada de primeira ordem é:
$$
f'(x) = \lim\limits_{h \rightarrow 0} \frac{f(x+h) – f(x)}{h}
$$

Células de código

Células de código contém as instruções a serem executadas pelo kernel. À esquerda de uma célula de código aparece o sinal In[n] que significa Input [n], onde n é a ordem de execução das linhas. Uma célula em exibição é marcada pelo sinal In[*], o que é importante observar quando se tem uma execução mais demorada de código. Em geral o notebook continua responsivo mas novas instruções devem aguardar o término das primeiras para serem executados. O resultado da computação da célula, quando existir, aparece abaixo com o sinal Out[n].

O código em um célula ativa (com bordas azuis) é executado com ctrl-enter ou shift-enter (alternativamente com botão ▶ Run na barra de ferramentas). A barra de menu contém as opções Cell | Run All, Cell | Run All Above Cell | Run All Below.

É importante notar que as células são executadas no kernel e seu estado persiste ao longo da execução. O estado do kernel se refere ao notebook como um todo e não às células individuais. Se você importar bibliotecas ou declarar variáveis ​​em uma célula elas estarão disponíveis em toda a seção e não apenas nas células abaixo da declaração. A ordem em que inputs e outputs aparecem pode ser enganosa: se você declarar uma variável em uma célula, digamos g = 5 e voltar para uma célula anterior a variável g ainda terá o mesmo valor. O que importa é a ordem de execução, marcada por In[n]. Por isso é preciso ter cuidado ao fazer alterações nas células. Idealmente se deve manter a ordem sequencial e, ao fazer alterações importantes, executar todo o notebook novamente.

Existem várias opções relativas à execução no menu Kernel:

Kernel | Interrupt Interromper o kernel,
Kernel | Restart Reinicializar o kernel,
Kernel | Restart & clear outputs Reinicializar kernel e limpar outputs,
Kernel | Restart & Run All Reinicializar kernel e executar todas as células.

A opção de reinicializar o kernel limpando as saídas e executar novamente todas as células pode ser útil quando muitas células foram alteradas. No entanto, dependendo das operações a serem executadas, ela pode ser uma operação demorada.

É possível escolher e rodar e Jupyter Notebook com diferentes opções de kernel, com diferentes versões de Python e muitas outras linguagens, incluindo Java, C, R e Julia.

O Notebook é salvo ao se pressionar Ctrl S. Isso renova o arquivo de ponto de verificação que é criado junto com o Notebook. Esse arquivo, que também tem extensão .ipynb fica localizado em pasta oculta e pode ser usado na recuperação da sessão caso algum erro inesperado cause a perda do notebook atual. Por padrão o Jupyter salva automaticamente o bloco de notas a cada 120 segundos sem alterar o arquivo de bloco de notas principal.Para reverter para um ponto de verificação use o menu via File | Revert to Checkpoint.

Tendo escolhido um notebook com kernel python 3, podemos experimentar um resultado com a execução de código que fornece um output de texto e gráfico:

# importe as bibliotecas necessárias
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(1, 15, 0.1);
y = np.sin(x)/np.cos(x)
z = np.cos(x)/np.sin(x)
print("x em [1, 15] \ny=seno/cosseno; z=cosseno/seno")
plt.plot(x, y)
plt.plot(x, z)
x em [1, 15]
y=seno/cosseno; z=cosseno/seno

É importante observar que a última variável listada em uma célula é exibida no output sem necessidade de um comando print. Por ex., no código abaixo importamos uma tabela disponível na web no formato *.csv para a construção de um dataframe do pandas. O dataframe é exibido com uma formatação amigável no final da célula.

1  import pandas as pd
2  bfi ='https://vincentarelbundock.github.io/Rdatasets/csv/psych/bfi.csv'
3  df = pd.read_csv(bfi)
4  # head exibirá apenas as 5 primeiras linhas do dataframe
5  df.head()
Unnamed: 0 A1 A2 A3 A4 A5 C1 C2 C3 C4 N4 N5 O1 O2 O3 O4 O5 gender education age
0 61617 2.0 4.0 3.0 4.0 4.0 2.0 3.0 3.0 4.0 2.0 3.0 3.0 6 3.0 4.0 3.0 1 NaN 16
1 61618 2.0 4.0 5.0 2.0 5.0 5.0 4.0 4.0 3.0 5.0 5.0 4.0 2 4.0 3.0 3.0 2 NaN 18
2 61620 5.0 4.0 5.0 4.0 4.0 4.0 5.0 4.0 2.0 2.0 3.0 4.0 2 5.0 5.0 2.0 2 NaN 17
3 61621 4.0 4.0 6.0 5.0 5.0 4.0 4.0 3.0 5.0 4.0 1.0 3.0 3 4.0 3.0 5.0 2 NaN 17
4 61622 2.0 3.0 3.0 4.0 5.0 4.0 4.0 5.0 3.0 4.0 3.0 3.0 3 4.0 3.0 3.0 1 NaN 17

2800 rows × 29 columns

Ajuda

É sempre útil lembrar os mecanismos de se conseguir ajuda no python e no Jupyter. A função help do python é útil para se ver documentação sobre um tipo de variável ou uma bliblioteca importada. (Em ambos os casos a saída exibida está truncada.)

# ajuda sobre dictionaries
help(dict)
Help on class dict in module builtins:
class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from a mapping object’s
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
import math
help(math)
Help on module math:
FUNCTIONS
acos(x, /)
Return the arc cosine (measured in radians) of x.
acosh(x, /)
Return the inverse hyperbolic cosine of x.
asin(x, /)
Return the arc sine (measured in radians) of x.

O comando help() usado sem argumento abre uma janela de busca interativa do tópico de ajuda.

Além disso escrever o nome de uma biblioteca, método ou variável precedido de ? provocará a exibição da docstring relativa ao item, o que é uma boa forma de conferir uso e sintaxe.

import pandas as pd
?pd
Type: module
String form: <module ‘pandas’ from ‘/home/guilherme/.anaconda3/lib/python3.7/site-packages/pandas/__init__.py’>
File: ~/.anaconda3/lib/python3.7/site-packages/pandas/__init__.py
Docstring:
pandas – a powerful data analysis and manipulation library for Python
=====================================================================
**pandas** is a Python package providing fast, flexible, and expressive data
structures [… texto truncado …]

Além disso é possível exibir a docstring de um objeto no Notebook apertando Shift-Tab após o nome do objeto dentro da célula.

Células e Linhas Mágicas em Jupyter

Uma sintaxe especial dos notebooks consiste em inserir comandos iniciados por %% para “mágica” na célula inteira e % para “mágica” na linha. Por exemplo %matplotlib inline faz com que gráficos do matplotlib sejam renderizados dentro do próprio notebook. Os sinais % e %%indicam respectivamente “mágica” na linha ou na célula. Digitar %lsmagic na célula e executá-la exibirá uma lista de todos os comandos mágicos de célula e de linha, informando ainda se automagic está ativado. Se automagic = ON os prefixos %% e % são desnecessários.

Alguns exemplos de “mágicas”:

Mágica Efeito
%run Roda um script externo como parte da célula.
Ex.: %run meu_codigo.py
executa meu_codigo.py como parte da célula.
%timeit Conta os loops, mede e exibe o tempo de execução da célula.
%prun Mostra o tempo de execução de uma função.
%%writefile Salva o conteúdo da célula para um arquivo.
Ex.: %%writefile meu_output.py
salva o código da célula no arquivo meu_output.py.
%%pycat Lê e exibe o conteúdo de um arquivo.
Ex.: %%pycat meu_output.py
lê o conteúdo gravada antes em meu_output.py.
%macro Define uma macro para execução posterior.
%store Salva a variável para uso em outro notebook.
%pwd Exibe a pasta default de trabalho.
%who Exibe varáveis ativas.
%matplotlib inline Exibe gráficos matplotlib inline.
%lsmagic Exibe lista de comandos “mágicos”.
%automagic Liga/desliga automagica (necessidade de %).
%%javascript Roda a célula como código JavaScript.
%%html Exibe código html renderizado.
%pdb Aciona o Python Debugger.
%<comando>? Mostra ajuda sobre a %<comando>
%%script python2
import sys
print 'Esse comando foi executado no Python %s' % sys.version
# output---> Esse comando foi executado no Python 2.7.18 (default, data / hora) 
%%script python3
import sys
print('Comando executado no Python: %s' % sys.version)
# output---> Esse comando foi executado no Python 3.8.5 (default, data / hora) 
%%bash
echo "O conteúdo de minha variável BASH: $BASH"
# output---> O conteúdo de minha variável BASH: /usr/bin/bash
# Definição de fatorial usando recursão	
def fatorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)
fatorial(40)      # resulta em 87178291200
# Claro que a mesma coisa seria obtida com a função
import math
print(math.factorial(14))    # 87178291200

Para medir o tempo de execução da célula ao rodar a função fatorial usamos %timeit

%timeit fatorial(14)

1.78 µs ± 16.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Executando comandos da SHELL

Usando o prefixo ! você pode enviar comandos diretamente para a shell de seu sistema operacional. Os comandos dependerão, claro, de seu sistema. No exemplo seguinte uso o bash do linux.

! ls *.ipynb     # vai listar todos os arquivos ipynb
! ls *.py        # lista meu_output.py gravado no exemplo anterior 

# mv é o comando bash para renomear arquivos
! mv meu_output.py novo_nome.py 
! ls *.py        # lista novo_nome.py

# Se você tem pip instalado pode gerenciar pacotes python de dentro do notebook:
!pip install --upgrade nome_do_pacote

Extensões do Jupyter Notebook


Extensões são recursos adicionais que estendem a funcionalidade dos Notebooks. Elas podem melhorar sua funcionalidade como uma IDE (um ambiente integrado de desenvolvimento), permitindo o acompanhamento de variáveis para fins de debugging, formatando o código automaticamente ou alterando sua aparência. Elas também permitem a inserção de widgets, que geralmente são objetos com representação gráfica no navegador, tais como caixas de texto, sliders e outras ferramentas interativas. Elas adicionam funcionalidades GUIs nos notebooks.

Uma extensão muito usada é a Nbextensions que pode ser instalada à partir do código seguinte, inserido no próprio notebook.

conda install -c conda-forge jupyter_contrib_nbextensions
conda install -c conda-forge jupyter_nbextensions_configurator 

A instalação de extensões pode variar de acordo com o seu ambiente de trabalho.

Jupyter Widgets

Widgets são objetos do python que possuem uma representação visual no navegador e permitem interação do usuário. Eles podem ser simples como uma caixa de texto ou um controle de arrastar, ou objetos sofisticados como os controles sobre uma tabela de dados ou sobre um mapa.

Slider é um widget útil para receber um valor do usuário por meio do mouse ou toque de tela. O objeto possui propriedades que podem ser alteradas e lidas, e métodos.
No exemplo abaixo um slider é criado, algumas de suas propriedades são ajustadas e depois é exibido.

import ipywidgets as ipw
# Inicializar um slider
slid = ipw.IntSlider()
slid.min = 0
slid.max = 50
slid.description = "Avalie:"
display(slid) # Exibe o slider ( figura a)

# O objeto pode ser inicializado com as propriedades desejadas
slid2 = ipw.IntSlider(
    value=0,
    min=0,
    max=10,
    step=2,
    description='Só pares:',
    disabled=False,
    continuous_update=True,
    orientation='horizontal',
    readout=True,
    readout_format='d'
)
display(slid2) # Exibe o slider ( figura b)

Button é um widget que pode armazenar interação do usuário em resposta binária (sim/não, por ex.).

ipw.Button (
    description='Clica aí',
    disabled=False,
    tooltip='clica...',
    button_style='success', # pode ser 'success', 'info', 'warning', 'danger' ou ''
    icon='check'
)
# Exibe o botão (figura c)

Text apresenta uma caixa de texto a ser preenchida pelo usuário:

ipw.Text (
    placeholder='Digite seu nome',
    description='Nome:',
)
# Exibe a caixa de texto ( figura d)

Progress Bar mostra uma barra de progresso:

ipw.IntProgress (
    min=0,
    max=10,
    value=7,
    description='Realizado %',
    bar_style='success', # pode ser 'success', 'info', 'warning', 'danger' ou ''
    orientation='horizontal'
)
# Exibe a barra de progresso ( figura e)

Para serem úteis as widgets devem interagir com o código. Como ex. vamos criar uma barra de progresso atribuindo o objeto a uma variável.

pb = ipw.IntProgress (
    min=0,
    max=10,
    value=7,
    description='Realizado%',
    bar_style='success', # pode ser 'success', 'info', 'warning', 'danger' ou ''
    orientation='horizontal'
)

print('valor inicial = ', pb.value) # value é uma propriedade de pb. Neste caso seu valor é 7

pb.value = 10                       # que pode ser alterada no código
display(pb)	                        # exibe uma barra de progesso cheia

No exemplo seguinte fazemos uma interação entre os valores da barra de progresso com o clique de dois botões. O código usa o evento on_click dos botões para acionar as funções que aumentam e diminuem o valor da barra.

def aumentaBarra(s):
    pb.value +=1

def diminuiBarra(s):
    pb.value -=1

botaoMais = ipw.Button(description='Aumenta barra')
botaoMais.on_click(aumentaBarra)

botaoMenos = ipw.Button(description='Diminue barra')
botaoMenos.on_click(diminuiBarra)
# display(botaoMenos, botaoMais, pb)
# -- isso exibiria os controles um sobre o outro

# Para exibir os controles em uma linha única com título
# usamos HBox, Label

from ipywidgets import HBox, Label
HBox([Label('Em uma linha única:'), botaoMenos, botaoMais, pb])         # exibe figura (f)


Existem widgets mais sofisticados para formatação dos controles, inclusive com o uso de html e formatação css. Apenas para mostrar um exemplo segue o código abaixo, que usa HBox para construir uma linha e VBox para construir uma coluna de controles.

from ipywidgets import HBox, VBox, Label	
linha1 = HTML('<h2 style="color: red;">Use controles para modificar <i>progress box</i> </h2>')
linha2 = HBox([Label('Botões de controle:'), botaoMenos, botaoMais])
linha3 = pb
VBox([linha1, linha2, linha3]) # exibe figura (g)


Um clique no primeiro botão faz a barra de progressa avançar, no segundo faz a barra regredir.

Observe importamos os controles HBox,VBox e Label de ipywidget e, portanto eles podem ser usados diretamente sem menção à biblioteca de origem. Poderíamos usar a notação ipw.HBox para usar a caixa horizontal, sem precisar da importação explícita.

Outras informações sobre formatação dos widgets em
Layout and Styling of Jupyter widgets
.

Outros Widgets Úteis

HTMLMath serve para exibir equações matemáticas:

equacao = ipw.HTMLMath(
    value = r"Widgets podem ser usados para exibir equações inline \(x^2\) e destacadas $$\frac{x^2-1}{x-1}= x+1$$",
    placeholder='HTML',
    description='MathML:',
)
display(equacao)
MathML: Widgets podem ser usados para exibir equações inline \(x^2\) e destacadas $$\frac{x^2-1}{x-1}= x+1$$

ipyleaflet é uma biblioteca para a criação de mapas interativos que permitem marcação, arraste e ampliação. Em muitos casos, para executar um biblioteca recém instalada, pode ser necessário reinicializar o kernel.

# A biblioteca pode ser instalada com condas:
conda install -c conda-forge ipyleaflet

from ipyleaflet import Map
# inserindo as coordenadas da Praça da Liberdade em Belo Horizonte, MG
Map(center=[-19.932252,-43.9381638], zoom=15)

BeakerX é um pacote com widgets destinados a representar tabelas, gráficos, formulários de modo interativo. O widget de tabela reconhece e exibe dataframes do pandas. Ele facilita a pesquisa, e permite classificar, arrastar, filtrar, formatar ou selecionar células na tabela. Ele também permite a exportação para CSV ou área de transferência e a representação gráfica dos dados. O widget de tabela é mostrado abaixo, em imagem estática. Mais informações em Beakerx.

# Instalação do pacote
conda install -c conda-forge beakerx ipywidgets

import pandas as pd
from beakerx import *
pd.read_csv("UScity.csv")
# Instalando o pacote
conda install -c conda-forge k3d	

import k3d
plot = k3d.plot()
plot += k3d.points([0, 0, 0, 1, 1, 1])
plot.display()

A figura dentro do Notebook é dinâmica, podendo ser girada, ampliada, exportada, etc.

Modificando o estilo do Notebook

Jupyter Notebooks são enviados para o navegador como páginas html e são formatadas e estilizadas por meio de arquivos css (cascade style sheets) padrões. Para quem conhece html e css pode ser interessante editar diretamente esse arquivo. (Não se esqueça de fazer cópias de segurança dos arquivos que vai modificar).

Se sua instalação não contém a pasta “.jupyter” execute o comando jupyter notebook --generate-config no terminal. No Linux o arquivo de estilos está em /home/usuario/.jupyter/custom/custom.css, no Windows em pasta.do.python\Lib\site-packages\notebook\static\custom\custom.css, embora isso possa ser alterado ou ser diferente para outras instalações. Para ver a pasta onde está o arquivo de configurações você pode usar:

import jupyter_core
jupyter_core.paths.jupyter_config_dir()

Existem muitas folhas de estilo prontas para baixar, por exemplo no GitHub. Elas podem ser baixadas e copiadas para a pasta citada.

Para modificar apenas o estilo do Notebook aberto, sem alterar a aparência dos demais você pode usar:

# Importe biblioteca 
from IPython.core.display import HTML
HTML("<style> instruções de estilo </style>")
# Alternativamente se pode usar "mágica" em uma célula
%%html "<style> estilos - CSS </style>"

# Por exemplo, para atribuir margens laterais de 10% do tamanho da página use
display(HTML("<style>.container { margin: 0 10% 0 10% !important; }<style>"))

# Para exibir outputs em negrito
display(HTML("<style>.output_wrapper { font-weight: bold !important; }</style>"))

De dentro de um Notebook você pode carregar uma folha de estilo particular que passa a valer para aquele documento. Esse método é útil para formatar documentos de um tipo específico, por exemplo para uma apresentação ou páginas com estilos incomuns.

from IPython.core.display import HTML
def css_styling():
    styles = open("../styles/custom.css", "r").read()
    return HTML(styles)
css_styling()

Finalmente você pode instalar extensões tais como Jupyter themes.

Exportando e Compartilhando Notebooks


Notebooks podem ser exportados para diversos formatos tais como HTML e PDF e vários outros formatos. Isso pode ser encontrado no menu File | Download as. Se estiver trabalhando na nuvem a importação no formato nativo .ipynb serve para baixar para seu computador uma cópia do notebook. Ao subir uma cópia para o GitHub ou para o Colab você pode disponibilizar a sessão para outros colaboradores. Slides são úteis para aulas e apresentações.

NBViewer: NBViewer é um renderizador de notebooks, útil para quem quer ver uma sessão sem ter que necessariamente instalar o Jupyter. Esse é um serviço gratuito oferecido pelo Projeto Jupyter, disponível em nbviewer.jupyter.org. Ele recebe uma URL do notebook e o renderiza como página da web.

Bibliografia

  • Wintjen, Marc: Practical Data Analysis Using Jupyter Notebook: Learn how to speak the language of data by extracting useful and actionable insights using Python, Packt Publishing, Birmingham, UK, 2020.
  • Taieb, David: Thoughtful Data Science, , Packt Publishing, Birmingham, UK, 2018.
  • Dataquest: Advanced Jupyter Notebooks Tutorial, acessado em novembro de 2020.
  • Real Python: Jupyter Notebook Introduction, acessado em novembro de 2020.
  • Jupyter.org: Jupyter Main Site, acessado em novembro de 2020.
  • jupyter.org: Jupyter Widgets, acessado em novembro de 2020.

Marcação de Texto com Markdown

“O poeta é um fingidor. Finge tão completamente que chega a fingir que é dor a dor que deveras sente.”
Fernando Pessoa, Autopsicografia.

O que é Markdown

Markdown é uma linguagem de marcação de texto. Ele permite a insercão de marcas ou códigos em texto simples que são representados na exibição final em formatos sofisticados. Além disso ela permite a conversão para textos em HTML, que podem ser distribuídos pela web (ou localmente) e lidos por browsers. A marcação foi concebida para deixar o texto simples e legível. Markdown é software livre, disponível sob uma licença BSD de código aberto.

Títulos (Headings)

Para criar títulos use sinais # em frente ao texto (um para cada nível).

Markdown html Output obtido
# Título de nível 1 <h1>Título de nível 1</h1> (título de nível 1)
## Título de nível 2 <h2>Título de nível 2</h2> (título de nível 2)
### Título de nível 3 <h3>Título de nível 3</h3> (título de nível 3)
#### Título de nível 4 <h4>Título de nível 4</h4> (título de nível 4)
##### Título de nível 5 <h5>Título de nível 5</h5> (título de nível 5)
###### Título de nível 6 <h6>Título de nível 6</h6> (título de nível 6)

Alternativamente, é possível sublinhar com sinais de igual (===) para gerar um título de nível 1 e com traços (---) para gerar um título de nível 2:

Markdown Output
Título de nível 1
=================
(Título de nível 1)
Título de nível 2
—————–
(Título de nível 2)

Bastam dois sinais == ou -- para a representação de título.

Parágrafos

Parágrafos são linhas de texto separadas por linhas vazias.

Markdown html Output obtido
Markdown é simples.
Vou usá-lo sempre!
<p>Markdown é simples.</p>
<p>Vou usá-lo sempre!</p>
Markdown é simples.
Vou usá-lo sempre!

Quebra de linhas

Para quebrar uma linha termine a linha com 2 ou mais espaços (⎵ ⎵) seguidos de return ().

Markdown html Output obtido
Essa é a primeira linha. ⎵⎵↲
Essa é a segunda linha.
<p>Essa é a primeira linha.<br>
Essa é a segunda linha.</p>
Essa é a primeira linha.
Essa é a segunda linha.

Negrito e itálico

Texto em negrito é obtido com asteriscos ou traços baixos (underscores) envolvendo o texto a negritar. Letras dentro da palavra também podem ser negritadas com duplos asteriscos.

Markdown html Output obtido
Precisamos de **negrito**. Precisamos de <strong>negrito</strong>. Precisamos de negrito.
Precisamos de __negrito__. Precisamos de <b>negrito</b>. Precisamos de negrito.
abcdef**GH**ij abcdef<strong>GH</strong>ij abdcefGHij

Textos em itálico são obtidos com um asterisco ou uma traço baixo (underscores) envolvendo o texto. Letras dentro da palavra também podem ser tornadas itálicos com um asterisco.

Markdown html Output obtido
Texto *em itálico*. Texto <em>em itálico</em>. Texto em itálico.
Texto _em itálico_. Texto <i>em itálico</i>. Texto em itálico.
a*bcd*ef a<em>bcd</em>ef abcdef

Texto em negrito e itálico simultâneos são obtidos com três asteriscos ou traços baixos (underscores) envolvendo o texto. Letras dentro da palavra também podem ser negritadas com três asteriscos.

Markdown html Output obtido
Um texto ***muito importante***. Um texto <strong><em>muito importante</em></strong>. Um texto muito importante.
Um texto ___muito importante___. Um texto <b><i>muito importante</i></b>. Um texto muito importante.
Um texto __*muito importante*__. Um texto <i><b>muito importante</b></i>. Um texto muito importante.
Um texto **_muito importante_**. …idem… Um texto muito importante.
Texto***muito***importante. Texto<strong><em>muito</em></strong>importante. Textomuitoimportante.

Textos riscados são obtidos com dois “tils” ˜˜ anterior e posterior ao texto.

Markdown html Output obtido
Texto ˜˜riscado˜˜. Texto <del>riscado</del>. Texto riscado.

Linha horizontal

Linhas horizontais são inseridas com três ou mais asteriscos (***), ‘dashes’ (---), ou ‘underscores’ (___) em uma linha.

Markdown html Output obtido
*** <hr>

Blocos

Para criar blocos destacados (citações) coloque > na frente do parágrafo.

> “O poeta é um fingidor. Finge tão completamente que chega a fingir que é dor a dor que deveras sente.”
> ― Fernando Pessoa, Autopsicografia.

“O poeta é um fingidor. Finge tão completamente que chega a fingir que é dor a dor que deveras sente.”
― Fernando Pessoa, Autopsicografia.

Blocos destacados podem conter muitos parágrafos. Para isso basta inserir > em cada linha branca entre as parágrafos.

> “Nós, os mortais, alcançamos a imortalidade nas coisas que criamos em comum e que estão atrás de nós.”
>
> “A coisa mais bela que podemos experimentar é o mistério. Essa é a fonte de toda a arte e ciências verdadeiras.”
> ― Albert Einstein.

“Nós, os mortais, alcançamos a imortalidade nas coisas que criamos em comum e que estão atrás de nós.”
“A coisa mais bela que podemos experimentar é o mistério. Essa é a fonte de toda a arte e ciências verdadeiras”.
― Albert Einstein.

Blocos ou citações podem ser aninhadas. Para isso coloque >> na frente do parágrafo com segunda indentação.

> “Eu jamais iria para a fogueira por uma opinião minha pois não tenho certeza alguma. Porém, eu iria pelo
direito de ter e mudar de opinião, quantas vezes quisesse.”
>
>> ”Sem música a vida seria um erro.”
> ― Friedrich Nietzsche

“Eu jamais iria para a fogueira por uma opinião minha pois não tenho certeza alguma. Porém, eu iria pelo direito de ter e mudar de opinião, quantas vezes quisesse.”

“Sem música a vida seria um erro.”

― Friedrich Nietzsche

Listas

Listas ordenadas (numeradas) são criadas numerando-se as linhas, cada número seguido de ponto. Qualquer número serve para isso, embora o número da primeira linha marque o número usado na lista:

Markdown html Output obtido
1. Primeiro item
2. Segundo item
3. Terceiro item
4. Quarto item
<ol>
<li>Primeiro item</li>
<li>Segundo item</li>
<li>Terceiro item</li>
<li>Quarto item</li>
</ol>
  1. Primeiro item
  2. Segundo item
  3. Terceiro item
  4. Quarto item
1. Primeiro item
1. Segundo item
1. Terceiro item
  1. Indentado item 1
  1. Indentado item 2
1. Quarto item
<ol>
<li>Primeiro item</li>
<li>Segundo item</li>
<li>Terceiro item
<ol>
<li>Indentado item 1</li>
<li>Indentado item 2</li>
</ol>
</li>
<li>Quarto item</li>
</ol>
  1. Primeiro item
  2. Segundo item
  3. Terceiro item
    1. Indentado item 1
    2. Indentado item 2
  4. Quarto item

Observe que não é necessário usar a numeração correta nas listas. Alguns editores completam automaticamente esses números. A segunda indentação (ou lista dentro de outra lista) é gerada por espaços ou tab antes do item.

Listas não numeradas são criadas com os sinais *, + ou - antes dos itens.

Markdown html Output obtido
– Primeiro item
– Segundo item
– Terceiro item
– Quarto item
<ul>
<li>Primeiro item</li>
<li>Segundo item</li>
<li>Terceiro item</li>
<li>Quarto item</li>
</ul>
  • Primeiro item
  • Segundo item
  • Terceiro item
  • Quarto item
– Primeiro item
– Segundo item
– Terceiro item
  - Indentado item 1
  - Indentado item 2
– Quarto item
<ul>
<li>Primeiro item</li>
<li>Segundo item</li>
<li>Terceiro item
<ul>
<li>Indentado item 1</li>
<li>Indentado item 2</li>
</ul>
</li>
<li>Quarto item</li>
</ul>
  • Primeiro item
  • Segundo item
  • Terceiro item
    • Indentado item 1
    • Indentado item 2
  • Quarto item

Blocos de código

Uma anotação apropriada para código, principalmente de programação, é uma característica muito útil no markdown. A indentação correta para blocos de código pode ser conseguida com linhas precedidas por 4 espaços ( ) ou 1 tab. Dentro de uma lista essas linhas são precedidas por 8 espaços ou 2 tabs. Nesse contexto usamos o sinal para representar um espaço:

Markdown html Output obtido
⎵⎵⎵⎵def soma(a,b):
⎵⎵⎵⎵⎵⎵⎵⎵return a + b
⎵⎵⎵⎵print (soma(5, 7))
<pre>
  def soma(a,b):
    return a + b
  print (soma(5, 7))
</pre>
  def soma(a,b):
    return a + b
  print(soma(5, 7))

Alternativamente, blocos de código também podem ser colocados entre três acentos graves, backticks, ```:

Markdown Output obtido
```
function factorial(n) {
  if (n === 0) return 1; // 0! = 1
  return n * factorial(n – 1);
}
factorial(3); // returns 6}
```
function factorial(n) {
  if (n === 0) return 1; // 0! = 1
  return n * factorial(n – 1);
}
factorial(3); // returns 6}

Nesse formato de bloco de código é possível marcar o código com cores, de acordo com a linguagem selecionada (syntax highlighting). Essa característica depende do processador usado. No exemplo seguinte o código é marcado como JSON, o que é informado após o primeiro grupo de acentos graves.

Markdown Output obtido
``` json
{
  ”nome”: “José”,
  ”sobrenome”: “Garcia”,
  ”idade”: 45
}
```
{
  “nome”: “José”,
  “sobrenome”: “Garcia”,
  “idade”: 45
}

Para denotar texto em código, uma ou mais palavras, coloque-as entre acentos graves (backticks, `).

Markdown html Output obtido
Digite no prompt de comando: `nano`. Digite no prompt de comando: <code>nano</code>. Digite no prompt de comando: nano.

Imagens

Para acrescentar uma imagem use a seguinte sintaxe:


![Imagem 1](caminho/images/tux.png “Título a ser exibido”)

Tabelas

Tabelas são criadas usando-se a barra vertical | e hífens. Por exemplo, as três tabelas abaixo mostram o uso do código markdown a seguir

Tabela (1)

| Título 1 | Título 2 | T3 |
| ‐‐‐‐‐‐‐‐‐‐‐‐| ‐‐‐‐‐‐‐‐‐‐‐‐|‐‐-‐|
| célula 1, 1 | célula 1, 2 | 1 |
| célula 2, 1 | célula 2, 2 | 2 |

Tabela (2)

| Alinhamento | Alinhamento | Alinhamento |
| :‐‐‐‐‐‐‐‐‐‐ | :‐‐‐‐‐‐‐‐‐: | ‐‐‐‐‐‐‐‐‐‐: |
| Esquerda | Centro | Direita |
| 1 | 2 | 3 |

Tabela (3)

Título | N
-|-
a|b

são representadas como as tabelas (1), (2) e (3) abaixo:

A tabela (2) ilustra o uso dos sinais de alinhamento: à esquerda (:‐‐), centralizado (:‐‐:) (centralizado) e à direita (‐‐:). Os itens da tabela não precisam estar bem alinhados no markdown e as barras laterais são dispensáveis, como é mostrado na tabela (3). Dentro das tabelas o texto pode conter links, negrito e itálico e código.

Links

Links para outras páginas, locais ou na web, podem ser inseridos dentro de colchetes (ex.: [Duck Duck Go]) seguidos da URL entre parênteses. Por ex. (https://duckduckgo.com)).

Markdown html Output obtido
Recomendo o mecanismo de busca_ _
[Duck Duck Go](https://duckduckgo.com).
Recomendo o mecanismo de busca <br>
<a href=”https://duckduckgo.com”>Duck Duck Go</a>.
Recomendo o mecanismo de busca
Duck Duck Go.

Notas de Pés de Página

Segue um exemplo de texto com notas de pé de página.

Use a seguinte notação para criar pés de página[^1], que também pode conter textos mais longos e várias parágrafos[^2].
Os marcadores não precisam ser números[^t].[^1]: O pé de página fica no final do texto.
[^2]: Um pé de página pode ter vários parágrafos.
Todos os parágrafos dentro da mesma nota devem ser indentados.
`{ function teste(a, b): # pode conter código e outras formatações. }`
[^t]: Outro pé de página.

Esse código é apresentado em forma final da seguinte forma (com os links ativos):


Use a seguinte notação para criar pés de página[1], que também pode conter textos mais longos e várias parágrafos[2].
Os marcadores não precisam ser números[3].


1. O pé de página fica no final do texto.
2. Um pé de página pode ter vários parágrafos.
Todo o texto dentro do mesma anotação deve ser indentado.
{ function teste(a, b): # pode conter código e outras formatações. }
3. Outro pé de página.

Lista de tarefas

O código abaixo gera a lista mostrada à direita.

## Lista de Tarefas
- [x] Tarefa primeira (completa)
- [ ] Tarefa segunda
- [ ] Arranjar outras **tarefas**

Lista de Tarefas
☑ Tarefa primeira (completa)
Tarefa segunda
Arranjar outras tarefas

HTML

Vários aplicativos que usam Markdown, entre eles o Jupyter Notebook, também podem representar corretamente diversas tags de marcação html. Isso é útil para usuários familiarizados com html, principalmente quando se pretende inserir texto com formatos mais sofisticados, tais como como texto colorido, com variação de tamanho, caixas coloridas, etc.

Como exemplo, o código abaixo produzirá uma barra vermelha, útil para marcar o ponto de parada dentro de um notebook (Jupyter). Outro exemplo consiste na produção de texto sublinhado. Como não há código markdown específico para isso você pode conseguir texto sublinhado com as tags html.

<u>Texto 1 sublinhado</u> ou <ins>Texto 2 sublinhado</ins><div style=”border:1px solid black; padding:10px; color:white; background-color:red;”>parei aqui &curarr;</div>


Texto 1 sublinhado ou Texto 2 sublinhado

parei aqui ↷

Latex

LaTeX é uma linguagem de composição de texto para a produção de documentos científicos. Vários editores de Markdown podem renderizar equações matemáticas e símbolos Latex para reproduzir notação matemática. Em particular o Jupyter Notebook reconhece código LaTeX escrito nas células de Markdown e os representa corretamente no navegador usando a biblioteca JavaScript MathJax. Para inserir uma fórmula matemática em uma linha use o símbolo $expressão$. Para equações isoladas em uma linha e centralizadas use $$expressão$$.

A identidade de Euler é $e^{i \pi} + 1 = 0$.
As equações de Einstein são:
$$
R_{\mu\nu}-\frac{1}{2} Rg_{\mu\nu} +\Lambda g_{\mu\nu} = \frac{8 \pi G}{c^4} T_{\mu\nu}.
$$
A definição da derivada de primeira ordem é:
$$
f'(x) = \lim\limits_{h \rightarrow 0} \frac{f(x+h) – f(x)}{h}
$$

A identidade de Euler é \(e^{i \pi} + 1 = 0\).
As equações de Einstein são:
$$
R_{\mu\nu}-\frac{1}{2} Rg_{\mu\nu} +\Lambda g_{\mu\nu} = \frac{8 \pi G}{c^4} T_{\mu\nu}.
$$
A definição da derivada de primeira ordem é:
$$
f'(x) = \lim\limits_{h \rightarrow 0} \frac{f(x+h) – f(x)}{h}
$$

Editores de Markdown

Existem muitos bons editores de markdown. Algumas sugestões:

Remarkable
Haroopad
UberWriter
Macdown
Ghostwriter
Marcor

Editores online:

São excelentes aplicativos de anotações, permitindo o uso de plugins para calendários, ToDos, customização de aparência por css. Permitem a sincronização entre diversos aparelhos Mac, Windows, Linux e Androide:

SimpleNote, Joplin, Obsidian

Bibliografia

Análise Fatorial Usando Python

🔻Final do artigo

Para dar um exemplo de análise de fatores usaremos o módulo factor_analyser do Python. Os dados usados são originados do Synthetic Aperture Personality Assessment (SAPA) que contém 25 questões de auto-avaliação pessoais disponíveis na web na página de Vincent Arel-Bundock, no Github.

A documentação do Factor Analyser pode ser lida nessa página.

SAPA e BFI

SAPA, Synthetic Aperture Personality Assessment, é um método usado para avaliar diferenças de personalidade individuais, muito utilizado para pesquisas online. O sujeito testado recebe um subconjunto aleatório dos itens em estudo com o objetivo de reunir grande volume de dados suficientes para a montagem de grandes matrizes de covariância (de relacionamento entre os dados verificados). O teste online foi desenvolvido por William Revelle e é mantido pela Northwestern University, Ilinois, EUA.

O teste consiste dos seguintes ítens:
A1: Sou indiferente aos sentimentos das outras pessoas.
A2: Sempre pergunto sobre o bem-estar dos outros.
A3: Sei como confortar os outros.
A4: Adoro crianças.
A5: Faço as pessoas se sentirem à vontade.C1: Sou exigente no meu trabalho.
C2: Continuo minhas tarefas até que tudo esteja perfeito.
C3: Faço as coisas de acordo com um plano.
C4: Deixo tarefas semi-acabadas.
C5: Desperdiço meu tempo.E1: Não falo muito.
E2: Tenho dificuldades para abordar outras pessoas.
E3: Sei como cativar as pessoas.
E4: Faço amigos facilmente.
E5: Assumo o controle das situações.

N1: Fico com raiva facilmente.
N2: Irrito-me facilmente.
N3: Tenho alterações de humor frequentes.
N4: Muitas vezes me sinto triste.
N5: Entro em pânico facilmente.

O1: Sempre tenho muitas ideias.
O2: Evito leituras complexas.
O3: Procuro levar as conversas para um nível elevado.
O4: Passo algum tempo refletindo sobre as coisas.
O5: Nunca me detenho a avaliar um assunto profundamente.

Escala:
A escala usada para respostas usada foi:
1. Totalmente falso
2. Moderadamente falso
3. Um pouco falso
4. Um pouco correto
5. Moderadamente correto
6. Totalmente corretoIndicadores Demográficos:
estão codificados da seguinte forma:
Gênero:
1. Masculino,
2. Feminino.
Idade: a idade (em anos).
Educação:
1. Nível médio incompleto,
2. Nível médio completo,
3. Nível superior incompleto,
4. Nível superior completo,
5. Pós-graduação.Fatores esperados:
Os itens estão organizados por fatores esperados (a serem verificados pela análise):
Agreeableness, (Amabilidade),
Conscientiousness, (Conscienciosidade),
Extroversion, (Extroversão),
Neuroticism, (Neuroticismo) e
Opennness, (Abertura).

big 5
Diversos itens contribuem em grau maior ou menor para os fatores ou variáveis latentes.

O arquivo de respostas disponível foi baixado como o nome bfi.csv e salvo na pasta do projeto, subpasta ./dbs. Esse arquivo contém dados no formato *.csv (valores separados por vírgula) relativos a 2800 sujeitos com 3 campos adicionais de dados demográficos: sexo, educação e idade.

Jupyter Notebook e convenções usadas

Você pode ler sobre Jupyter Notebook nessa página.

Jupyter Notebook é uma aplicação web e opensource que permite sessões colaborativas e documentos compartilhados contendo código que pode ser executado dentro da página, equações bem formatadas, visualização gráfica e texto narrativo que podem ser postas sob forma de apresentações ou usadas para desenvolvimento. Seu uso inclui tratamento, transformação e visualização de dados, simulações numéricas, modelagem estatística, machine learning entre outras aplicações.

O projeto será rodado em uma sessão do Jupyter Notebook. Nessa página usamos as seguintes convenções: células de código do Jupyter Notebook aparecem dentro de caixas como a exibida abaixo. Nos notebooks (como no Python) linhas iniciadas pelo sinal “#” são comentários. Apenas nessas páginas outputs simples e compactos podem aparecer como um comentário após o comando como mostrado abaixo (diferente do que ocorre nos notebooks). Outpus mais complexos aparecem em caixas separadas.

# Exemplo de exibição das células do Jupyter Notebook.
print('output simples')    # Esse comando imprime 'output simples'

# Outpus mais complexos aparecem em caixas separadas:
print('Outputs do Jupyter Notebook, gráficos e dataframes exibidos aparecem como nesse quadro...')
Outputs do Jupyter Notebook, gráficos e dataframes exibidos aparecem como nesse quadro…

Módulo Factor Analyser

Veja mais sobre a documentação do Factor Analyser nessa página.

Factor Analyser é um módulo desenvolvido em Python por Jeremy Biggs e Nitin Madnani, publicado em 2017 para realizar análise fatorial exploratória e confirmatória (AFE, AFC). As classes do pacote são compatíveis com a biblioteca scikit-learn. Partes do código são portadas da biblioteca psych do R.

Tratamento dos dados do bsi por meio do factor_analyser

Instalamos o módulo factor_analyzer dentro do Jupyter Notebook. Em seguida importamos as bibliotecas necessárias: além do próprio factor_analyzer usamos o pandas e numpy para as manipelações de dados e matplotlib para as visualizações.

# Instalação do factor_analyzer:
conda install -c ets factor_analyzer

# Importando bibliotecas (libraries) necessárias
import pandas as pd
import numpy as np
from factor_analyzer import FactorAnalyzer
import matplotlib.pyplot as plt

# A leitura do arquivo de dados para dentro de um dataframe (do pandas)
df = pd.read_csv('./dbs/bfi.csv')

# Renomear a primeira coluna (que está sem nome) para 'id'
df.rename(columns = {'Unnamed: 0':'id'}, inplace = True) 
print('A tabela importada contém %d linhas, %d colunas' % df.shape)
print('contendo as seguintes colunas:\n', df.columns)

# Para visualizar a tabela importada:
df.head()
A tabela importada contém 2800 linhas, 29 colunas
contendo as seguintes colunas:
Index([‘id’, ‘A1’, ‘A2’, ‘A3’, ‘A4’, ‘A5’, ‘C1’, ‘C2’, ‘C3’, ‘C4’, ‘C5’, ‘E1’,
‘E2’, ‘E3’, ‘E4’, ‘E5’, ‘N1’, ‘N2’, ‘N3’, ‘N4’, ‘N5’, ‘O1’, ‘O2’, ‘O3’,
‘O4’, ‘O5’, ‘gender’, ‘education’, ‘age’],
dtype=’object’)
id A1 A2 A3 A4 A5 C1 C2 C3 C4 N4 N5 O1 O2 O3 O4 O5 gender education age
0 61617 2.0 4.0 3.0 4.0 4.0 2.0 3.0 3.0 4.0 2.0 3.0 3.0 6 3.0 4.0 3.0 1 NaN 16
1 61618 2.0 4.0 5.0 2.0 5.0 5.0 4.0 4.0 3.0 5.0 5.0 4.0 2 4.0 3.0 3.0 2 NaN 18
2 61620 5.0 4.0 5.0 4.0 4.0 4.0 5.0 4.0 2.0 2.0 3.0 4.0 2 5.0 5.0 2.0 2 NaN 17
3 61621 4.0 4.0 6.0 5.0 5.0 4.0 4.0 3.0 5.0 4.0 1.0 3.0 3 4.0 3.0 5.0 2 NaN 17
4 61622 2.0 3.0 3.0 4.0 5.0 4.0 4.0 5.0 3.0 4.0 3.0 3.0 3 4.0 3.0 3.0 1 NaN 17

2800 rows × 29 columns

Como em qualquer outro uso de dados, principalmente quando importados de fontes externas, fazemos uma verificação de estrutura e completeza ou a existência de valores ausentes (NaN). Os dados demográficos são armazendos em outro dataframe enquanto a avaliação das questões em si são deixadas no dataframe df depois de eliminados os campos relativos a dados demográficos.

# Tabela dfDemografico armazena id, sexo, educação e idade
dfDemografico = df[['id', 'gender', 'education', 'age']]

# Colunas desnecessárias são eliminadas de df
df.drop(['id', 'gender', 'education', 'age'],axis=1,inplace=True)

# Possíveis dados ausentes são eliminados
df.dropna(inplace=True)

# Para verificar as colunas de df
df.head(2)
A1 A2 A3 A4 A5 C1 C2 C3 C4 C5 N1 N2 N3 N4 N5 O1 O2 O3 O4 O5
0 2.0 4.0 3.0 4.0 4.0 2.0 3.0 3.0 4.0 4.0 3.0 4.0 2.0 2.0 3.0 3.0 6 3.0 4.0 3.0
1 2.0 4.0 5.0 2.0 5.0 5.0 4.0 4.0 3.0 4.0 3.0 3.0 3.0 5.0 5.0 4.0 2 4.0 3.0 3.0

2 rows × 25 columns

# Uma visão geral dos dados no dataframe df pode ser vista:
df.info()
<class ‘pandas.core.frame.DataFrame’>
Int64Index: 2436 entries, 0 to 2799
Data columns (total 25 columns):
# Column Non-Null Count Dtype
—————————————————————————————
0 A1 2436 non-null float64
1 A2 2436 non-null float64
2 A3 2436 non-null float64
3 A4 2436 non-null float64
4 A5 2436 non-null float64
Grupos C, E, N omitidos
20 O1 2436 non-null float64
21 O2 2436 non-null int64
22 O3 2436 non-null float64
23 O4 2436 non-null float64
24 O5 2436 non-null float64
dtypes: float64(24), int64(1)
memory usage: 494.8 KB

Observamos que apenas o campo O2 tem tipo de variável int64. Apenas para ter todos os campos do mesmo tipo fazemos a conversão para float64.

# Converter o campo O2 em float
df['O2'] = df['O2'].astype(np.float64)

type(df['O2'][0])    # Agora o campo é do tipo numpy.float64

Considerando que em operações que faremos podemos perder o nome das colunas, vamos armazenar esses nomes em uma varável (que nesse caso é uma série).

itens=df.columns
print(itens)
Index([‘A1’, ‘A2’, ‘A3’, ‘A4’, ‘A5’, ‘C1’, ‘C2’, ‘C3’, ‘C4’, ‘C5’, ‘E1’, ‘E2’,
‘E3’, ‘E4’, ‘E5’, ‘N1’, ‘N2’, ‘N3’, ‘N4’, ‘N5’, ‘O1’, ‘O2’, ‘O3’, ‘O4’,
‘O5′],
dtype=’object’)

Matriz de Correlação

Estamos prontos para encontrar a matriz de correlação entre as variáveis. Cada célula dessa matriz mostra a correlação entre duas variáveis, listadas como labels das linhas e colunas. Por isso ele tem os valores da diagonal iguais a 1 (que é a correlação da variável consigo mesma). A matriz de correlação fornece uma visão geral de interrelacionamento dos dados e é usada como input para análises mais advançadas.

A correlação .corr é um método de dataframes do pandas. Por default o método corr usa os coeficientes de Pearson mas também pode usar os coeficientes Tau de Kendall ou coefficientes de Spearman.

# A matriz de correlação entre todas as respostas na tabela
corrMatriz = df.corr()

# Para ver apenas as correlações entre variáveis do grupo A
corrMatriz[["A1", "A2","A3", "A4","A5"]].head(5)
A1 A2 A3 A4 A5
A1 1.000000 -0.350905 -0.273636 -0.156754 -0.192698
A2 -0.350905 1.000000 0.503041 0.350856 0.397400
A3 -0.273636 0.503041 1.000000 0.384918 0.515679
A4 -0.156754 0.350856 0.384918 1.000000 0.325644
A5 -0.192698 0.397400 0.515679 0.325644 1.000000

Em seguida fazemos o gráfico de calor (heatmap) da matriz de correlação. Usamos a biblioteca seaborn para isso. Um heatmap associa uma cor a cada valor na matriz de correção. Tons mais escuros de azul (nesse caso) são valores mais perto de 1, tons mais claros são valores mais perto de -1.

import seaborn as sns
plt.figure(figsize=(12,12))
sns.set(font_scale=1)
sns.heatmap(corrMatriz, linewidths=.1, linecolor='#ffffff',
            cmap='YlGnBu', xticklabels=1, yticklabels=1)


A mera análise do gráfico de calor permite que algumas características da pesquisa sejam visualmente reconhecidas. Duas variáveis diferentes com índice de correlação muito alto podem ser, na verdade, a mesma variável escrita de forma diversa. Nesse caso o pesquisador pode preferir retirar uma delas. Uma ou mais variáveis com nível de correlação muito baixo com todas as demais podem indicar a medida de elementos isolados e fora de contexto com o modelo explorado. Nesse caso vemos agrupamentos claros entre as variáveis A2, A3, A4, A5 e todas as do grupo N, só para citar alguns exemplos.

Análise Fatorial

Análise Fatorial Exploratória, AFE

Podemos agora dar início ao uso específico da Análise Fatorial, começando pela Análise Fatorial Exploratória, AFE, usando o módulo factor_analyzer. O primeiro passo para isso é a avaliação da fatorabilidade dos dados. Isso significa que os dados coletados podem ser agrupados em fatores, que são as nossas variáveis ocultas com o poder de sintetizar e melhor descrever o objeto estudado. Para isso o módulo factor_analyzer oferece dois testes: o Teste de Bartlett e o Teste de Kaiser-Meyer-Olkin.

Teste da esfericidade de Bartlett

O Teste da esfericidade de Bartlett verifica se as variáveis estão correlacionadas entre si, comparando a matriz de correlação com a matriz identidade (que representaria variáveis completamente não correlacionadas).

# Importa o módulo que realiza o teste de Bartlett
from factor_analyzer.factor_analyzer import calculate_bartlett_sphericity
chi_square_value, p_value = calculate_bartlett_sphericity(df)
print('Teste da Esfericidade de Bartlett: chi² = %d,  p_value = %d' % (chi_square_value, p_value))
Teste da Esfericidade de Bartlett: chi2 = 18170.966350869272 p_value = 0.

No nosso caso o teste de Bartlett resulta em p-value = 0, o que indica que os dados podem ser fatorados e a matriz de correlação observada não é a identidade.

Teste de Kaiser-Meyer-Olkin

O Teste de Kaiser-Meyer-Olkin (KMO) fornece uma técnica de avaliação se os dados colhidos são apropriados para esta análise fatorial. Ele realiza um teste para cada variável observada e para o conjunto completo de variáveis. O resultado representa o grau em que cada variável observada pode ser predita, sem erros, pelas demais variáveis no conjunto de dados. KMO é uma estimativa da proporção de variância entre todas as variáveis. Os valores de KMO podem estar entre 0 e 1 e valores abaixo de 0.6 são consideredos inadequados.

# Importa calculate_kmo
from factor_analyzer.factor_analyzer import calculate_kmo
kmo_all,kmo_model = calculate_kmo(df)

print('Valores de kmo_all =\n', kmo_all, '\n')
print('KMO =', kmo_model)
Valores de kmo_all =
[0.75391928 0.8363196 0.87010963 0.87795367 0.90348747 0.84325413
0.79568263 0.85186857 0.82647206 0.86401687 0.83801873 0.88380544
0.89697008 0.87731273 0.89332158 0.77933902 0.78025018 0.86229919
0.88518467 0.86014155 0.85858672 0.78019798 0.84434957 0.77003158
0.76144469]KMO = 0.848539722194922

Todos os valores de kmo_all são superiores a 0,7 e o KMO geral é KMO = 0.8485 o que são considerados valores muito favoráveis para a análise dos fatores.

Prosseguimos criando uma instância do objeto factor_analysis, tentativamente com 5 fatores

# Criamos objeto factor_analysis, sem rotação e usando 5 fatores (tentativamente)
fa = FactorAnalyzer(5, rotation=None)

# Aplicamos o método fit (ajuste) desse objeto no dataframe
fa.fit(df)

# Depois desse ajuste podemos coletar os autovetores e autovalores
ev, v = fa.get_eigenvalues()
print('São ' + str(len(ev)) + ' autovalores:\n', ev)
São 25 eigenvalues:
[5.13431118 2.75188667 2.14270195 1.85232761 1.54816285 1.07358247
0.83953893 0.79920618 0.71898919 0.68808879 0.67637336 0.65179984
0.62325295 0.59656284 0.56309083 0.54330533 0.51451752 0.49450315
0.48263952 0.448921 0.42336611 0.40067145 0.38780448 0.38185679
0.26253902]

Usando os autovalores calculados traçamos um Screeplot que é um gráfico que lista os autovalores em ordem decrescente, usado para determinar o número de fatores a serem retidos em uma análise fatorial exploratória. O teste, introduzido por R.B. Cattell em 1966, sugere manter tantos fatores quantos forem as autovalores anteriores a uma “dobra mais acentuada” ou “cotovelo” no gráfico. Também é sugerido manter o mesmo número de fatores quantos autovalores existirem maiores que 1.

from bokeh.plotting import figure, output_notebook, show
output_notebook()

eixoX = range(1, len(ev)+1)   # de 1 0 26
eixoY = ev
p = figure(title="Scree Plot", x_axis_label='n-ésimo autovalor',y_axis_label='autovalor',
           x_range=[0,25], y_range=(0, 6), plot_width=600, plot_height=400,
           background_fill_color="#c9b2dd")

p.line(eixoX, eixoY, line_width=1, color = 'black')
p.circle(eixoX, eixoY, size=8, fill_color='red', color="black")

show(p)

Algumas críticas são dirigidas ao teste feito dessa forma pois ele insere uma decisão pouco objetiva. Mais de um cotovelo podem aparecer no gráfico. De qualquer forma, como veremos no presente caso, o bom senso e a análise posterior dos agrupamentos de fatores podem sugerir uma alteração nesse número.

Cargas Fatoriais (factor loadings)

A carga fatorial é o coeficiente de correlação entre a variável e o fator. Ela mostra a variância explicada pela variável naquele fator em particular.

Prosseguimos criando um objeto FactorAnalyzer com 6 fatores (tentativamente) e usando o método de rotação varimax.

# 6 fatores
fa = FactorAnalyzer( 6, rotation="varimax")

# o objeto tem o método fit para análise do dataframe
fa.fit(df)

# Desse extraimos as cargas fatoriais (factor loadings)
# Observe que fa.loadings_ é um numpy.array com shape (25,6). Usamos o método
# do pandas pd.DataFrame.from_records para convertê-lo em um dataframe
factorLoadings = pd.DataFrame.from_records(fa.loadings_)

# Para ver a dataframe gerado:
factorLoadings.head(4)
0 1 2 3 4 5
0 0.095220 0.040783 0.048734 -0.530987 -0.113057 0.161216
1 0.033131 0.235538 0.133714 0.661141 0.063734 -0.006244
2 -0.009621 0.343008 0.121353 0.605933 0.033990 0.160106
3 -0.081518 0.219717 0.235140 0.404594 -0.125338 0.086356

Vemos que os nomes dos itens, de A1 até O5, foram perdidos no cálculo. Vamos renomear tanto esses itens quanto os nomes das colunas (que são os fatores) para ter uma visualização mais clara do que obtivemos até aqui:

# Substitue as linhas pelo nomes dos itens
factorLoadings.index=itens

# Renomeia as colunas
factorLoadings.rename(columns = {0:'Fator 1',
                                 1:'Fator 2',
                                 2:'Fator 3',
                                 3:'Fator 4',
                                 4:'Fator 5',
                                 5:'Fator 6'}, inplace = True)

# Exibe o resultado
factorLoadings
Fator 1 Fator 2 Fator 3 Fator 4 Fator 5 Fator 6
A1 0.095220 0.040783 0.048734 -0.530987 -0.113057 0.161216
A2 0.033131 0.235538 0.133714 0.661141 0.063734 -0.006244
A3 -0.009621 0.343008 0.121353 0.605933 0.033990 0.160106
A4 -0.081518 0.219717 0.235140 0.404594 -0.125338 0.086356
A5 -0.149616 0.414458 0.106382 0.469698 0.030977 0.236519
C1 -0.004358 0.077248 0.554582 0.007511 0.190124 0.095035
C2 0.068330 0.038370 0.674545 0.057055 0.087593 0.152775
C3 -0.039994 0.031867 0.551164 0.101282 -0.011338 0.008996
C4 0.216283 -0.066241 -0.638475 -0.102617 -0.143846 0.318359
C5 0.284187 -0.180812 -0.544838 -0.059955 0.025837 0.132423
E1 0.022280 -0.590451 0.053915 -0.130851 -0.071205 0.156583
E2 0.233624 -0.684578 -0.088497 -0.116716 -0.045561 0.115065
E3 -0.000895 0.556774 0.103390 0.179396 0.241180 0.267291
E4 -0.136788 0.658395 0.113798 0.241143 -0.107808 0.158513
E5 0.034490 0.507535 0.309813 0.078804 0.200821 0.008747
N1 0.805806 0.068011 -0.051264 -0.174849 -0.074977 -0.096266
N2 0.789832 0.022958 -0.037477 -0.141134 0.006726 -0.139823
N3 0.725081 -0.065687 -0.059039 -0.019184 -0.010664 0.062495
N4 0.578319 -0.345072 -0.162174 0.000403 0.062916 0.147551
N5 0.523097 -0.161675 -0.025305 0.090125 -0.161892 0.120049
O1 -0.020004 0.225339 0.133201 0.005178 0.479477 0.218690
O2 0.156230 -0.001982 -0.086047 0.043989 -0.496640 0.134693
O3 0.011851 0.325954 0.093880 0.076642 0.566128 0.210777
O4 0.207281 -0.177746 -0.005671 0.133656 0.349227 0.178068
O5 0.063234 -0.014221 -0.047059 -0.057561 -0.576743 0.135936

Vamos montar mais um heatmap com essa tabela.

# A bibioteca seaborn já foi importada como sns
plt.figure(figsize=(8,6))
sns.set(font_scale=.9)
sns.heatmap(factorLoadings,  linewidths=1, linecolor='#ffffff', cmap="YlGnBu", xticklabels=1, yticklabels=1)

Lembrando que as cores mais escuras indicam correlação direta e as mais claras correlação inversa, percebemos que existem cargas mais fortes entre os itens N1, N2, N3, N4 e N5 com o fator 1, E1 até E5 no fator 2, etc. Nenhum dos itens, no entanto, tem carga relevante no sexto fator. Isso indica que podemos refazer o cálculo de cargas fatoriais com apenas 5 fatores.

# Refazendo o cáculo com 5 fatores apenas
# Apaga a variável fa
del fa

fa = FactorAnalyzer( 5, rotation="varimax")
fa.fit(df)
factorLoadings = pd.DataFrame.from_records(fa.loadings_)

# Renomeia itens
factorLoadings.index=itens

# Renomeia as colunas (fatores)
factorLoadings.rename(columns = {0:'Fator 1',
                                 1:'Fator 2',
                                 2:'Fator 3',
                                 3:'Fator 4',
                                 4:'Fator 5'}, inplace = True)

# Exibe o resultado
factorLoadings
Fator 1 Fator 2 Fator 3 Fator 4 Fator 5
A1 0.111126 0.040465 0.022798 -0.428166 -0.077931
A2 0.029588 0.213716 0.139037 0.626946 0.062139
A3 0.009357 0.317848 0.109331 0.650743 0.056196
A4 -0.066476 0.204566 0.230584 0.435624 -0.112700
A5 -0.122113 0.393034 0.087869 0.537087 0.066708
C1 0.010416 0.070184 0.545824 0.038878 0.209584
C2 0.089574 0.033270 0.648731 0.102782 0.115434
C3 -0.030855 0.023907 0.557036 0.111578 -0.005183
C4 0.240410 -0.064984 -0.633806 -0.037498 -0.107535
C5 0.290318 -0.176395 -0.562467 -0.047525 0.036822
E1 0.042819 -0.574835 0.033144 -0.104813 -0.058795
E2 0.244743 -0.678731 -0.102483 -0.112517 -0.042010
E3 0.024180 0.536816 0.083010 0.257906 0.280877
E4 -0.115614 0.646833 0.102023 0.306101 -0.073422
E5 0.036145 0.504069 0.312899 0.090354 0.213739
N1 0.786807 0.078923 -0.045997 -0.216363 -0.084704
N2 0.754109 0.027301 -0.030568 -0.193744 -0.010304
N3 0.731721 -0.061430 -0.067084 -0.027712 -0.004217
N4 0.590602 -0.345388 -0.178902 0.005886 0.075225
N5 0.537858 -0.161291 -0.037309 0.100931 -0.149769
O1 -0.002224 0.213005 0.115080 0.061550 0.504907
O2 0.175788 0.004560 -0.099729 0.081809 -0.468925
O3 0.026736 0.310956 0.076873 0.126889 0.596007
O4 0.220582 -0.191196 -0.021906 0.155475 0.369012
O5 0.085401 -0.005347 -0.062730 -0.010384 -0.533778

Construimos o heatmap com essa tabela de 5 fatores.

plt.figure(figsize=(8,6))
sns.set(font_scale=.9)
sns.heatmap(factorLoadings,  linewidths=1, linecolor='#ffffff', cmap="YlGnBu", xticklabels=1, yticklabels=1)
Os retângulos de borda
preta indicando grupos
de correlação foram
acrescentados manualmente.

Pelo heatmap percebemos que o grupo de itens N está associado ao fator 1 (Neuroticismo), E ao fator 2 (Extroversão), C ao fator 3 (Conscienciosidade), A ao fator 4 (Amabilidade) e O ao fator 5 (Abertura), previamente identificados. É claro que essa identificação do fator foi realizada pelos pesquisadores no caso dessa pesquisa em particular. Para uma pesquisa nova seria necessário um estudo para o entendimento da natureza de cada fator.

Observe ainda que correlação negativa é correlação. É o que ocorre, por exemplo entre os itens A1 (Sou indiferente aos sentimentos das outras pessoas) e A2 (Sempre pergunto sobre o bem-estar dos outros). A correlação entre eles pode ser obtida da matriz de correlação, corrMatriz['A1']['A2'] = -0.35091.

Observamos que existem as seguintes correspondências entre os grupos de questões e os fatores propostos:

Fator Grupo Descrição
1 N Neuroticismo
2 E Extroversão
3 C Conscienciosidade
4 A Amabilidade
5 O Abertura

Vamos, portanto, renomear as colunas de nossa matriz de cargas fatoriais para refletir esse entendimento:

# Renomeia as colunas (fatores)
factorLoadings.rename(columns = {'Fator 1':'Neuroticismo',
                                 'Fator 2':'Extroversão',
                                 'Fator 3':'Conscienciosidade',
                                 'Fator 4':'Amabilidade',
                                 'Fator 5':'Abertura'}, inplace = True)

# Exibe o resultado (só duas linhas)
factorLoadings.head(2)	
Neuroticismo Extroversão Conscienciosidade Amabilidade Abertura
A1 0.111126 0.040465 0.022798 -0.428166 -0.077931
A2 0.029588 0.213716 0.139037 0.626946 0.062139

Comunalidades

Comunalidades são a soma das cargas fatoriais ao quadrado de cada variável medida. Denotando por \(l_{ij}\) os elementos da matriz da cargas fatoriais a comunalidade da i-ésima variável é \(h_i^2\) dado por
$$
h_i^2 =\Sigma_{j=1}^n l_{ij}^2
$$
Por exemplo, a comunalidade relativa à questão A1 é a soma dos elementos (ao quadrado) da primeira linha da matriz de cargas fatoriais acima:

(0.111126)**2 + (0.040465)**2 + (0.022798)**2 + (-0.428166)**2 +(-0.077931)**2
0.203905517222

As comunalidades relativas a todas as variáveis podem ser obtidas com get_communalities() que retorna um numpy.ndarray com dimensões (25,):

fa.get_communalities()
array([0.20390517, 0.46280338, 0.53969218, 0.30190473, 0.47002029,
0.34839471, 0.45387181, 0.32428892, 0.47669926, 0.43538283,
0.34780933, 0.545502 , 0.44105495, 0.54125654, 0.40714621,
0.68139838, 0.60800298, 0.54447487, 0.50580328, 0.34931564,
0.31733902, 0.26745151, 0.47464268, 0.2460347 , 0.29628368])

A soma de todos os valores de comunalidade é o valor de comunalidade total:

fa.get_communalities().sum()
10.590479059488883

Para exibir uma tabela com os nomes da variáveis e suas respectivas comunalidades vamos construir um dataframe contendo esses dados. Lembrando que já temos a variável itens = ['A1', 'A2', ..., 'O4','O5']:

dfComunalidades = pd.DataFrame(comunalidades)
dfComunalidades.index = itens
Variável Comunalidade
A1 0.203905
A2 0.462803
A3 0.539692
A4 0.301905
A5 0.470020
C1 0.348395
C2 0.453872
C3 0.324289
C4 0.476699
C5 0.435383
Variável Comunalidade
E1 0.347809
E2 0.545502
E3 0.441055
E4 0.541257
E5 0.407146
N1 0.681398
N2 0.608003
N3 0.544475
N4 0.505803
N5 0.349316
Variável Comunalidade
O1 0.317339
O2 0.267452
O3 0.474643
O4 0.246035
O5 0.296284
Total 10.5905
Como temos 25 fatores:
Total/25 0.4236

Podemos pensar na comunalidade de uma variável como a proporção de variação nessa variável explicada pelos fatores propostos no modelo. Por exemplo, a variável N1 tem a maior comunalidade (0.681398) nesse modelo, indicando que aproximadamente 69% da variação nas respostas para “N1: Fico com raiva facilmente” é explicada pelo modelo de 5 fatores proposto enquanto esse valor é de apenas 20% para “A1: Sou indiferente aos sentimentos das outras pessoas”.

Comunalidades servem para avaliar o desempenho do modelo. Valores mais próximos de um indicam que o modelo explica a maior parte da variação para essas variáveis. Nesse caso o modelo está melhor ajustado para as variáveis do grupo N (neuroticismo)​ e menos eficiente para as variáveis do grupo O (abertura)​.

A Comunalidade total é de 10.5905 que, dividido entre as 25 variáveis indica uma média de 10.5905/25 = 0.4236 geral para o modelo, ou seja, uma eficiência média de 42% do modelo em explicar a variação de cada variável do teste.

🔺Início do artigo

Bibliografia

Linguagem de Consulta SQL


Definições

Um banco de dados é uma coleção de dados armazenada e acessada eletronicamente. Tipicamente consiste em várias tabelas e das relações predefinidas entre elas. Cada tabela contém colunas ou campos, cada uma delas com seu nome exclusivo e tipos de dados definido. Elas podem ter atributos que definem a funcionalidade da coluna (se é uma chave primária, se há um valor default, etc.). As linhas da tabela são chamadas de registros e contêm os dados armazenados.

Banco de dados relacional é um banco de dados digital baseado no modelo relacional proposto por E. F. Codd em 1970. Sistema de software usados para construir e manter bancos de dados relacionais são chamados Sistema de Gerenciamento de Banco de Dados Relacionais (RDBMS).

SQL, (Structured Query Language) ou Linguagem de Consultas Estruturada é uma linguagem de programação usada para manipular de bancos de dados relacionais. Usando SQL é possível construir o BD, estabelecer relações entre dados nas tabelas, fazer consultas, inserir, apagar ou editar dados.

Para que serve a SQL?

Consultas SQL podem ser feitas dentro de diversos aplicativos tais como em suites MS Office, em particular no MS Access, e seus equivalentes open source. Elas também são frequentes em aplicativos que armazenam e manipulam informações, de Business Inteligence e análises de dados tais como Qlikview e MS PowerBI. A maior parte dos sistemas de gerenciamento de conteúdo usados em sites na Internet, como WordPress e Joomla usam bancos de dados controlados por SQL.

A maioria das linguagens de programação fornecem interfaces com as variantes de bancos de dados SQL. Além disso podemos instalar um Sistema Gerenciador de Banco de Dados (DBMS) em seu computador, seja ele um servidor ou sua desktop pessoal, disponíveis em todas as plataformas. Os DBMS mais populares são: SQLite, MySQL, PostgreSQL, LibreOffice Base (todos eles open source) e Microsoft Access, SQL Server, FileMaker, Oracle (proprietários).

Comandos e instruções do SQL

Vamos considerar o exemplo de uma escola que mantém um banco de dados com informações sobre alunos, professores, funcionários, disciplinas lecionadas, salas de aulas, e a descrição de relacionamentos entre eles. Segue um exemplo simples de uma tabela contendo o dados (simplificados e fictícios) de alunos. A primeira linha, em negrito, contém os nomes das colunas da tabela (os campos):

Tabela (1): Alunos
Matricula Nome Sobrenome email Nascimento Fone
734236 João Santos joao@yahoo.com 02-04-1998 61 123455667
789234 George Pereira george@gmail.com 04-04-2000 41 345678987
654987 Paula Torres ptorres@globo.com 25-01-2004 31 987854543
765098 Marcos Melo mamelo@gmail.com 25-10-2004 31 987843231

Nessa tabela Matricula é um campo numérico que, sendo composto de valores únicos, pode ser usado como um id ou identificador. Nome, Sobrenome, email e Fone são campos de strings, Nascimento é um campo de datas.

SELECT

Comandos SQL não são sensíveis à maiúsculas e minúsculas mas é uma convenção escrevê-los em maiúsculas, o que ajuda a diferenciá-los quando embutidos dentro de códigos de outra linguagem.

A instrução SELECT é usada para recuperar (ler e retornar) dados de uma tabela. Ela tem a seguinte sintaxe geral:

SELECT Coluna1, ..., Colunan FROM Tabela1
SELECT * FROM Tabela1

A primeira consulta retorna todos os registros (linhas) da Tabela1, existentes nos campos listados, Coluna1, …, Colunan, sem qualquer critério de seleção. A segunda consulta retorna todas as linhas e todos os campos da Tabela1. O asterisco * substitui a lista de todos os campos. Devemos observar que a declaração explícita de quais campos se pretende retornar torna a consulta mais eficiente (rápida). O sinal -- (dois hífens) marca o início de um comentário em SQL, uma parte da instrução que será ignorada.

Por exemplo, para retornar todos os Nomes e Sobrenomes da tabela usamos

SELECT Nome, Sobrenome FROM Alunos
-- a tabela abaixo será retornada
Tabela (2):
Nome Sobrenome
João Santos
George Pereira
Paula Torres
Marcos Melo

Cláusula WHERE

A Cláusula WHERE é uma modificação da consulta com SELECT especificando condições de retorno. Ela restringe os dados retornados para apenas aqueles satisfeitos pela cláusula. Por exemplo, lembrando que o campo matrícula é numérico:

SELECT Nome, Sobrenome, email FROM Alunos WHERE Matricula = 654987
Tabela (3):
Nome Sobrenome email
Paula Torres ptorres@globo.com
SELECT Matricula, email FROM Alunos WHERE Nome = 'Marcos'
Tabela (4):
Matricula email
765098 mamelo@gmail.com

Os seguintes operadores de comparação podem ser usados com WHERE:

= igual, usado acima 🔘 < menor que
<> diferente 🔘 =< menor ou igual
> maior que 🔘 LIKE similar
>= maior ou igual 🔘 BETWEEN entre (define uma faixa)

Podemos então requisitar todos os registros com Matricula > 740000 ou Nascimento <> '25-01-2004':

SELECT Matricula, Nome, Sobrenome FROM Alunos WHERE Matricula > 740000
Tabela (5):
Matricula Nome Sobrenome
789234 George Pereira
765098 Marcos Melo
SELECT Matricula, Nome, Sobrenome FROM Alunos WHERE Nascimento <> '25-01-2004'
Tabela (6):
Matricula Nome Sobrenome
734236 João Santos
789234 George Pereira

O mesmo procedimento é usado para os demais operadores.

O Operador LIKE pode ser usado junto com %, um wildcard ou coringa que representa qualquer string (um ou mais caracteres). Ele pode ser posto em qualquer lugar, quantas vezes for necessário.

SELECT * FROM Alunos WHERE Nome LIKE 'PA%'
-- Nomes que começam com 'PA'
Tabela (7):
Matricula Nome Sobrenome email Nascimento Fone
654987 Paula Torres ptorres@globo.com 25-01-2004 31 987854543

O Operador BETWEEN possui sintaxe um pouco diferente para cada RDBMS. Em geral é algo do tipo:

SELECT * FROM Alunos WHERE Nascimento BETWEEN '01-01-2002' AND '01-01-2005'
-- Nascidos no intervalo
Tabela (8):
Matricula Nome Sobrenome email Nascimento Fone
654987 Paula Torres ptorres@globo.com 25-01-2004 31 987854543
765098 Marcos Melo mamelo@gmail.com 25-01-2004 31 987843231

O coringa _ representa um único caracter e [] uma faixa de valores. Por exemplo, a consulta seguinte retorna todos os registros onde Fone começa com 3, 4, 5 ou 6 e tem qualquer segundo dígito. No caso de nossa tabela Alunos seriam todos os registros.

SELECT * FROM Alunos WHERE Fone LIKE '[3-6]_%'

SELECT DISTINCT

Se você quiser obter valores únicos de uma tabela, sem repetições, use a cláusula DISTINCT.

SELECT DISTINCT Nascimento FROM Alunos 
Tabela (9):
Nascimento
02-04-1998
04-04-2000
25-01-2004

ORDER BY

A cláusula ORDER BY é usada junto com SELECT para ordenar os resultados de uma consulta. Ela tem a seguinte sintaxe geral:

SELECT campo1, ..., campon FROM Tabela1 ORDER BY (lista de campos)

Por exemplo:

-- Para retornar todos os Nomes e Sobrenomes da tabela, em ordem de Nome
SELECT Nome, Sobrenome FROM Alunos ORDER BY Nome
Tabela (10):
Nome Sobrenome
George Pereira
João Santos
Marcos Melo
Paula Torres

ORDER BY pode ser usada com os modificadores ASC ou DESC, para produzir a lista em ordem crescente ou descendente. ASC é o default e não precisa ser especificado. Para obter a mesma lista anterior em ordem descendente no sobrenome:

-- Para retornar todos os Nomes e Sobrenomes da tabela, em ordem de Nome
SELECT Nome, Sobrenome FROM Alunos ORDER BY Sobrenome DESC
Tabela (11):
Nome Sobrenome
Paula Torres
João Santos
George Pereira
Marcos Melo

TOP

A cláusula TOP é usada junto com SELECT e, geralmente com ORDER BY para selecionar apenas um número fixo de resultados retornados por uma consulta. Ela tem a seguinte sintaxe geral:

SELECT TOP número| % campo1, ..., campon FROM Tabela1 ORDER BY (lista de campos)

Por exemplo:

-- Para retornar os dois últimos Nomes da tabela, em ordem de Nome
SELECT TOP 2 Nome, Sobrenome FROM Alunos ORDER BY Nome DESC
Tabela (12):
Nome Sobrenome
Paula Torres
Marcos Melo

Para ver 10% dos primeiros registros de uma lista podemos usar:

SELECT TOP 10% * FROM Tabela ORDER BY campo

Operadores lógicos

Operadores lógicos são usados para testes compostos com mais de uma condição.

Operador Descrição
AND TRUE se ambas as condições ligadas forem TRUE,
OR TRUE se ambas ou uma delas forem TRUE,
IN se um valor está presente em uma lista
NOT nega uma expressão lógica
IS NULL se um valor é nulo (NULL)
IS NOT NULL se um valor não é nulo

Operadores AND e OR

Mais de uma condição podem ser anexadas à cláusula WHERE usando os operadores AND e OR.

SELECT * FROM Alunos WHERE Matricula > 700000 AND Fone LIKE '31%'
Tabela (13):
Matricula Nome Sobrenome email Nascimento Fone
765098 Marcos Melo mamelo@gmail.com 25-10-2004 31 987843231
SELECT * FROM Alunos WHERE Matricula > 700000 OR Sobrenome = 'Torres'

Nessa última consulta todos os registros seriam retornados pois Paula Torres é a única aluna com matrícula inferior à 700000.

Condições mais complexas podem ser expressas usando-se parênteses.

SELECT Matricula, Nome, Sobrenome FROM Alunos
   WHERE (Nome = 'George' OR Nome = 'Paula') AND Sobrenome = 'Pereira'
Tabela (14):
Matricula Nome Sobrenome
789234 George Pereira

Operador lógico IN

O operador IN é usado junto com WHERE para selecionar um campo com valor dentro de um conjunto de valores discretos. A sintaxe é

SELECT campo1, ..., campon
   FROM Tabela
   WHERE campo1 IN (valor1, ..., valorr)
SELECT * FROM Alunos WHERE Matricula IN (654987, 765098)
Tabela (15):
Matricula Nome Sobrenome
654987 Paula Torres
765098 Marcos Melo

SELECT INTO

A instrução SELECT INTO é usada para selecionar registros e campos de uma tabela e copiar o resultado retornado em uma nova tabela.

SELECT Coluna1, ..., Colunan INTO Tabela2 FROM Tabela1	WHERE <condições>

A Tabela2 será criada com os mesmos campos e relacionamentos da tabela inicial. Alguns casos são listados a seguir:

-- Faça uma cópia backup de Alunos
SELECT * INTO AlunosBackup FROM Alunos;

-- Faça uma cópia backup de Alunos cujo nome começa com 'Pa'
SELECT * INTO AlunosBackup FROM Alunos WHERE Nome LIKE 'PA%';

-- Faça uma cópia backup de Alunos em outro banco de dados (BDCopia)
SELECT * INTO AlunosBackup IN 'BDCopia' FROM ALunos;

-- Cria tabela vazia novoAlunos com estrutura idêntica a de Alunos
SELECT * INTO novoAlunos FROM Alunos WHERE 1 = 0;

Obs.: Aqui o teste 1 = 0 foi introduzida apenas para gerar um booleano FALSE. Nem todas as versões de gerenciadores comportam dados booleanos. Em alguns podemos usar WHERE TRUE, WHERE 1 (que são equivalentes), ou WHERE FALSE, WHERE 0 ou WHERE NOT TRUE(também equivalentes).

INSERT INTO

A instrução INSERT INTO é usada para inserir novos registros em uma tabela. Ele pode ser usado em dois formatos:

INSERT INTO Tabela1 VALUES (valor1, ..., valorn)
-- ou
INSERT INTO Tabela1 (campo1, ..., campon)  VALUES (valor1, ..., valorn)

No primeiro caso \(n\), o número de valores inseridos deve ser igual ao número de campos da tabela e devem estar na ordem default. No segundo caso a ordem pode ser alterada mas campon) deve corresponder à valorn. Em ambos os casos o tipo de dado do campo deve ser respeitado. Por exemplo: as seguintes instruções aplicadas sobre a tabela Alunos:

-- Inserindo todos os campos
INSERT INTO Alunos VALUES (854254, 'João', 'Alves', 'jalves@yahoo.com', '15-03-2004', '31 885466112')
-- Inserindo alguns campos
INSERT INTO Alunos (Matricula, Nome, Sobrenome) VALUES (785294, 'Marta', 'Soares')

alteraria a tabela para

Tabela (16):
Matricula Nome Sobrenome email Nascimento Fone
734236 João Santos joao@yahoo.com 02-04-1998 61 123455667
789234 George Pereira george@gmail.com 04-04-2000 41 345678987
654987 Paula Torres ptorres@globo.com 25-01-2004 31 987854543
765098 Marcos Melo mamelo@gmail.com 25-01-2004 31 987843231
854254 João Alves jalves@yahoo.com 15-03-2004 31 885466112
785294 Marta Soares

Os valores de campos não fornecidos na última instrução ficam nulos (null, inexistência de valor) ou assumem um valor default definido na construção da estrutura da tabela.

UPDATE

A instrução UPDATE é usada para alterar registros já inseridos em uma tabela. Ela tem a sintax geral:

UPDATE Tabela1
   SET campo1 = valor1, ..., campon = valorn
   WHERE <condições>

O formato acima mostra que podemos quebrar as linhas de uma instrução SQL para torná-la mais legível. A cláusula WHERE <condições> limita quais os registros serão alterados. Sem ela todos os registros (todas as linhas da tabela) seriam alterados.

Por exemplo, podemos completar o registro relativo à aluna Marta em nossa tabela:

UPDATE Alunos
   SET Sobrenome = 'Alves', email = 'marta123@yahoo.com', Nascimento = '14-03-2001', fone = '21 956855441'
   WHERE Matricula = 785294
-- Para ver o resultado (os demais registros ficam inalterados)
SELECT * FROM Alunos WHERE Matricula = 785294

O resultado seria a tabela:

Tabela (17):
Matricula Nome Sobrenome email Nascimento Fone
785294 Marta Alves marta123@yahoo.com 14-03-2001 21 956855441

Novamente, se não tivéssemos especificado a condição Matricula = 785294 todos os registros, de todos os alunos seriam alterados.

Suponha que, por algum motivo, a escola tenha decido alterar o padrão de numeração das matrículas acrescentando o dígito 1 à esquerda de todas as matrículas. Isso seria o mesmo que somar 1000000 à todas as matrículas. Podemos conseguir isso com a seguinte operação:

UPDATE Alunos SET Matricula = Matricula + 1000000
-- Para ver o resultado (os demais campos ficam inalterados)
SELECT Matricula FROM Alunos
Tabela (18):
Matricula
1734236
1789234
1654987
1765098
1854254
1785294

DELETE

A instrução DELETE permite o apagamento de registros em uma tabela. Ela tem a sintax geral:

DELETE FROM Tabela1 WHERE <condições>

O formato acima mostra que podemos quebrar as linhas de uma instrução SQL para torná-la mais legível. A cláusula WHERE <condição> limita quais os registros serão apagados. Sem ela todos os registros (todas as linhas da tabela) seriam apagadas.

Por exemplo, podemos apagar os registros relativos aos alunos com sobrenome “Alves” de nossa nossa tabela:

DELETE FROM Alunos WHERE Sobrenome = 'Alves'

Dois alunos seriam removidos da tabela que ficaria assim:

Tabela (19):
Matricula Nome Sobrenome email Nascimento Fone
1734236 João Santos joao@yahoo.com 02-04-1998 61 123455667
1789234 George Pereira george@gmail.com 04-04-2000 41 345678987
1654987 Paula Torres ptorres@globo.com 25-01-2004 31 987854543
1765098 Marcos Melo mamelo@gmail.com 25-01-2004 31 987843231

Se não tivéssemos especificado uma condição para o apagamento todos os registros seriam apagados. A linha abaixo apagaria todos os registros da tabela Alunos:

DELETE FROM Alunos

A tabela e sua estrutura continuaria existindo.

Aliases

Aliases (nomes alternativos) são usados para simplificar uma consulta. Nossa tabela de exemplo é uma tabela pequena e simples. Na prática os bancos de dados e tabelas podem conter muitos campos. A possibilidade de renomear tabelas e campos pode ser muito útil, principalmente quando a consulta envolve mais de uma tabela e a consulta se refere às tabelas e campos mais de uma vez.

Por exemplo considerando que nossa tabela está no estado da Tabela (19) a consulta

SELECT Matricula, Nome + ', ' + Sobrenome AS NomeCompleto FROM Alunos

resulta em

Tabela (20):
Matricula NomeCompleto
1734236 João Santos
1789234 George Pereira
1654987 Paula Torres
1765098 Marcos Melo

Aqui foi feita uma concatenação das strings Nome + Sobrenome e o resultado renomeado como NomeCompleto.

Quando usamos várias tabelas de um banco de dados pode ocorrer que mais de uma delas tenha um campo com o mesmo nome. Nesse caso é obrigatória descriminar a que tabela nos referimos. A mesma consulta acima pode ser colocada na seguinte forma, com o mesmo resultado:

SELECT a.Matricula, a.Nome + ', ' + a.Sobrenome AS NomeCompleto FROM Alunos a

Aqui a tabela Alunos ganhou o alias a. a.Matricula se refere ao campo Matricula da tabela Alunos.

Agrupamentos e funções de grupos

Vamos acrescentar novas tabelas para considerar os agrupamentos. Suponha que a escola possui 4 funcionários identificados por um id único (um número de indentificação). Uma tabela armazena a relação entre id e nome (nome do funcionário). Outras tabela contém o número de horas trabalhadas por dia, para cada funcionário.

Tabela (21): Horas_trabalhadas
id dia horas
36 ’01-03-2019′ 6
41 ’01-03-2019′ 8
48 ’01-03-2019′ 2
58 ’01-03-2019′ 8
36 ’02-03-2019′ 5
41 ’02-03-2019′ 8
48 ’02-03-2019′ 1
58 ’02-03-2019′ 4
Tabela (22): Funcionarios
id Nome
36 Mariana Goulart
41 Tânia Ferreira
48 Humberto Torres
58 Francisco Pedroso

Diversas operações são permitidas sobre as linhas de uma tabela. Em particular

Instrução Efeito
GROUP BY Agrupa registros por valores do campo
Função Efeito
COUNT Conta o número de ocorrências do campo
MAX Fornece o valor máximo do campo
MIN Fornece o valor mínimo do campo
AVG Fornece a média dos valores do campo
SUM Fornece a soma dos valores do campo

Por exemplo, para calcular o número de horas trabalhadas por todos os funcionários podemos usar a consulta

SELECT SUM(horas) as soma FROM Horas_trabalhadas

que resulta em um único registro para um único campo:

Tabela (23):
soma
42

Para calcular o número de horas trabalhadas por cada funcionário fazemos a soma das horas com os campos de horas agrupados por cada funcionário.

SELECT id, SUM(horas) as soma
   FROM Horas_trabalhadas
   GROUP BY id
Tabela (24): Horas_trabalhadas
id horas
36 11
41 16
48 3
58 12

A consulta a seguir mostra o cálculo da média de horas trabalhadas por funcionário e de quantos dias cada um trabalhou.

SELECT id, AVG(horas) as media, COUNT(id) as numDias
   FROM Horas_trabalhadas
   GROUP BY id
Tabela (25): Média e número de dias trabalhados
id media numDias
36 5.5 2
41 8 2
48 1.5 2
58 6 2

Cláusula HAVING

A cláusula HAVING é usada para se estabelecer critérios sobre valores obtidos em funções agregadas. No SQL não é permitido usar WHERE para restringir resultados de uma consulta agregada. A consulta seguinte resultaria em erro:

SELECT id, SUM (horas) 
    FROM Horas_trabalhadas
    WHERE SUM (horas) > 11
    -- Isso geraria um erro
    GROUP BY id

Para esse efeito usamos HAVING, uma forma de se especificar condições sobre o resultado de uma função agregada.

SELECT id, SUM (horas) as soma
   FROM Horas_trabalhadas
   GROUP BY id
   HAVING soma > 11

Essa consulta gera a tabela (uma modificação da Tabela 24), satisfeita a condição soma > 11:

Tabela (24′): Horas_trabalhadas
id soma
41 16
58 12

Instrução JOIN

É claro que a última tabela ficaria mais fácil de interpretar se, ao invés de conter apenas ids, ela contivesse também os nomes dos funcionários. Essa informação está contida na Tabela (22): Funcionarios. A instrução JOIN serve para ler dados relacionados, gravados em mais de uma tabela. Sua forma geral é:

SELECT tabela1.campo11, tabela1.campo12, tabela2.campo21, tabela2.campo22, ...
   FROM tabela1 INNER JOIN tabela2 ON tabela1.id1 = tabela2.id2

Esse comando seleciona todos os campo11 e campo12 da tabela1, campo21, campo22, … da tabela2, relacionados pela condição tabela1.id1 = tabela2.id2. OS campos id1 e id2 podem ter ou não os mesmos nomes. Usando aliáses essa consulta pode ser deixada mais clara (e isso se torna mais importante para consultas mais longas envolvendos muitos campos e tabelas):

SELECT t1.campo11, t1.campo12, t2.campo21, t2.campo22, ...
   FROM tabela1 t1 INNER JOIN tabela2 t2 ON t1.id1 = t2.id2

Por exemplo, se quisermos uma tabela com os mesmos resultados da tabela (24) mas incluindo nomes dos funcionários fazemos

SELECT ht.id, f.Nome, SUM(horas) as soma
   FROM Horas_trabalhadas ht
   INNER JOIN Funcionarios f ON ht.id = f.id
   GROUP BY ht.id HAVING soma > 11

Com o resultado:

Tabela (26): Horas trabalhadas por funcionário
id Nome soma
41 Tânia Ferreira 16
58 Francisco Pedroso 12

Existem outros tipos de junções ou JOINs no SQL:

  • (INNER) JOIN: Retorna registros com valores correspondentes nas duas tabelas.
  • LEFT (OUTER) JOIN: Retorna registros da tabela esquerda (a primeira na query) e os correspondentes na tabela direita.
  • RIGHT (OUTER) JOIN: Retorna registros da tabela direita e os registros correspondentes da tabela esquerda.
  • FULL (OUTER) JOIN: Retorna registros quando houver correspondência em qualquer uma das tabelas.
Variantes do operador JOIN

Bibliografia

  • Faroult, Stéphane; Robson, Peter: The Art of SQL, O’Reilly Media, Sebastopol, CA, 2006.
  • Taylor,Allen: SQL For Dummies, 9th Edition John Wiley & Sons, New Jersey, 2019.

Páginas na Web:

O Futuro da Inteligência Artificial

Expectativa e Desafio Futuro

No campo das expectativas para um futuro muito próximo podemos mencionar as interfaces entre cérebro e computador mediadas por IAs, conectados à máquinas externas tais como exoesqueletos. Esses sistemas estarão disponíveis para pacientes com acidentes cérebro-vasculares, com traumas, doenças neurológicas, ou simplesmente como uma extensão de habilidades de uma pessoa saudável.

Nenhuma nova tecnologia é introduzida sem apresentar simultaneamente problemas e desafios em seu uso. Estima-se que um número relevante de empregos será perdido para a automação inteligente. Diferente das máquinas mecânicas, que substituíram o trabalhador braçal, agora é razoável considerar que computadores farão o trabalho de profissionais com níveis mais elevados de qualificação. Investimento em equipamentos, e não em pessoas, provavelmente aumentará o problema do desemprego e da concentração de renda.

O uso das máquinas para recomendações de conteúdo, por exemplo, tem se mostrado problemático em algumas plataformas. Buscando atrair a atenção do usuário e mantê-lo por mais tempo conectado e fidelizado as redes apresentam como sugestões conteúdos cada vez mais contundentes, muitas vezes envolvendo violência e intolerância. A indicação de conteúdo de fácil aceitação, em geral aprovado por muitos usuários, tende a esconder aqueles de menor circulação ou de gosto mais elaborado. Como resultado se observa a tendência de uniformização e formação de guetos, com a consequente inclinação à radicalização e intolerância. Junta-se a isso a facilidade para a geração inteligente de imagens, áudios e vídeos falsos que torna as campanhas de desinformação ainda mais nocivas e de difícil deteção.

Os problemas envolvendo a indústria da propaganda nos meios virtuais ficam exacerbados pelas práticas ilegais e imorais do roubo de dados. Dados se tornaram um produto valioso e a ausência de uma legislação atualizada estimula a prática da invasão de computadores e telefones pessoais e corporativos. Redes sociais importantes já foram flagradas vendendo informações sobre seus usuários que são usadas para o mero estímulo ao consumo ou para o atingimento de metas políticas muitas vezes obscuras e antidemocráticas.

A habilidade das IAs de reconhecer texto escrito e falado e extrair conteúdo das linguagens naturais agrava a ameaça à privacidade. Uma espionagem feita pelo microfone de um celular pode não apenas indicar que tipo de propaganda deve ser oferecida ao usuário mas também revelar traços que essa pessoa gostaria de manter privados, tais como traços de comportamentos íntimos ou a presença de uma doença.

Questões éticas, usualmente difíceis, ficam mais complexas na presença de IAs. Quem deve ser responsabilizado se um médico autômato comete um erro em seu diagnóstico ou procedimento? Quanta autonomia se pode atribuir à um juiz máquina ou a um automóvel auto-guiado? É aceitável permitir que um drone armado dispare contra um grupo que ele considera perigoso?

Provavelmente a afirmação do falecido físico Stephen Hawkings de que “O desenvolvimento de uma inteligência artificial completa pode determinar o fim da raça humana” seja pessimista em excesso.

É inegável, no entanto, que vigilância e responsabilidade devem ser elementos comuns no uso de toda nova tecnologia.

Referências

CHOLLET, F.. Deep Learning with Python. Nova Iorque: Manning Publications Co., 2018.

GOODFELLOW, I.; BENGIO, Y.; COURVILLE, A. Deep Learning: Cambridge, MA : MIT Press, 2017.

UNESCO. Artificial Intelligence in Education: Challenges and Opportunities for Sustainable Development, United Nations Educational, Scientific and Cultural Organization, Paris, 2019.

Inteligência Artificial

Onde o Aprendizado de Máquina é usado?

Máquinas treinadas podem ser empacotadas em aplicativos para computadores, telefones celulares ou circuitos com processadores que podem ser inseridos em outros equipamentos maiores, como automóveis e aviões, ou simplesmente serem carregados com o usuário.

Bons exemplos são os programas de busca e remoção de vírus de computadores e programas espiões que visam o roubo de dados pessoais ou corporativos. Novos vírus são disseminados a todo momento mas a maior parte de seu código replica o de vírus já detectados e combatidos. Sistemas inteligentes podem prever com boa exatidão quando um código representa um ataque ou, caso contrário, informar sobre possíveis riscos que ele representa. Da mesma forma um servidor de correio eletrônico (email) pode processar as mensagem recebidas e seus anexos em busca de spams, vírus, mensagens enviadas por remetentes cadastrados como perigosos ou prever outras anomalias.


Procedimentos de segurança de pessoas ou grupos podem ser automatizados. Filas de aeroportos ou de ingresso a um evento público de grande comparecimento podem ser varridas por meio de câmeras que localizam faces cadastradas em listas de criminosos ou encontrar outras características suspeitas que seriam de difícil identificação por funcionários humanos.

A análise de séries temporais, dados coletados ao longo do tempo, submetidas ao treinamento de máquina pode, em muitos casos, permitir a previsão de eventos futuros ou a inferência retrógrada de períodos passados que não foram documentados. Dois casos de interesse evidente são as previsões meteorológicas e da flutuação de mercados e bolsas de valores. Em ambos os casos as previsões automatizadas estão sendo rapidamente aprimoradas e os resultados cada vez mais úteis. A previsão meteorológica, por exemplo, permite o planejamento de ações de defesa ou de emergência em casos de tempestades. Empresas voltadas para as aplicações financeiras têm conseguido sucesso relevante nessa previsão, o que se reflete em lucro. Bancos e outros agentes financeiros usam a IA para previsão de comportamento de clientes, para sugestões de investimentos e prevenção de fraudes.

Um exemplo não óbvio da análise de séries temporais está nos aplicativos de reconhecimento de voz. Conversão direta de voz em texto ou vice-versa, de tradução ou de uso nos chamados assistentes pessoais, tais como o Alexa do Google e Siri da Apple são aplicações deste tecnologia. Através desses assistentes o usuário pode acionar outros aplicativos em seu aparelho pessoal, escolher músicas, enviar emails, entre outras funcionalidades.

A análise automática de textos já é usada nos tribunais brasileiros para triagem e encaminhamento de processos. Muitas questões estão em aberto no que se refere ao uso desses sistemas e se torna necessário discutir a inserção do assunto nos currículos de ensino de Direito.

O reconhecimento de significado de texto representa uma parte importante dos aplicativos de IA. Chatbots são aplicativos que simulam uma conversação com o usuário, fazendo pesquisas de opinião e preferência, sugerindo um procedimento para o usuário ou escolhendo o atendente humano que melhor responderá a suas demandas. A análise automática de um texto pode discriminar o estado de humor de quem o escreveu e sugerir a ele uma página na web de seu interesse ou um produto que ele esteja inclinado a consumir.

Muitas desta aplicações podem, e de fato o fazem, se beneficiar de um acesso direto à informação obtida por meio de sensores de natureza diversa, principalmente quando estão conectados à internet e transmitem dados em tempos real. Exemplos disso são as leituras de GPS (Global Positioning System ou Sistema Global de Posicionamento) que permitem a exibição de mapas de trânsito atualizados a cada instante, exibindo rotas de menor tráfego ou pontos de congestionamentos. Usando estes sistemas associados a diversos outros sensores, todos analisados por IA, automóveis autônomos podem circular pelas ruas das cidades provocando um número de acidentes inferior àqueles provocados por motoristas humanos. Da mesma forma um drone pode entregar produtos adquiridos remotamente diretamente nas mãos do consumidor.

Baseados na interação que fazemos com as páginas das chamadas rede sociais é possível a captura muito precisa de personalidades, gostos e tendências de um usuário, o que é usado pelos agentes de mídia para veicular propaganda de consumo, de comportamento ou política. Os provedores de conteúdo áudio-visuais, tais como o Netflix, o Youtube e Spotify, usam IAs para acompanhamento dos hábitos de seus usuários para indicar novos acessos à filmes, músicas ou outros produtos de qualquer natureza.

As aplicações de máquinas inteligentes são particularmente animadoras no campo da saúde, em especial para diagnósticos por imagem. Doenças de pele ou dos olhos, ou células cancerosas podem ser detectadas por meio da análise de imagens, muitas vezes com precisão superior à obtida por um técnico humano. O estudo do registro médico pormenorizado de um paciente pode indicar tendências e sugerir formas viáveis de tratamento.

Usuários dos jogos de computadores também são expostos à decisões de máquinas rotineiramente. A industria dos jogos foi uma das primeiras a se aproveitar desta tecnologia e muitos jogos lançam mão de IA para tomadas de decisões e para a animação de personagens virtuais que povoam os mundos de fantasia dos jogos.

Telescópio Espacial kepler – representação artística

A inteligência artificial, com sua habilidade de análise de grande volume de dados, é um recurso poderoso na pesquisa científica. Um exemplo brilhante dessa faceta foi a descoberta anunciada pela NASA em março de 2019 de dois exoplanetas por meio de um algoritmo chamado AstroNet-K2, uma rede neural modificada para o estudo dos dados colhidos pelo telescópio espacial Kepler.

IA e Educação

A Educação é um setor tradicionalmente refratário às novas tecnologias e as novidades demoram para entrar na sala de aula. Apesar disso muitas propostas envolvendo IA têm surgido, algumas delas já em aplicação.

O professor hoje compete na atenção dos alunos com uma variedade de estímulos eletrônicos, jogos, páginas da web interativas e ambientes de interação social. Qualquer sistema educacional no presente e futuro próximo deve oferecer estímulo similar ao encontrado nesses meios, buscando dialogar com os alunos no ambiente de interatividade digital com que estão bem familiarizados.

Ainda que os computadores já sejam, há algum tempo, utilizados no manejo da burocracia escolar, sistemas inteligentes podem agilizar esse processo. Professores gastam boa parte de seu tempo em atividades burocráticas, não relacionadas com o ensino em si. A avaliação de exames e o preenchimento de formulários, o acompanhamento de frequências às aulas, o contato permanente com pais e responsáveis e o controle de estoque de material escolar, todas são tarefas que podem ser facilitadas com o uso de IA. O mesmo ocorre com o gerenciamento de matrículas, formulação de calendários e manejo de pessoal. Para o aluno um assistente escolar pode agendar compromissos virtuais ou não, acompanhar a rotina de estudos e contato com professores.

É no campo puramente acadêmico, no entanto, que se pode esperar os melhores resultados. Sistemas inteligentes tem sido treinados para a personalização com ajuste super fino de ementas e fluxos de estudo para o indivíduo, levando em conta suas habilidades e deficiências. Com a adoção de textos eletrônicos a informação antes contida em grandes volumes de papel pode ser, de modo simples e de baixo custo, fragmentada em guias menores de estudos, contendo blocos lógicos completos com referência a recursos multimídia e conteúdo expandido. Sistemas de IA podem fornecer uma interface interativa capaz de responder perguntas ou indicar referências visando esclarecer pontos pouco compreendidos. Uma IA pode auxiliar o professor inclusive por meio de diálogos falados, resolvendo dúvidas e ajudando na solução de exercícios, enquanto a análise de imagens capturadas por câmeras pode indicar o nível de concentração ou dispersão dos estudantes.

Associada ao uso da apresentação dinâmica de conteúdo um sistema treinado por IA e subsidiado por avaliações permanentes e automáticas de desempenho pode indicar o melhor roteiro, as necessárias revisões e ritmo do aprendizado. Tutores automáticos podem acompanhar e sugerir o ritmo de estudo de um estudante. A avaliação permanente, além de tornar desnecessária a temida temporada de provas, avaliará o nível atual de conhecimento do aluno, insistindo em exemplos e exercícios caso um conceito não esteja bem assimilado através da apresentação de questões com níveis crescentes de dificuldade, sugerindo o retorno para níveis mais básicos ou a progressão para tópicos mais avançados. Ele pode identificar lacunas no entendimento e apresentar as intervenções corretas para preencher essas deficiências.

Simultaneamente com a avaliação de testes e exercícios podem ser inseridos mecanismos para a detecção de dificuldades outras que não as puramente acadêmicas, tais como deficiências de visão e concentração para alunos mais jovens.

Evidentemente nenhum sistema, por mais arrojado que seja, poderá se furtar ao estímulo de aspectos humanos básicos tais como a criatividade, cooperação entre indivíduos e desenvolvimento ético. Principalmente nas primeiras fases de implantação dos sistemas inteligentes nas escolas, visto que o efeito completo da exposição a sistemas artificiais não é plenamente conhecido, não se pode permitir uma dependência excessiva nas máquinas. Alunos devem receber estímulo para realizar pesquisas tradicionais em bibliotecas e usar livros físicos. A interação entre alunos e educadores e colegas deve ser uma prioridade. Além disso, em um contexto onde a informação, como acesso aos dados, e a capacidade de processamento destes dados estão super facilitados pela presença de computadores, a criatividade e a habilidade para a solução de problemas, individualmente ou em grupo, deve ser o foco do processo educativo.

Grande parte do desafio das instituições de ensino em face da explosão da IA consiste em treinar profissionais para o uso e desenvolvimento dos próprios sistemas inteligentes. Um usuário não especialista em computação deve adquirir compreensão básica do mecanismo de funcionamento das máquinas, com conhecimento mínimo da arquitetura destes sistemas e da análise estatística utilizada por eles. Caso contrário não saberá discernir que tipo de demandas são adequadas para as IAs nem terá competência para interpretar os resultados destes processos.

O treinamento de especialistas com competência para desenvolver sistemas inteligentes não é desafio menor. A inteligência de máquina e os sistemas de aprendizado dependem profundamente de matemática avançada, em particular a álgebra linear e a estatística, e de programação e lógica. Existe risco evidente do uso das ferramentas mais modernas como caixas pretas onde um programador pode colocar em funcionamento sistemas complexos sem ter um bom domínio da tecnologia envolvida. Além da dependência cultural e tecnológica dos centros desenvolvedores isso cria restrição severa sobre a habilidade para tratar de novos desafios, especialmente aqueles de interesse restrito à comunidade local, tais como problemas dos países em desenvolvimento que não foram devidamente tratados pelos grandes centros de pesquisa.


Expectativa e Desafio Futuro

Inteligência Artificial e Aprendizado de Máquina


O que são e para que servem?

O aperfeiçoamento de tecnologias da informação, não diferente de outras tecnologias, tem causado grande impacto na sociedade humana. Grande parte deste impacto é positiva no sentido de aprimorar a experiência do indivíduo, liberando-o de tarefas mecânicas pesadas ou atividades intelectuais extenuantes. No geral a tecnologia amplia a capacidade humana de transformação da natureza ao mesmo tempo em que facilita a exploração científica que, por sua vez, realimenta o avanço tecnológico. No entanto os mesmos aspectos que podem ser benéficos também podem introduzir desafios. Máquinas, como ferramentas mecânicas, aumentam a eficiência e produtividade de um indivíduo, colateralmente provocando desemprego e concentração de renda. Da mesma forma máquinas eletrônicas que simulam as atividades de cognição e interpretação humanas estão, já há alguns anos, transformando a sociedade e as relações entre indivíduos de modo construtivo, em certa medida. Muitos aspectos desta transformação são claramente nocivos, como a evidente tendência da substituição de trabalhadores por máquinas “inteligentes” ou, por exemplo, a manipulação de opiniões para fins políticos usando o levantamento de perfis psicológicos. No entanto esta é uma tecnologia nova e de crescimento muito rápido e a maior parte do impacto causado por ela continua desconhecido e deve ser considerado com atenção.

A inteligência artificial (IA) começou a ser desenvolvida na década de 1950, em um esforço para automatizar atividades antes empreendidas apenas por humanos. Tomadas de decisão básicas podem ser implementadas por equipamentos simples, tal como um termostato que limita a atividade de um condicionador de ar desligando-o quando uma temperatura mínima é atingida. Processadores, que são o núcleo dos computadores eletrônicos, são formados por grande número de circuitos capazes de implementar testes lógicos básicos descritos na chamada Álgebra de Boole. Com o desenvolvimento da programação, que consiste em uma fila de instruções a serem seguidas pelo computador, tornou-se viável a elaboração de sistemas especialistas. Esses sistemas são compostos por uma longa série de instruções, geralmente com acesso a um repositório de informações (um banco de dados) para a tomada de decisões. Eles podem classificar vinhos, jogar xadrez, resolver problemas matemáticos usando apenas símbolos, entre muitas outras tarefas.

Apesar do sucesso de tais sistemas especialistas existem tarefas de complexidade muito superior à de jogar xadrez ou classificar objetos de um conjunto, mesmo que com milhares de elementos. Uma tarefa como a identificação e localização de objetos em uma imagem, por exemplo, exigiria um conjunto gigantesco de linhas de instruções ou informações em bancos de dados. Para tratar grandes volumes de dados e questões que não admitem soluções por meio de algoritmos fixos, mesmo que complexos, foi desenvolvido o Aprendizado de Máquina Artificial (Machine learning).

 

Ada Lovelace e Charles Babbage

Nas décadas de 1830 e 1840, quando Ada Lovelace e Charles Babbage desenvolveram o Analytical Engine, o primeiro computador mecânico, eles não o consideravam uma máquina a ser utilizada para a solução de problemas genéricos. Pelo contrário, ele foi concebido e utilizado em problemas específicos na área da análise matemática. Nas palavras de Lovelace:

O Analytical Engine não tem pretensões de criar coisa alguma. Ele apenas pode fazer aquilo que conhecemos e sabemos como instruí-lo em sua execução… Sua função é a de nos ajudar com o que já estamos familiarizados.…”

 

Essas conclusões foram analisadas por Alan Turing, o pioneiro da IA, em seu artigo de 1950 “Computing Machinery and Intelligence”, onde são introduzidos os conceitos de teste de Turing e outros que se tornaram fundamentos da IA. Ele concluiu que máquinas eletrônicas poderiam ser capazes de aprendizado e originalidade. Aprendizado de máquina (machine learning) é a resposta positiva para a pergunta: um computador pode ir além das instruções com as quais foi programado e aprender a executar tarefas?

O aprendizado de máquina representa um novo paradigma na programação. Ao invés de armazenar na memória do computador um conjunto de regras fixas a serem usadas na execução de uma tarefa o computador é carregado com algoritmos flexíveis que podem ser modificados por meio de treinamento. O aprendizado consiste em exibir para a máquina um conjunto grande de exemplos anotados (devidamente etiquetados) por um humano ou por outra máquina previamente treinada. Uma vez treinado o mesmo sistema será capaz de identificar corretamente (ou com bom nível de precisão) casos novos além daqueles antes exibidos.

 

Programação clássica x Aprendizado de Máquina

 

Suponha, por exemplo, que queremos identificar em uma pilha de fotos aquelas que contêm imagens de gatos ou cachorros. O código contendo os algoritmos é alimentado com fotos dos animais, cada uma devidamente etiquetada. Uma forma de avaliação de erro da previsão é fornecida juntamente com um algoritmo flexível que pode ser alterado automaticamente de forma a minimizar os erros da avaliação. Por meio da leitura repetida destas imagens o algoritmo é modificado para produzir o menor erro possível de leitura.

 

Treinamento de máquina

 

A este processo chamamos de treinamento. Em terminologia técnica dizemos que ele consiste em alterar os parâmetros do algoritmo de forma a minimizar os erros. Uma vez encontrados estes parâmetros o algoritmo pode ser usado para identificar novas fotos contendo gatos ou cachorros. Ao treinamento feito com o uso de dados etiquetados é denominado supervised learning (aprendizado supervisionado). É também possível submeter à análise do computador um conjunto de dados não identificados com a demanda de que o o algoritmo identifique padrões de forma autônoma e classifique elementos de um conjunto por similaridade desses padrões. No unsupervised learning (aprendizado não supervisionado) é possível que o sistema inteligente distinga padrões que mesmo um humano não seria capaz de perceber.

Machine learning é um método de análise e processamento de dados que automatiza a construção do algoritmo de análise.

O treinamento de máquinas depende da velocidade e capacidade de computadores mas, também, do acesso à informação ou dados. Esse acesso é fornecido pela atual conectividade entre fontes diversas de dados, armazenados de forma estruturada ou não. A habilidade dos computadores de realizar uma análise sobre um volume muito grande desses dados leva ao conceito de Big Data. A operação de busca e coleta desses dados é a atividade de Data Mining (mineração de dados) enquanto a seleção e interpretação desses dados é eficientemente realizada por sistemas inteligentes.

xkcd.com

Embora os primeiros passos na construção de sistemas de aprendizado tenham sido inspirados no funcionamento de cérebros e neurônios humanos (ou animais), as chamadas redes neurais artificiais não são projetadas como modelos realistas da arquitetura ou funcionalidade biológica. A expressão deep learning ou aprendizado profundo se refere apenas às múltiplas camadas usadas para o aprendizado artificial. A plasticidade do cérebro biológico, que é a capacidade de partes do cérebro de se reordenar para cumprir tarefas diferentes daquelas em que estava inicialmente treinado, levantou a hipótese de que algoritmos simples e comuns podem ser especializados para resolver tarefas diversas. Reconhecimento de imagens ou textos, por exemplo, podem ser efetuados por estruturas similares. A neurociência mostrou que a interação de partes simples pode exibir comportamento inteligente e complexo. Considerando as grandes lacunas existentes no entendimento da inteligência biológica, a memória e outras funções dos organismos vivos, é de se esperar que os avanços nessa área da ciência, juntamente com a evolução dos computadores, ainda venha a oferecer guias importantes para o aperfeiçoamento da inteligência artificial.


Onde o Aprendizado de Máquina é usado?

Linguagem de Scripts para Linux


Introdução ao BASH

Para interagir com o seu sistema operacional (no nosso caso o Linux) você pode usar interfaces gráficas ou GUI (graphic user interfaceem>, as famosas janelas) ou rodar um programa que permite que os comandos sejam inseridos um a um através de linhas de comando (command line interfaces, CLIs). Existem vários destes programas (ou Shells) mas trataremos aqui de bash (Bourne Again SHell, uma versão muito usada e já instalada na maioria das distribuições linux.

Muitas vezes, quando você usa um computador, uma tarefa pode ser realizada mais facilmente através de um comando de linha, escrito em um terminal. Por exemplo, suponha que queremos obter em um texto toda a lista de músicas que se encontram no diretório (ou pasta). Uma forma de fazer isto seria a seguinte:

Abra o terminal e digite

$ cd ~/Musica
$ ls > minhas_musicas.txt

(1) Listas de comandos podem ser encontradas em Site: SS64 ou Linux commands.

Faça suas experiências com os comandos de linha com cuidado. Nunca execute um comando cuja ação você desconhece. Embora seja difícil que um uusário sem permissão de surperusuário ou admnistrador faça grandes estragos no sistema, você pode apagar dados ou corromper programas instalados dentro de sua conta. Não se esqueça: faça sempre cópias de segurança!

Nestes exemplos $ é o prompt de comandos, a indicação de que o computador está pronto para receber instruções. O comando cd troca o foco da execução para o diretório ~/Musica, onde ~ é um atalho para representar a pasta do usuário, (home/usr). A segunda linha faz uma listagem de todos os arquivos usando ls (list screen) e, ao invés de exibí-los na tela, (devido ao sinal > ou pipe, redirecionador) envia esta lista para um arquivo de nome minhas_musicas.txt. Poderíamos ainda filtrar a lista usando ls *.mp3, e apenas os arquivos com extensão mp3 seriam listados1.

Observe agora que a Shell permite comandos encadeados ou seja, a entrada de vários comandos de uma só vez. É possível inclusive passar resultados de um comando para outro em um único passo. Para fazer isto escrevemos os comandos na mesma linha e os separamos por um ponto e vírgula (;):

$ cd ~/Musica; ls > minhas_musicas.txt

Essa linha efetuará o mesmo que as duas linhas do exemplo anterior. Para ver o conteúdo no novo arquivo criado digite gedit minhas_musicas.txt ou clique no arquivo de dentro de um gerenciador de arquivos como o Nautilus ou Thunar. Também é possível listar o arquivo de caracteres usando o comando cat minhas_musicas.txt.

Com esta técnica você pode escrever linhas com até 255 caracteres. Se uma sequência de ações for usada com frequência pode ser útil gravá-la em um arquivo, que depois pode ser executado quando necessário. Para isto use um editor qualquer que possa gravar arquivos como uma sequência simples de caracteres ASCII (isto quer dizer: sem comandos de formatação). O Ubuntu (e várias outras distribuições) traz o gedit ou kate (na distribuição KDE). Você também pode usar o geany, bluefish ou outro qualquer de sua escolha.

Uma lista de comandos gravada em um arquivo executável é chamada de script. Para especificar que se trata de um shell script colocamos na primeira linha a seguinte informação:

#!/bin/bash

O sinal de sustenido (#) em uma linha qualquer (exceto na primeira linha) é usado para indicar um comentário, um trecho que não é executado pela shell. Na primeira linha ele possui um significado especial: #! indica que o arquivo a seguir deverá ser executado pela bash shell fornecendo o caminho onde bash está instalado (normalmente em /bin/bash). Outros comandos, a serem executados na ordem em que se encontram, podem ser inseridos em linhas sucessivas. Você ainda pode usar o ponto e vírgula para separar comandos na mesma linha mas, em um script isto não é necessário nem recomendado. É mais fácil ler e compreender scripts onde cada linha contém um único comando.

O seguinte exemplo ilustra o conceito:

#!/bin/bash
# Esta linha é um comentário e não é executada
# Primeiro faça uma lista de músicas (mp3 e wav)
cd ~/Musica
ls *.mp3 > MusicasMp3
ls *.wav > ArquivosWav
# Faça uma lista de livros no formato pdf, na pasta ~/Livros
cd ~/Livros
ls *.pdf > LivrosEmPdf

Evidentemente estas linhas supõem a existências das pastas ~/Musicas e ~/Livros caso contrário uma mensagem de erro será exibida. Comentários são úteis para tornar mais legíveis os scripts, principalmente para o caso de outro usuário (ou você mesmo, no futuro) utilizar o script. Grave seu script com o nome lista_arquivos em uma pasta /.scripts (ou outro nome de sua preferência). Para executar esse script podemos digitar:

# se você já está na pasta scripts:
$ ./lista_arquivos
# se você não está na pasta scripts:
$ ~/.scripts/lista_arquivos

Alternativamente, para que um script seja encontrado a partir de qualquer outro diretório é necessário informar à shell como encontrá-lo. A shell usa uma “variável de ambiente” ( environment variable) chamada PATH para localizar onde estão os arquivos a serem executados. Para compreender isto melhor abra um terminal e digite:

$ echo $PATH
/usr/local/bin:/usr/bin:/bin

A segunda linha é a resposta (ou output) do comando echo, destinado a exibir informações, indicando que neste computador a shell fará uma busca nos diretórios /usr/local/bin, /usr/bin e /bin quando um comando for emitido. Observe que nossa pasta de scripts não está incluída. Para que o arquivo lista_arquivos seja executado podemos acrescentar a pasta onde ele reside ao PATH:

PATH=$PATH:/.scripts
$ echo $PATH
/usr/local/bin:/usr/bin:/bin: /.scripts

(2) Lembrando:

. Atalho para o diretório atual
cd ~ Para diretório home.
cd .. Volta para o dir pai

Observe que PATH contém agora a pasta /.scriptscode>. Esta alteração, no entanto, permanece válida apenas durante a sessão do terminal, ou dentro do arquivo de script de onde ela foi emitida. Veremos depois como alterar de forma permanente esta variável.

O ponto2, na primeira linha não comentada, é uma referência ao diretório atual, neste caso /.scripts. O til na outra linha, como já vimos, é um atalho para a pasta do usuário, o mesmo que /home/nome_do_usuário.

O último passo consiste em tornar este arquivo um executável. Para isto use o comando chmod (change mode):

$ chmod u+x teste1
# agora você pode executar o arquivo teste1
$ ./teste1

Se você procurar nas pastas ~/Musicas e ~/Livros deverá encontrar os arquivos recentemente gravados com o conteúdo esperado! O sistema de permissões do Linux (diretamente copiado do Unix) constitui uma das grandes vantagens deste sistema operacional. Por causa dele um usuário só pode alterar arquivos de sua propriedade enquanto arquivos de sistema só podem ser alterados pelo superuser ou gerente do sistema. Mesmo em seu próprio computador você não faz (ou pelo menos, não deve fazer) login como superuser.

Para alterar o PATH de modo definitivo você pode usar os seguintes procedimentos: primeiro verifique seu caminho atual usando o comando: $ echo $PATH. Para alterar a variável PATH para todos os usuários do sistema edite o arquivo /etc/profile (como root) e modifique a linha que começa com "PATH =".

sudo gedit /etc/profile
# forneça seu password
# procure a definição de PATH e edite para: PATH = "$PATH: $HOME/.scripts

(3) O ponto (.) como primeiro caracter do nome do arquivo .bash_profile ou indica que este é um arquivo oculto, um arquivo que não aparece normalmente em listagens requisitadas com o comando ls nem dentro do gerenciador de arquivos. Para listar arquivos ocultos use ls -a ou pressione Ctrl-H de dentro do Nautilus ou do Thunar (ou outro gerenciador de arquivos). Diretórios também podem ser ocultos da mesma forma.

(4) Segundo o manual online de bash (man pages) o arquivo .bash_profile é executado quando o usuário faz login em uma shell. Se você já está logado e abre uma janela de terminal (por exemplo) o arquivo .bashrc é executado.

Para alterar o PATH de um único usuário edite o arquivo /home/nome_do_usuario/.bash_profile ou .bashrc(Veja nota4). A especificação para o caminho no /etc/.bash_profile, ou /etc/.bashrc, é análoga ao caso anterior. Agora você não precisa usar sudo pois o arquivo é de sua propriedade e você tem permissão para alterá-lo.

gedit /etc/.bash_profile
# forneça seu password
# procure a definição de PATH e edite para: PATH = "$PATH: $HOME/scripts
export PATH

A linha de comando PATH = "$PATH: $HOME/scripts pega o conteúdo da variável de ambiente PATH já definida para todos os usuários no arquivo /etc/profile, e acrescenta a ela o nome do diretório $HOME/scripts (ou qualquer outro que você queira incluir). O comando export na última linha serve para que a variável fique visível fora do script onde ela foi definida.

Recapitulando: Para escrever um arquivo executável você dever informar em seu cabeçalho quem o executará (#!/bin/bash no caso de bash, #!/bin/env python no caso de um script Python, etc.Depois você deverá informar o caminho completo para a sua execução ou colocar o diretório onde seu script está na variável path. Finalmente você deve torná-lo um executável com chmod.

Para saber um pouco mais sobre as permissões5 de um arquivo vamos executar o comando ls -l, onde -l é uma chave (opção) para exibir permissões:

$ ls -l teste1
 -rwxr-xr-- 1
 usuario_1 grupo_1 227 Jun 8 21:22
teste1

(5) Permissões:

r permissão para leitura(read)
w permissão para escrever (write)
x permissão para executar (execute)
- substitui r, w, x se a permissão é negada

As permissões são listadas por meio de 9 caracteres: o primeiro indica o tipo do arquivo (d, se é um diretório, l para links e para arquivos comuns. Em seguido temos 3 grupos formados por 3 letras r, w, x (veja tabela) que se referem ao dono do arquivo (o usuário que o criou), ao grupo a que este usuário pertence, e aos demais usuários do computador ou rede. No exemplo acima teste1 é um arquivo () pertencente à usuario_1, grupo_1. O dono pode ler, escrever (gravar) e executar (rwx), usuários do grupo podem ler e executar (r-x) mas não modificar este arquivo, e os demais usuários podem apenas ler (r–).

Exibindo valores e resultados

Para exibir o valor da variável PATH, uma variável do sistema estabelecida durante o boot, usamos o comando echo $PATH. Também podemos usar echo para exibir mensagens de textos:

$ echo Uma mensagem para seu usuario ...
 Uma mensagem para seu usuario ...
$ echo # pula uma linha
$ echo "Um texto pode ser delimitado por aspas duplas ou simples"
Um texto pode ser delimitado por aspas duplas ou simples
$ echo ou ate mesmo aparecer sem nenhuma aspas ou ate mesmo aparecer sem nenhuma aspas

Observe que não é obrigatório o uso de aspas embora, para alguns usuários, pode ser mais claro ler programas onde as strings estejam delimitadas. Mensagens são úteis em um script, por exemplo, para passar informações sobre o funcionamento ou requisitar a digitação de algum dado.

echo "Os seguintes usuarios estao logados no sistema:"
who

O comando who exibe todos os usuários logados no momento. Para exibir uma informação sem trocar de linha usamos o parâmetro -n.

echo -n "Hoje e: " 	date
# O script acima exibe algo como: 	Hoje e: Wed Jun 22 11:29:23 BRT 2011