pl.col()
, aqui você verá apenas col()
from polars import *
from datetime import date
import json
Nada muito longe do que já é feito no Pandas ou no próprio R. Inclusive, usa exatamente a mesma síntaxe que a do Pandas :)
dados = read_csv(file = '../02-data/titanic.csv')
dados
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
1 | 0 | "Allison, Mr. H... | "male" | 30.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | 135 | "Montreal, PQ /... |
1 | 0 | "Allison, Mrs. ... | "female" | 25.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
1 | 1 | "Anderson, Mr. ... | "male" | 48.0 | 0 | 0 | "19952" | 26.55 | "E12" | "S" | "3" | null | "New York, NY" |
1 | 1 | "Andrews, Miss.... | "female" | 63.0 | 1 | 0 | "13502" | 77.9583 | "D7" | "S" | "10" | null | "Hudson, NY" |
1 | 0 | "Andrews, Mr. T... | "male" | 39.0 | 0 | 0 | "112050" | 0.0 | "A36" | "S" | null | null | "Belfast, NI" |
1 | 1 | "Appleton, Mrs.... | "female" | 53.0 | 2 | 0 | "11769" | 51.4792 | "C101" | "S" | "D" | null | "Bayside, Queen... |
1 | 0 | "Artagaveytia, ... | "male" | 71.0 | 0 | 0 | "PC 17609" | 49.5042 | null | "C" | null | 22 | "Montevideo, Ur... |
1 | 0 | "Astor, Col. Jo... | "male" | 47.0 | 1 | 0 | "PC 17757" | 227.525 | "C62 C64" | "C" | null | 124 | "New York, NY" |
1 | 1 | "Astor, Mrs. Jo... | "female" | 18.0 | 1 | 0 | "PC 17757" | 227.525 | "C62 C64" | "C" | "4" | null | "New York, NY" |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3 | 0 | "Wiseman, Mr. P... | "male" | null | 0 | 0 | "A/4. 34244" | 7.25 | null | "S" | null | null | null |
3 | 0 | "Wittevrongel, ... | "male" | 36.0 | 0 | 0 | "345771" | 9.5 | null | "S" | null | null | null |
3 | 0 | "Yasbeck, Mr. A... | "male" | 27.0 | 1 | 0 | "2659" | 14.4542 | null | "C" | "C" | null | null |
3 | 1 | "Yasbeck, Mrs. ... | "female" | 15.0 | 1 | 0 | "2659" | 14.4542 | null | "C" | null | null | null |
3 | 0 | "Youseff, Mr. G... | "male" | 45.5 | 0 | 0 | "2628" | 7.225 | null | "C" | null | 312 | null |
3 | 0 | "Yousif, Mr. Wa... | "male" | null | 0 | 0 | "2647" | 7.225 | null | "C" | null | null | null |
3 | 0 | "Yousseff, Mr. ... | "male" | null | 0 | 0 | "2627" | 14.4583 | null | "C" | null | null | null |
3 | 0 | "Zabour, Miss. ... | "female" | 14.5 | 1 | 0 | "2665" | 14.4542 | null | "C" | null | 328 | null |
3 | 0 | "Zabour, Miss. ... | "female" | null | 1 | 0 | "2665" | 14.4542 | null | "C" | null | null | null |
3 | 0 | "Zakarian, Mr. ... | "male" | 26.5 | 0 | 0 | "2656" | 7.225 | null | "C" | null | 304 | null |
3 | 0 | "Zakarian, Mr. ... | "male" | 27.0 | 0 | 0 | "2670" | 7.225 | null | "C" | null | null | null |
3 | 0 | "Zimmerman, Mr.... | "male" | 29.0 | 0 | 0 | "315082" | 7.875 | null | "S" | null | null | null |
dados = read_excel(file = '../02-data/titanic.xlsx')
dados.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
Esse tópico também não foge muito se você já trabalhou com o Pandas. Apenas a inclusão de um novo método do dataframe que é bem parecido com o que têm no Pyspark: o .limit()
dados.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados.limit(3) #---- alô usuários do PySpark
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados.tail(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
3 | 0 | "Zakarian, Mr. ... | "male" | 26.5 | 0 | 0 | "2656" | 7.225 | null | "C" | null | 304 | null |
3 | 0 | "Zakarian, Mr. ... | "male" | 27.0 | 0 | 0 | "2670" | 7.225 | null | "C" | null | null | null |
3 | 0 | "Zimmerman, Mr.... | "male" | 29.0 | 0 | 0 | "315082" | 7.875 | null | "S" | null | null | null |
dados.shape
(1309, 14)
dados.describe()
describe | pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | f64 | f64 | str | str | f64 | f64 | f64 | str | f64 | str | str | str | f64 | str |
"count" | 1309.0 | 1309.0 | "1309" | "1309" | 1309.0 | 1309.0 | 1309.0 | "1309" | 1309.0 | "1309" | "1309" | "1309" | 1309.0 | "1309" |
"null_count" | 0.0 | 0.0 | "0" | "0" | 263.0 | 0.0 | 0.0 | "0" | 1.0 | "1014" | "2" | "823" | 1188.0 | "564" |
"mean" | 2.294882 | 0.381971 | null | null | 29.881138 | 0.498854 | 0.385027 | null | 33.295479 | null | null | null | 160.809917 | null |
"std" | 0.837836 | 0.486055 | null | null | 14.413493 | 1.041658 | 0.86556 | null | 51.758668 | null | null | null | 97.696922 | null |
"min" | 1.0 | 0.0 | "Abbing, Mr. An... | "female" | 0.17 | 0.0 | 0.0 | "110152" | 0.0 | "A10" | "C" | "1" | 1.0 | "?Havana, Cuba" |
"max" | 3.0 | 1.0 | "van Melkebeke,... | "male" | 80.0 | 8.0 | 9.0 | "WE/P 5735" | 512.3292 | "T" | "S" | "D" | 328.0 | "Zurich, Switze... |
"median" | 3.0 | 0.0 | null | null | 28.0 | 0.0 | 0.0 | null | 14.4542 | null | null | null | 155.0 | null |
Aqui temos algumas particularidades do Polars, que também são bem parecidas com o que já existe no Pandas e no PySpark.
dados\
.select(col('age'))\
.head(3)
age |
---|
f64 |
29.0 |
0.92 |
2.0 |
dados\
.select(col(['pclass', 'survived', 'name']))\
.head(3)
pclass | survived | name |
---|---|---|
i64 | i64 | str |
1 | 1 | "Allen, Miss. E... |
1 | 1 | "Allison, Maste... |
1 | 0 | "Allison, Miss.... |
dados[:, 0]\
.head(3)
pclass |
---|
i64 |
1 |
1 |
1 |
dados[:, 0:3]\
.head(3)
pclass | survived | name |
---|---|---|
i64 | i64 | str |
1 | 1 | "Allen, Miss. E... |
1 | 1 | "Allison, Maste... |
1 | 0 | "Allison, Miss.... |
dados\
.select(\
col('*')
)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados\
.select(\
exclude('PassengerId')
)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados\
.select(\
exclude(['PassengerId', 'Survived'])
)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
Existem duas funções específicas para este fim: a with_column
e a with_columns
.
Qual a melhor usar? Vai na with_columns
, pois ela permite criar várias colunas de uma vez só, bem parecido com o que dplyr::mutate()
faz no R.
dados\
.with_columns(\
(col('age') * col('parch')).alias('nova_coluna'),
)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | nova_coluna |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | f64 |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" | 0.0 |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... | 1.84 |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... | 4.0 |
#---- Método 1:
dados\
.with_columns(\
nova_coluna = col('age') * col('parch'),
nova_coluna1 = col('age') * col('sibsp'),
nova_coluna2 = col('age') * col('fare')
)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | nova_coluna | nova_coluna1 | nova_coluna2 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | f64 | f64 | f64 |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" | 0.0 | 0.0 | 6128.7875 |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... | 1.84 | 0.92 | 139.426 |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... | 4.0 | 2.0 | 303.1 |
#---- Método 2:
dados\
.with_columns([\
(col('age') * col('parch')).alias('nova_coluna'),
(col('age') * col('sibsp')).cast(Int64).alias('nova_coluna1'),
(col('age') * col('fare')).cast(Float32).fill_nan(0).alias('nova_coluna2')
])\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | nova_coluna | nova_coluna1 | nova_coluna2 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | f64 | i64 | f32 |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" | 0.0 | 0 | 6128.787598 |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... | 1.84 | 0 | 139.425995 |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... | 4.0 | 2 | 303.100006 |
nova_coluna_constante
: Coluna com um valor úniconova_coluna_soma
: Coluna com um valor único, que é a soma da variável Agenova_coluna_42
: Coluna com um valor somado (42) constante para a variável Agenova_coluna_case_when
: Coluna com uma ideia de case whennova_coluna_lista
: Coluna que concatena todas as variáveis em uma listadados\
.select(col('age'))\
.with_columns(\
nova_coluna_constante = lit(1),
nova_coluna_soma = col('age').sum(),
nova_coluna_42 = col('age') + 42,
nova_coluna_case_when = when(col('age') > 10).then(lit('novo')).otherwise(lit('idoso')) # ^.^
)\
.with_columns(\
nova_coluna_lista = concat_list(all()) # Problema aqui: tive que adicionar um novo with_columns, pois ele estava criando a lista com um único elemento, que era da coluna PassengerId quando adicionava no primeiro
)\
.head(3)
age | nova_coluna_constante | nova_coluna_soma | nova_coluna_42 | nova_coluna_case_when | nova_coluna_lista |
---|---|---|---|---|---|
f64 | i32 | f64 | f64 | str | list[str] |
29.0 | 1 | 31255.67 | 71.0 | "novo" | ["29.0", "1", ... "novo"] |
0.92 | 1 | 31255.67 | 42.92 | "idoso" | ["0.92", "1", ... "idoso"] |
2.0 | 1 | 31255.67 | 44.0 | "idoso" | ["2.0", "1", ... "idoso"] |
Segue a mesma ideia do rename do Pandas. Irei trocar age -> Age.
dados\
.rename(mapping = {'age': 'Age'})\
.columns
['pclass', 'survived', 'name', 'sex', 'Age', 'sibsp', 'parch', 'ticket', 'fare', 'cabin', 'embarked', 'boat', 'body', 'home.dest']
Obviamente, alguns filtros podem ser aplicados tanto à variáveis quantitativas quanto qualitativas!
dados\
.filter(col('age') > 10)\
.head(3)
# age maior que 10
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 0 | "Allison, Mr. H... | "male" | 30.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | 135 | "Montreal, PQ /... |
1 | 0 | "Allison, Mrs. ... | "female" | 25.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados\
.filter(col('age') <= 10)\
.head(3)
# age menor ou igual a 10
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
1 | 1 | "Dodge, Master.... | "male" | 4.0 | 0 | 2 | "33638" | 81.8583 | "A34" | "S" | "5" | null | "San Francisco,... |
dados\
.filter(col('age').is_between(10, 20))\
.head(3)
# age entre 10 e 20
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Astor, Mrs. Jo... | "female" | 18.0 | 1 | 0 | "PC 17757" | 227.525 | "C62 C64" | "C" | "4" | null | "New York, NY" |
1 | 1 | "Bishop, Mrs. D... | "female" | 19.0 | 1 | 0 | "11967" | 91.0792 | "B49" | "C" | "7" | null | "Dowagiac, MI" |
1 | 0 | "Carrau, Mr. Jo... | "male" | 17.0 | 0 | 0 | "113059" | 47.1 | null | "S" | null | null | "Montevideo, Ur... |
dados\
.filter((col('age') > 10) & (col('survived') == 1))\
.head(3)
# ge maior que 10 E survided igual a 1
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Anderson, Mr. ... | "male" | 48.0 | 0 | 0 | "19952" | 26.55 | "E12" | "S" | "3" | null | "New York, NY" |
1 | 1 | "Andrews, Miss.... | "female" | 63.0 | 1 | 0 | "13502" | 77.9583 | "D7" | "S" | "10" | null | "Hudson, NY" |
dados\
.filter((col('age') > 10) | (col('survived') == 1))\
.head(3)
# age maior que 10 OU survided igual a 1
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Mr. H... | "male" | 30.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | 135 | "Montreal, PQ /... |
dados\
.filter(col('embarked') == 'S')\
.head(3)
# age maior que 10 OU survided igual a 1
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados\
.filter(col('embarked') != 'S')\
.head(3)
# Embarked diferente de 'S'
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 0 | "Artagaveytia, ... | "male" | 71.0 | 0 | 0 | "PC 17609" | 49.5042 | null | "C" | null | 22 | "Montevideo, Ur... |
1 | 0 | "Astor, Col. Jo... | "male" | 47.0 | 1 | 0 | "PC 17757" | 227.525 | "C62 C64" | "C" | null | 124 | "New York, NY" |
1 | 1 | "Astor, Mrs. Jo... | "female" | 18.0 | 1 | 0 | "PC 17757" | 227.525 | "C62 C64" | "C" | "4" | null | "New York, NY" |
dados\
.filter(col('embarked').is_in(['C', 'S']))\
.head(3)
# Embarked está na lista C ou S
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
#---- Contém uma palavra
dados\
.filter(col('name').str.contains('Mr'))\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 0 | "Allison, Mr. H... | "male" | 30.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | 135 | "Montreal, PQ /... |
1 | 0 | "Allison, Mrs. ... | "female" | 25.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
1 | 1 | "Anderson, Mr. ... | "male" | 48.0 | 0 | 0 | "19952" | 26.55 | "E12" | "S" | "3" | null | "New York, NY" |
#---- Começa com uma palavra
dados\
.filter(col('name').str.starts_with('Allison'))
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
1 | 0 | "Allison, Mr. H... | "male" | 30.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | 135 | "Montreal, PQ /... |
1 | 0 | "Allison, Mrs. ... | "female" | 25.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
#---- Finaliza com uma palavra
dados\
.filter(col('name').str.ends_with('Walton'))
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
dates = DataFrame(date_range(date(2022, 1, 1), date(2022, 3, 1), '1d', name = 'drange'))
dates.head()
drange |
---|
date |
2022-01-01 |
2022-01-02 |
2022-01-03 |
2022-01-04 |
2022-01-05 |
dates\
.filter(col('drange') <= datetime(2022, 1, 2))
# drange menor ou igual à 02/01/2022
drange |
---|
date |
2022-01-01 |
2022-01-02 |
dates\
.filter(col('drange').is_between(datetime(2022, 1, 1), datetime(2022, 1, 3)))
# drange entre 01/01/2022 e 03/01/2022
drange |
---|
date |
2022-01-01 |
2022-01-02 |
2022-01-03 |
Aqui temos algumas particularidades do Polars. Pelo Polars ser relativamente novo, em comparação com um Pandas da vida, por exemplo, algumas das funções não são criadas tão facilmente (e.g. value_counts()
ou dplyr::count()
).
#---- value counts da variável survived
dados\
.select([\
col('survived').value_counts()
])\
.to_series().struct.unnest()
survived | counts |
---|---|
i64 | u32 |
0 | 809 |
1 | 500 |
#---- agregação de uma única variável: máximo da var age
dados\
.select(col('age'))\
.max()
age |
---|
f64 |
80.0 |
#---- Para acessar a primeira linha e trazer o valor
dados\
.select(col('age'))\
.max()\
.row(0)[0]
80.0
Lembrem de usar colchete "[]", caso queiram adicionar mais de uma função de agregação. Atualmente, temos as seguintes funções de agregação, segundo a documentação do Polars.
dados\
.groupby('survived')\
.agg([\
col('age').count().alias('n'), # tanto faz a variável, common kids
col('age').mean().alias('mean_age'),
col('age').median().alias('median_age'),
col('age').max().alias('max_age'),
col('age').min().alias('min_age'),
col('fare').sum().cast(Int64).alias('sum_fare'), # trocando o tipo da variável
col('fare').quantile(0.1).alias('q10_fare'),
col('fare').quantile(0.9).alias('q90_fare'),
col('name').first().alias('first_name'),
col('name').last().alias('last_name'),
col('name').n_unique().alias('unique_names')
])
# nunique
survived | n | mean_age | median_age | max_age | min_age | sum_fare | q10_fare | q90_fare | first_name | last_name | unique_names |
---|---|---|---|---|---|---|---|---|---|---|---|
i64 | u32 | f64 | f64 | f64 | f64 | i64 | f64 | f64 | str | str | u32 |
0 | 809 | 30.545363 | 28.0 | 74.0 | 0.33 | 18869 | 7.25 | 50.4958 | "Allison, Miss.... | "Zimmerman, Mr.... | 808 |
1 | 500 | 28.918244 | 28.0 | 80.0 | 0.17 | 24680 | 7.75 | 120.0 | "Allen, Miss. E... | "Yasbeck, Mrs. ... | 500 |
Vamos considerar que você quer fazer saber somente a média da idade de quem sobreviveu ou não para os passageiros das classes 1 e 2. Podemos fazer o seguinte códiguin:
PS: Esse tipo de agregação é possível no PySpark, SQL e R, mas no Pandas não :/
dados\
.groupby('survived', maintain_order = True)\
.agg([\
col('age').filter(col('pclass').is_in([1, 2])).mean().alias('idade_media_classe_1_2'),
col('age').filter(col('pclass').is_in([2, 3])).mean().alias('idade_media_classe_2_3')
])
survived | idade_media_classe_1_2 | idade_media_classe_2_3 |
---|---|---|
i64 | f64 | f64 |
1 | 32.181318 | 23.094187 |
0 | 37.337349 | 28.009845 |
#---- Mesma coisa que o código acima
dados\
.filter(col('pclass').is_in([1, 2]))\
.groupby('survived', maintain_order = True)\
.agg(\
col('age').mean().alias('mean_age')
)
survived | mean_age |
---|---|
i64 | f64 |
1 | 32.181318 |
0 | 37.337349 |
Aos mais chegados, é a mesma lógica de:
#---- Ordem crescente da variável age
dados\
.sort(by = 'age', reverse = False)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 0 | "Baumann, Mr. J... | "male" | null | 0 | 0 | "PC 17318" | 25.925 | null | "S" | null | null | "New York, NY" |
1 | 1 | "Bradley, Mr. G... | "male" | null | 0 | 0 | "111427" | 26.55 | null | "S" | "9" | null | "Los Angeles, C... |
1 | 0 | "Brewe, Dr. Art... | "male" | null | 0 | 0 | "112379" | 39.6 | null | "C" | null | null | "Philadelphia, ... |
#---- Ordem decrescente da variável age
dados\
.sort(by = 'age', reverse = True)\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Barkworth, Mr.... | "male" | 80.0 | 0 | 0 | "27042" | 30.0 | "A23" | "S" | "B" | null | "Hessle, Yorks" |
1 | 1 | "Cavendish, Mrs... | "female" | 76.0 | 1 | 0 | "19877" | 78.85 | "C46" | "S" | "6" | null | "Little Onn Hal... |
3 | 0 | "Svensson, Mr. ... | "male" | 74.0 | 0 | 0 | "347060" | 7.775 | null | "S" | null | null | null |
#---- Ordem decrescente da variável age e crescente da variável fare
dados\
.sort(by = ['age', 'fare'], reverse = [True, False])\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Barkworth, Mr.... | "male" | 80.0 | 0 | 0 | "27042" | 30.0 | "A23" | "S" | "B" | null | "Hessle, Yorks" |
1 | 1 | "Cavendish, Mrs... | "female" | 76.0 | 1 | 0 | "19877" | 78.85 | "C46" | "S" | "6" | null | "Little Onn Hal... |
3 | 0 | "Svensson, Mr. ... | "male" | 74.0 | 0 | 0 | "347060" | 7.775 | null | "S" | null | null | null |
Funciona bem parecido com o drop_duplicates
do Pandas.
dados\
.unique(subset = 'embarked', keep = 'first')
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 0 | "Artagaveytia, ... | "male" | 71.0 | 0 | 0 | "PC 17609" | 49.5042 | null | "C" | null | 22 | "Montevideo, Ur... |
1 | 1 | "Icard, Miss. A... | "female" | 38.0 | 0 | 0 | "113572" | 80.0 | "B28" | null | "6" | null | null |
1 | 0 | "Minahan, Dr. W... | "male" | 44.0 | 2 | 0 | "19928" | 90.0 | "C78" | "Q" | null | 230 | "Fond du Lac, W... |
dados\
.unique(subset = ['embarked', 'pclass'], maintain_order = True, keep = 'last')
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Minahan, Mrs. ... | "female" | 37.0 | 1 | 0 | "19928" | 90.0 | "C78" | "Q" | "14" | null | "Fond du Lac, W... |
1 | 1 | "Stone, Mrs. Ge... | "female" | 62.0 | 0 | 0 | "113572" | 80.0 | "B28" | null | "6" | null | "Cincinatti, OH... |
1 | 0 | "Wright, Mr. Ge... | "male" | 62.0 | 0 | 0 | "113807" | 26.55 | null | "S" | null | null | "Halifax, NS" |
1 | 1 | "Young, Miss. M... | "female" | 36.0 | 0 | 0 | "PC 17760" | 135.6333 | "C32" | "C" | "8" | null | "New York, NY /... |
2 | 1 | "Slayter, Miss.... | "female" | 30.0 | 0 | 0 | "234818" | 12.35 | null | "Q" | "13" | null | "Halifax, NS" |
2 | 0 | "Stanton, Mr. S... | "male" | 41.0 | 0 | 0 | "237734" | 15.0458 | null | "C" | null | null | "New York, NY" |
2 | 0 | "Yrois, Miss. H... | "female" | 24.0 | 0 | 0 | "248747" | 13.0 | null | "S" | null | null | "Paris" |
3 | 0 | "Tobin, Mr. Rog... | "male" | null | 0 | 0 | "383121" | 7.75 | "F38" | "Q" | null | null | null |
3 | 0 | "Zakarian, Mr. ... | "male" | 27.0 | 0 | 0 | "2670" | 7.225 | null | "C" | null | null | null |
3 | 0 | "Zimmerman, Mr.... | "male" | 29.0 | 0 | 0 | "315082" | 7.875 | null | "S" | null | null | null |
dados.null_count()
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 | u32 |
0 | 0 | 0 | 0 | 263 | 0 | 0 | 0 | 1 | 1014 | 2 | 823 | 1188 | 564 |
#---- Selecionando as linhas nulas da variável age
dados\
.filter(col('age').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 0 | "Baumann, Mr. J... | "male" | null | 0 | 0 | "PC 17318" | 25.925 | null | "S" | null | null | "New York, NY" |
1 | 1 | "Bradley, Mr. G... | "male" | null | 0 | 0 | "111427" | 26.55 | null | "S" | "9" | null | "Los Angeles, C... |
1 | 0 | "Brewe, Dr. Art... | "male" | null | 0 | 0 | "112379" | 39.6 | null | "C" | null | null | "Philadelphia, ... |
#---- Excluindo as linhas nulas da variável age
dados\
.filter(~ col('age').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... |
dados\
.with_columns(\
(col('age').fill_null(lit(0))).alias('age_fill_null')
)\
.filter(col('age').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | age_fill_null |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | f64 |
1 | 0 | "Baumann, Mr. J... | "male" | null | 0 | 0 | "PC 17318" | 25.925 | null | "S" | null | null | "New York, NY" | 0.0 |
1 | 1 | "Bradley, Mr. G... | "male" | null | 0 | 0 | "111427" | 26.55 | null | "S" | "9" | null | "Los Angeles, C... | 0.0 |
1 | 0 | "Brewe, Dr. Art... | "male" | null | 0 | 0 | "112379" | 39.6 | null | "C" | null | null | "Philadelphia, ... | 0.0 |
dados\
.with_columns(\
(col('age').fill_null(col('age').median())).alias('age_fill_null')
)\
.filter(col('age').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | age_fill_null |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | f64 |
1 | 0 | "Baumann, Mr. J... | "male" | null | 0 | 0 | "PC 17318" | 25.925 | null | "S" | null | null | "New York, NY" | 28.0 |
1 | 1 | "Bradley, Mr. G... | "male" | null | 0 | 0 | "111427" | 26.55 | null | "S" | "9" | null | "Los Angeles, C... | 28.0 |
1 | 0 | "Brewe, Dr. Art... | "male" | null | 0 | 0 | "112379" | 39.6 | null | "C" | null | null | "Philadelphia, ... | 28.0 |
dados\
.with_columns(\
(col('age').interpolate()).alias('age_fill_null')
)\
.filter(col('age').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | age_fill_null |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | f64 |
1 | 0 | "Baumann, Mr. J... | "male" | null | 0 | 0 | "PC 17318" | 25.925 | null | "S" | null | null | "New York, NY" | 52.0 |
1 | 1 | "Bradley, Mr. G... | "male" | null | 0 | 0 | "111427" | 26.55 | null | "S" | "9" | null | "Los Angeles, C... | 31.5 |
1 | 0 | "Brewe, Dr. Art... | "male" | null | 0 | 0 | "112379" | 39.6 | null | "C" | null | null | "Philadelphia, ... | 46.0 |
dados\
.with_columns(\
(col('embarked').fill_null(col('embarked').mode())).alias('embarked_fill_null')
)\
.filter(col('embarked').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | embarked_fill_null |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | str |
1 | 1 | "Icard, Miss. A... | "female" | 38.0 | 0 | 0 | "113572" | 80.0 | "B28" | null | "6" | null | null | "S" |
1 | 1 | "Stone, Mrs. Ge... | "female" | 62.0 | 0 | 0 | "113572" | 80.0 | "B28" | null | "6" | null | "Cincinatti, OH... | "S" |
#---- A variável body tem basicamente só nulos
dados\
.drop_nulls()
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str |
dados\
.with_columns(\
col('age').is_null().alias('flag_age_null')
)\
.filter(col('age').is_null())\
.head(3)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | flag_age_null |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | bool |
1 | 0 | "Baumann, Mr. J... | "male" | null | 0 | 0 | "PC 17318" | 25.925 | null | "S" | null | null | "New York, NY" | true |
1 | 1 | "Bradley, Mr. G... | "male" | null | 0 | 0 | "111427" | 26.55 | null | "S" | "9" | null | "Los Angeles, C... | true |
1 | 0 | "Brewe, Dr. Art... | "male" | null | 0 | 0 | "112379" | 39.6 | null | "C" | null | null | "Philadelphia, ... | true |
Atualmente, segundo a documentação, existem os seguintes joins:
Exemplo copiado descaradamente da documentação, por motivos de não ter um dataset para fazer joins. No fim, basta trocar qual tipo de join você deseja utilizar, dentro da própria função.
df_customers = DataFrame(
{
"customer_id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
}
)
df_customers
customer_id | name |
---|---|
i64 | str |
1 | "Alice" |
2 | "Bob" |
3 | "Charlie" |
df_orders = DataFrame(
{
"order_id": ["a", "b", "c"],
"customer_id": [1, 2, 2],
"amount": [100, 200, 300],
}
)
df_orders
order_id | customer_id | amount |
---|---|---|
str | i64 | i64 |
"a" | 1 | 100 |
"b" | 2 | 200 |
"c" | 2 | 300 |
df_customers\
.join(df_orders, on = 'customer_id', how = 'inner') # mudar o parâmetro do how para o join desejado
customer_id | name | order_id | amount |
---|---|---|---|
i64 | str | str | i64 |
1 | "Alice" | "a" | 100 |
2 | "Bob" | "b" | 200 |
2 | "Bob" | "c" | 300 |
Também temos no Polars essas funções, vou criar um exemplo somente com o pivot, pois os dados estão num formato que faz sentido.
Você pode encontrar um exemplo do melt na documentação ou nesse exemplo do Stack Overflow.
dados\
.select([col('name'), col('cabin'), col('age')])\
.pivot(values = 'age', index = 'cabin', columns = 'name')\
.head(3)
cabin | Allen, Miss. Elisabeth Walton | Allison, Master. Hudson Trevor | Allison, Miss. Helen Loraine | Allison, Mr. Hudson Joshua Creighton | Allison, Mrs. Hudson J C (Bessie Waldo Daniels) | Anderson, Mr. Harry | Andrews, Miss. Kornelia Theodosia | Andrews, Mr. Thomas Jr | Appleton, Mrs. Edward Dale (Charlotte Lamson) | Artagaveytia, Mr. Ramon | Astor, Col. John Jacob | Astor, Mrs. John Jacob (Madeleine Talmadge Force) | Aubart, Mme. Leontine Pauline | Barber, Miss. Ellen "Nellie" | Barkworth, Mr. Algernon Henry Wilson | Baumann, Mr. John D | Baxter, Mr. Quigg Edmond | Baxter, Mrs. James (Helene DeLaudeniere Chaput) | Bazzani, Miss. Albina | Beattie, Mr. Thomson | Beckwith, Mr. Richard Leonard | Beckwith, Mrs. Richard Leonard (Sallie Monypeny) | Behr, Mr. Karl Howell | Bidois, Miss. Rosalie | Bird, Miss. Ellen | Birnbaum, Mr. Jakob | Bishop, Mr. Dickinson H | Bishop, Mrs. Dickinson H (Helen Walton) | Bissette, Miss. Amelia | Bjornstrom-Steffansson, Mr. Mauritz Hakan | Blackwell, Mr. Stephen Weart | Blank, Mr. Henry | Bonnell, Miss. Caroline | Bonnell, Miss. Elizabeth | Borebank, Mr. John James | Bowen, Miss. Grace Scott | ... | Vander Cruyssen, Mr. Victor | Vander Planke, Miss. Augusta Maria | Vander Planke, Mr. Julius | Vander Planke, Mr. Leo Edmondus | Vander Planke, Mrs. Julius (Emelia Maria Vandemoortele) | Vartanian, Mr. David | Vendel, Mr. Olof Edvin | Vestrom, Miss. Hulda Amanda Adolfina | Vovk, Mr. Janko | Waelens, Mr. Achille | Ware, Mr. Frederick | Warren, Mr. Charles William | Webber, Mr. James | Wenzel, Mr. Linhart | Whabee, Mrs. George Joseph (Shawneene Abi-Saab) | Widegren, Mr. Carl/Charles Peter | Wiklund, Mr. Jakob Alfred | Wiklund, Mr. Karl Johan | Wilkes, Mrs. James (Ellen Needs) | Willer, Mr. Aaron ("Abi Weller") | Willey, Mr. Edward | Williams, Mr. Howard Hugh "Harry" | Williams, Mr. Leslie | Windelov, Mr. Einar | Wirz, Mr. Albert | Wiseman, Mr. Phillippe | Wittevrongel, Mr. Camille | Yasbeck, Mr. Antoni | Yasbeck, Mrs. Antoni (Selini Alexander) | Youseff, Mr. Gerious | Yousif, Mr. Wazli | Yousseff, Mr. Gerious | Zabour, Miss. Hileni | Zabour, Miss. Thamine | Zakarian, Mr. Mapriededer | Zakarian, Mr. Ortin | Zimmerman, Mr. Leo |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | ... | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 |
"B5" | 29.0 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | ... | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null |
"C22 C26" | null | 0.92 | 2.0 | 30.0 | 25.0 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | ... | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null |
"E12" | null | null | null | null | null | 48.0 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | ... | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null |
dates.head(3)
drange |
---|
date |
2022-01-01 |
2022-01-02 |
2022-01-03 |
dates\
.select(col('drange'))\
.max()
drange |
---|
date |
2022-03-01 |
dates\
.with_columns([\
(col('drange').cast(str)).alias('drange_string')
])\
.with_columns((col('drange_string').str.strptime(Date, fmt = '%Y-%m-%d', strict = False).cast(Date)).alias('drange_new'))\
.head(3)
drange | drange_string | drange_new |
---|---|---|
date | str | date |
2022-01-01 | "2022-01-01" | 2022-01-01 |
2022-01-02 | "2022-01-02" | 2022-01-02 |
2022-01-03 | "2022-01-03" | 2022-01-03 |
dates\
.with_columns([\
col('drange').dt.day().alias('day'),
col('drange').dt.month().alias('month'),
col('drange').dt.year().alias('year'),
col('drange').dt.quarter().alias('quarter'),
col('drange').dt.week().alias('week'),
col('drange').dt.weekday().alias('weekday')
])\
.tail(3)
drange | day | month | year | quarter | week | weekday |
---|---|---|---|---|---|---|
date | u32 | u32 | i32 | u32 | u32 | u32 |
2022-02-27 | 27 | 2 | 2022 | 1 | 8 | 7 |
2022-02-28 | 28 | 2 | 2022 | 1 | 9 | 1 |
2022-03-01 | 1 | 3 | 2022 | 1 | 9 | 2 |
Não foge muito do que temos no Pandas também.
dados.write_csv('../02-data/output_csv.csv')
Ainda não existe uma função nativa para exportar para o formato xlsx. Então, o melhor jeito é transformar o dataframe Polars para Pandas e só aí fazer a exportação.
dados.to_pandas().to_excel('../02-data/output_xlsx.xlsx')
dados.write_parquet('../02-data/output_parquet.parquet')
Vamos criar uma função personalizada que traz somente a primeira palavra da variável name. Assim como no Pandas
, o nosso combo .apply()
+ lambda(x)
irá nos salvar, só que de um jeito diferente.
def get_first_word(x):
result = x.split(',', 1)[0]
return result
dados\
.with_columns([\
(col('name').apply(lambda x: get_first_word(x))).alias('first_name'),
(col('home.dest').apply(lambda x: get_first_word(x))).alias('first_word_home.dest')
])\
.head(5)
pclass | survived | name | sex | age | sibsp | parch | ticket | fare | cabin | embarked | boat | body | home.dest | first_name | first_word_home.dest |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | i64 | str | str | f64 | i64 | i64 | str | f64 | str | str | str | i64 | str | str | str |
1 | 1 | "Allen, Miss. E... | "female" | 29.0 | 0 | 0 | "24160" | 211.3375 | "B5" | "S" | "2" | null | "St Louis, MO" | "Allen" | "St Louis" |
1 | 1 | "Allison, Maste... | "male" | 0.92 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | "11" | null | "Montreal, PQ /... | "Allison" | "Montreal" |
1 | 0 | "Allison, Miss.... | "female" | 2.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... | "Allison" | "Montreal" |
1 | 0 | "Allison, Mr. H... | "male" | 30.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | 135 | "Montreal, PQ /... | "Allison" | "Montreal" |
1 | 0 | "Allison, Mrs. ... | "female" | 25.0 | 1 | 2 | "113781" | 151.55 | "C22 C26" | "S" | null | null | "Montreal, PQ /... | "Allison" | "Montreal" |
Exemplo retirado descaradamente do Stack Overflow. E também ainda não existe uma função parecida com a json_normalize
do Pandas 😢
json_list = [
"""{"name": "Maria",
"position": "developer",
"office": "Seattle"}""",
"""{"name": "Josh",
"position": "analyst",
"termination_date": "2020-01-01"}""",
"""{"name": "Jorge",
"position": "architect",
"office": "",
"manager_st_dt": "2020-01-01"}""",
]
df = DataFrame(
{
'tags': json_list,
}
).with_row_count('id', 1)
#---- Abrindo o JSON em cada uma das chaves
df.with_columns([
col('tags').str.json_path_match(r'$.name').alias('name'),
col('tags').str.json_path_match(r'$.office').alias('location'),
col('tags').str.json_path_match(r'$.manager_st_dt').alias('manager_start_date'),
])
id | tags | name | location | manager_start_date |
---|---|---|---|---|
u32 | str | str | str | str |
1 | "{"name": "Mari... | "Maria" | "Seattle" | null |
2 | "{"name": "Josh... | "Josh" | null | null |
3 | "{"name": "Jorg... | "Jorge" | "" | "2020-01-01" |
#---- Abrindo todas as chaves do JSON de uma vez (não testado em JSON dentro de JSON)
df.select(col('tags').apply(json.loads)).unnest('tags')
manager_st_dt | name | office | position | termination_date |
---|---|---|---|---|
str | str | str | str | str |
null | "Maria" | "Seattle" | "developer" | null |
null | "Josh" | null | "analyst" | "2020-01-01" |
"2020-01-01" | "Jorge" | "" | "architect" | null |
Conheci essa ideia de Window Function dentro do SQL, com as funções mais básicas de row_number
e rank
. Se eu pudesse traduzi-la para quem não conhece, seria um groupby
com mais funcionalidades. Como teoria, recomendo ler esse link de como usar window functions em SQL.
Vou utilizar como exemplo o dataset que está na documentação do Polars. Esse dataset está no contexto do desenho Pokémon e está na granularidade de cada um dos bichinhos (pokémons) junto às suas características, como velocidade, ataque e etc.
df = read_csv(
"https://gist.githubusercontent.com/ritchie46/cac6b337ea52281aa23c049250a4ff03/raw/89a957ff3919d90e6ef2d34235e6bf22304f3366/pokemon.csv"
)
df.head(5)
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary |
---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | str | str | str | i64 | i64 | i64 | i64 | i64 | i64 | i64 | i64 | bool |
1 | "Bulbasaur" | "Grass" | "Poison" | 318 | 45 | 49 | 49 | 65 | 65 | 45 | 1 | false |
2 | "Ivysaur" | "Grass" | "Poison" | 405 | 60 | 62 | 63 | 80 | 80 | 60 | 1 | false |
3 | "Venusaur" | "Grass" | "Poison" | 525 | 80 | 82 | 83 | 100 | 100 | 80 | 1 | false |
3 | "VenusaurMega V... | "Grass" | "Poison" | 625 | 80 | 100 | 123 | 122 | 120 | 80 | 1 | false |
4 | "Charmander" | "Fire" | null | 309 | 39 | 52 | 43 | 60 | 50 | 65 | 1 | false |
Vamos criar uma coluna que conta a quantidade de cada um dos Type 1 disponíveis.
Abaixo filtrei somente os pokémons onde o Type 1 é Fire. Notem que a coluna "#", que representa um ID da linha, mostra que a contagem das linhas continuou mesmo após um longo pulo das linhas. Em SQL, é equivalente a:
ROW_NUMBER() OVER (PARTITION BY Type1)
df\
.with_columns(\
lit(1).alias('constante')
)\
.select([\
col('#'),
col('Name'),
col('Type 1'),
col('constante').cumsum().over('Type 1').alias('row_number_type1') # Criação efetiva do row_number
])\
.filter(col('Type 1') == 'Fire')
# | Name | Type 1 | row_number_type1 |
---|---|---|---|
i64 | str | str | i32 |
4 | "Charmander" | "Fire" | 1 |
5 | "Charmeleon" | "Fire" | 2 |
6 | "Charizard" | "Fire" | 3 |
6 | "CharizardMega ... | "Fire" | 4 |
6 | "CharizardMega ... | "Fire" | 5 |
37 | "Vulpix" | "Fire" | 6 |
38 | "Ninetales" | "Fire" | 7 |
58 | "Growlithe" | "Fire" | 8 |
59 | "Arcanine" | "Fire" | 9 |
77 | "Ponyta" | "Fire" | 10 |
78 | "Rapidash" | "Fire" | 11 |
126 | "Magmar" | "Fire" | 12 |
136 | "Flareon" | "Fire" | 13 |
146 | "Moltres" | "Fire" | 14 |
A ideia agora é rankear os tipos de Pokémons (Type 1) por seu ataque (Attack). Para isso, vamos criar uma coluna que faz essa indicação de qual a linha possui qual rank.
RANK(Attack) OVER (PARTITION BY Type1 ORDER BY Attack ASC)
Notem que foi criado uma nova coluna chamada rank_attack_by_type1
que mostra qual o rank daquela linha baseado no ataque e "agrupada" para cada um dos Type 1
.
df\
.with_columns(\
(col('Attack').rank('dense').over('Type 1')).alias('rank_attack_by_type1')
)\
.filter(col('Type 1') == 'Fire')
# | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary | rank_attack_by_type1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i64 | str | str | str | i64 | i64 | i64 | i64 | i64 | i64 | i64 | i64 | bool | u32 |
4 | "Charmander" | "Fire" | null | 309 | 39 | 52 | 43 | 60 | 50 | 65 | 1 | false | 2 |
5 | "Charmeleon" | "Fire" | null | 405 | 58 | 64 | 58 | 80 | 65 | 80 | 1 | false | 3 |
6 | "Charizard" | "Fire" | "Flying" | 534 | 78 | 84 | 78 | 109 | 85 | 100 | 1 | false | 6 |
6 | "CharizardMega ... | "Fire" | "Dragon" | 634 | 78 | 130 | 111 | 130 | 85 | 100 | 1 | false | 12 |
6 | "CharizardMega ... | "Fire" | "Flying" | 634 | 78 | 104 | 78 | 159 | 115 | 100 | 1 | false | 10 |
37 | "Vulpix" | "Fire" | null | 299 | 38 | 41 | 40 | 50 | 65 | 65 | 1 | false | 1 |
38 | "Ninetales" | "Fire" | null | 505 | 73 | 76 | 75 | 81 | 100 | 100 | 1 | false | 5 |
58 | "Growlithe" | "Fire" | null | 350 | 55 | 70 | 45 | 70 | 50 | 60 | 1 | false | 4 |
59 | "Arcanine" | "Fire" | null | 555 | 90 | 110 | 80 | 100 | 80 | 95 | 1 | false | 11 |
77 | "Ponyta" | "Fire" | null | 410 | 50 | 85 | 55 | 65 | 65 | 90 | 1 | false | 7 |
78 | "Rapidash" | "Fire" | null | 500 | 65 | 100 | 70 | 80 | 80 | 105 | 1 | false | 9 |
126 | "Magmar" | "Fire" | null | 495 | 65 | 95 | 57 | 100 | 85 | 93 | 1 | false | 8 |
136 | "Flareon" | "Fire" | null | 525 | 65 | 130 | 60 | 95 | 110 | 65 | 1 | false | 12 |
146 | "Moltres" | "Fire" | "Flying" | 580 | 90 | 100 | 90 | 125 | 85 | 90 | 1 | true | 9 |
Usuários de Spark já conhecem.
Abaixo como podemos deixar o nosso dataframe como um objetivo lazy** dentro do Polars, adicionando apenas uma linha de comando.**
iris = read_csv("https://j.mp/iriscsv")
iris.head(3)
sepal_length | sepal_width | petal_length | petal_width | species |
---|---|---|---|---|
f64 | f64 | f64 | f64 | str |
5.1 | 3.5 | 1.4 | 0.2 | "setosa" |
4.9 | 3.0 | 1.4 | 0.2 | "setosa" |
4.7 | 3.2 | 1.3 | 0.2 | "setosa" |
#---- Notem que ele foi para um "Naive Plan"
iris\
.lazy()
#---- Criei uma coluna nova e mesmo assim ele não me mostrou o output
iris\
.lazy()\
.with_columns(\
(col('sepal_length') * 5).alias('new_sepal_length')
)
Para recuperar o seu dataframe após as manipulações/modificações nos dados, basta você utilizar um .collect()
.
#---- Utilizando o collect para recuperar o dataframe
iris\
.lazy()\
.with_columns(\
(col('sepal_length') * 5).alias('new_sepal_length')
)\
.filter(col('new_sepal_length') > 25)\
.collect()\
.head()
sepal_length | sepal_width | petal_length | petal_width | species | new_sepal_length |
---|---|---|---|---|---|
f64 | f64 | f64 | f64 | str | f64 |
5.1 | 3.5 | 1.4 | 0.2 | "setosa" | 25.5 |
5.4 | 3.9 | 1.7 | 0.4 | "setosa" | 27.0 |
5.4 | 3.7 | 1.5 | 0.2 | "setosa" | 27.0 |
5.8 | 4.0 | 1.2 | 0.2 | "setosa" | 29.0 |
5.7 | 4.4 | 1.5 | 0.4 | "setosa" | 28.5 |
Dica da Cenobita que recentemente precisou testar o Polars para uma tarefa de tratar dados, segundo ela, enormes.
Mesmo que seus dados não caibam na sua memória, você consegue aplicar toda sua manipulação, filtros e tudo mais nesses dados e salvar o resultado em um arquivo parquet
. E tudo isso é possível graças a combinação do lazy()
com o sink_parquet()
.
Para mais detalhes, veja este link sobre "Sinking larger-than-memory Parquet files".
Na prática, como ele funciona:
read_csv("https://j.mp/iriscsv")\
.lazy()\
.with_columns(\
(col('sepal_length') * 5).alias('new_sepal_length')
)\
.filter(col('new_sepal_length') > 25)\
.sink_parquet('../02-data/sink_parquet_output.parquet')