<jupyter_start><jupyter_text>Essas 3 abordagens armazenam exatamente as mesmas informações na memória do computador.E todas elas permitem o acesso programático dos valores.Porém, as possibilidades e características são diferentes.No caso de `my_var_list` os valores só podem ser acessados através de índices inteiros. Porém isso garante que os dados estão ordenados.No caso de `my_var_dict` os valores podem ser acessados por chaves, permitindo dar "nomes" a cada valor. Porém esses valores são intrinsecamente não ordenados.No caso das variáveis separadas, podemos trabalhar com cada valor separadamente, o que é mais complicado com as estruturas. Porém, precisamos replicar código ou acabar criando uma estrutura para poder fazer uma mesma operação com todos esses valores. Inclusive...<jupyter_code>my_var_list_inception = [
    my_var_0,
    my_var_1,
    my_var_2,
    my_var_3
]

assert my_var_list_inception == my_var_list
my_var_dict_inception = {
    0: my_var_0,
    1: my_var_1,
    2: my_var_2,
    3: my_var_3
}

assert my_var_dict_inception == my_var_dict
# ou ainda...
my_var_dict_compr = {index: value for index, value in enumerate(my_var_list)}
assert my_var_dict_compr == my_var_dict

my_var_list_compr = [value for value in my_var_dict.values()]<jupyter_output><empty_output><jupyter_text>Exercício!`my_var_dict_compr` é garantidamente igual a `my_var_dict`, prometi isso com o assert da célula acima! Explique como isso é verdade (recomendo escrever).    dica: não sabe o que faz enumerate? `help(enumerate)`, tente bater cabeça com a documentação um pouco, mas não demais. Travando, pesquise na internet.`my_var_list_compr` tem um porém! Que porém é esse?    dica: gostaríamos de poder afirmar, com certeza absoluta, que `my_var_list_compr` é idêntica a `my_var_list`. Ou seja, que podemos escrever `assert my_var_list_inception == my_var_list` assim como foi feito para o dicionário.Pontos extras...Tendo achado o porém da `my_var_list_compr`, escreva uma versão onde é possível colocar o `assert`.    dica1: use `dir()` e `help()` pra descobrir um método útil    dica2: Google não vai ajudar diretamente aqui, mas indiretamente pode com a pergunta certa    dica3: se tem dificuldade com a sintaxe de comprehension, considere um loop ____________________________________________________Voltando...<jupyter_code>proto_matriz = [
    [1,2,3],
    [4,5,6],
    [7,8,9]
]
proto_matriz[0][1]
proto_matriz[0]<jupyter_output><empty_output><jupyter_text>-----------------------------Pandas vai fornecer uma interface (classe) onde será possível angariar vantagens de listas (estruturas ordenadas), e de dicionários (indexação arbitrária, e outras).Essa estrutura se chama `pandas.Series`.E, assim como listas fornecem uma abstração para matrizes através de listas de listas, Pandas trará uma abstração de tabela através de `Series` de `Series`.A analogia não é perfeita, e as diferenças forçam a biblioteca a oferecem uma outra interface chamada `pandas.DataFrame`. Pandas é construída pra ter boa performance.Em Python isso não é exatamente trivial.Então Pandas foi toda escrita em cima de uma biblioteca chamada numpy, que por sua vez tem muito código escrito em C.Geralmente é verdade que, se Python e rápido, então é C num pacotinho amigável de Python (JuliaFTW).<jupyter_code>import pandas
from_var_list = pandas.Series(my_var_list)
from_var_list<jupyter_output><empty_output><jupyter_text>------------------------------------------Maneira direta de criar uma Series... usar seu método construtor.Obrigatoriamente precisa ser passado algum objeto python que pode armazenar valores estruturados.O termo técnico é **iterable** (basicamente alguma coisa que podemos botar num loop).<jupyter_code>from_var_list[2]
from_var_dict = pandas.Series(my_var_dict)

from_var_dict<jupyter_output><empty_output><jupyter_text>--------------------------------------------Acesso aos dados, como em listas e dicionários, é feito por índices.Observe que o display das variáveis criadas mostra 3 informações: - índices - valores - dtypeDe notável, vemos principalmente que dtype é único para a Series inteira.Em ambos os casos até agora, esse *dtype* foi `object`, o que quer dizer uma variável python qualquer.<jupyter_code>lst_1 = [1,2,3]
lst_2 = [1,2,3.0]
lst_3 = [1,2,'3']
lst_char = ['a', 'b', 'c']

ser_1 = pandas.Series(lst_1)
ser_2 = pandas.Series(lst_2)
ser_3 = pandas.Series(lst_3)
ser_char = pandas.Series(lst_char)
ser_1
ser_2
ser_3
ser_char<jupyter_output><empty_output><jupyter_text>----------------------------------------------------------------Dos exemplos acima podemos ver que o pandas se esforça pra achar um tipo numérico comum para atribuir à Series toda.Falhando, cai no caso object.Tristemente, não há uma separação para string.Essa decisão foi tomada pois strings não tem ganho de performance na identificação do dtype, então não existe esforço em identificar esse caso.Porém, o cientista de dados, analista que usa a biblioteca gosta de saber se é tudo string, é uma informação útil.Existem planos para incluir essa diferenciação pra ajudar os seres por trás dos teclados.Portanto esse comportamenteo pode mudar em versões futuras da biblioteca.<jupyter_code>ser_2_int = pandas.Series(lst_2, dtype=int)
ser_2_int
type(ser_2_int[0])<jupyter_output><empty_output><jupyter_text>---------------------------------------------------Mas, pedindo com jeitinho, o `dtype` pode ser o que preferimos... dentro do limite do razoável, claro<jupyter_code>ser_char.index
ser_1_char = pandas.Series(lst_1, index=lst_char, name='char_indexed')

ser_1_char
ser_1_char['a']
datum = {':-)': 6.283185, ':-(': 3.141592}

ser_str_keys = pandas.Series(datum)

ser_str_keys
ser_str_keys = pandas.Series(datum, index=[':-)', ':sad_face:'])

ser_str_keys<jupyter_output><empty_output><jupyter_text>---------------------------------------------------Também podemos passar pro método contrutor um outro iterable a ser usado como índice.No caso dos dados serem um dicionário, as chaves serão usadas como índice.Se além disso for passado algum valor pra index, temos o comportamento acima.Basicamente, o index que foi passado vai ser o índice da Series, se o dicionário contiver uma chave correspondente, usa o valor do dicionário, NaN caso contrário<jupyter_code>bad_series = pandas.Series(data=lst_3, index=['a', 'a', 'b'], name='bad')

bad_series
bad_series['a']
type(ser_1_char['a'])
type(bad_series['a'])<jupyter_output><empty_output><jupyter_text>---------------------Diferente de dicionários, Series podem ter repetição de índice.Esse comportamento será importante para que existam DataFrames com múltiplos índices.De todo jeito, "as is", é possível indexar Series por algum tipo de classificação, e filtrar facilmente de acordo com essa classificação.Claro, melhor mesmo num caso desses é ter um DataFrame com 2 colunas e filtrar pela classificação, mas apressado come cru! Ainda estamos em Series!!Observe que no caso em que o índice não é único, o valor de retorno é uma Series.Acontece o mesmo com listas, se acessamos um único elemento através de um índice, recebemos um objeto do tipo que estiver naquele lugar, se acessamos múltiplos elementos por um slice, recebemos algo como outra lista<jupyter_code>ser_1
ser_1[ [True, False, True] ]<jupyter_output><empty_output><jupyter_text>--------------A característica mais confusa sobre acessos a Series...podemos usar uma lista de booleanos para especificar exatamente quais elementos queremos da Series.Lembrando que Series são ordenadas, então isso faz algum sentido.<jupyter_code>ser_1 + ser_2_int
ser_1 + 2*ser_2_int
ser_1 + 3*ser_2_int / ser_2
ser_1 != ser_3
ser_char[ ser_1 == ser_3 ]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1094747817020722, 'fraction_numerical': 0.012120422259872279, 'mean_word_length': 4.516894320632638, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '453220', 'license': 'MIT', 'n_tokens_mistral': 2679, 'n_tokens_neox': 2592, 'n_words': 872, 'path': 'series.ipynb', 'repo_name': 'jnthmota/JupyterLab', 'size': '7673'}