Menu Zamknij

Porównaj styl pisarzy część 15 – Pickle vs. comma-separated values

W tej części zajmiemy się oszczędnością czasu i procesora, spróbujemy zapisać Dataframe jako plik, by nie musieć za każdym razem sięgać do treści książek.

Pewnie zauważyłeś, że przy każdym uruchomieniu naszego programu otwierany jest plik .txt, usuwane przypisy, usuwane znaki nie będące literami, tworzone są listy trzech wyrazów, zliczane i umieszczane w Dataframe. Im więcej książek, ta operacja będzie trwać dłużej i dłużej. Najwyższy czas pożegnać się z ciągłym wracaniem do początku i zapisać Dataframe do pliku, a następnie korzystać z zapisanego, gotowego Dataframe. Pewnie zdarzyło ci się spotkać z plikami .csv (comma-separated values), to po prostu plik tekstowy w którym dane oddzielane są przecinkami (comma). Nic nie stoi na przeszkodzie by uzyskane przez nas dane zapisać w tej postaci. Wykorzystamy do tego funkcję to_csv().Jako argument funkcji podajemy nazwę pliku wraz ze ścieżką zapisu. Popatrz na nasz program:

from os import listdir
import re
from collections import Counter
import pandas as pd

sciezka = "F:\\python\\ids\\"
sciezka_csv = "F:\\python\\csv\\"
pliki = listdir(sciezka)

#dla każdego pliu w katalogu
for plik in pliki:
    #otwórz plik
    ksiazka = open(sciezka + plik, mode="r", encoding = "utf-8")
    tekst = ksiazka.read()
    #usuń kłopotliwe przypisek T. D. M. i przypisy
    if plik == "Pamietnik pani Hanki - Tadeusz Dolega-Mostowicz.txt":
        tekst = tekst.replace("(Przypisek T. D. M.)","")
    elif plik == "Dziecko salonu - Janusz Korczak.txt" or \
         plik == "Kiedy znow bede maly - Janusz Korczak.txt" or \
         plik == "Bankructwo malego Dzeka - Janusz Korczak.txt":
        tekst = tekst.split("Przypisy:")[0]
    #przygotuj tekst do zliczania powtórzeń słów
    tekst = tekst.replace("\n", " ")
    tekst = tekst.lower()
    tekst = re.sub("[^a-z ąćęłńóśóżź]", "", tekst)
    tekst = re.sub(" +", " ", tekst)
    wyrazy = tekst.split()
    lista_trzech_wyrazow = []
    #utwórz listę trzech kolejnych wyrazów
    for indeks in range(len(wyrazy) - 2):
        trzy_wyrazy = " ".join(wyrazy[indeks:indeks + 3])
        lista_trzech_wyrazow.append(trzy_wyrazy)
    #utwórz słownik zliczający liczbę powtórzeń trzech kolejnych wyrazów
    liczba_trzygramow = Counter(lista_trzech_wyrazow)
    #nadaj tytuł kolumnie zliaczania równy inicjałom i tytułowi książki
    if plik.find("Tadeusz Dolega-Mostowicz") > -1:
        autortytul = (f"TDM {plik.split(' - Tadeusz Dolega-Mostowicz')[0]}")
    else:
        autortytul = (f"JK {plik.split(' - Janusz Korczak')[0]}")
    #utwórz Dataframe (df) ze słownika
    df = pd.DataFrame(liczba_trzygramow.items(),columns = ["Trzygram",autortytul])
    #posortuj od najliczniej występującego trzygramu
    df = df.sort_values(by = autortytul, ascending = False)
    #wyeksoprtuj dataframe do pliku .csv
    df.to_csv(f"{sciezka_csv}{autortytul}.csv")

W 7 linijce zdefiniowaliśmy ścieżkę zapisu (najpierw musisz utworzyć katalog o takiej nazwie, nie rób tego jako podkatalog katalogu w którym są pliki książek, to komplikuje odczytanie plików .txt). W 45 linijce wykorzystujemy funkcję na naszym Dataframe (df) z argumentem podającym ścieżkę, a nazwa piliku jest taka sama jak nazwa kolumny z liczbą powtórzeń 3-gramu z części 14. Efekt uruchomionego programu tym razem będzie tylko widoczny w wybranym katalogu, u mnie wygląda to tak:

to_csv()

Otworzę pierwszy plik za pomocą notatnika (nie excela jak sugeruje ikonka), kilka górnych wierszy wygląda tak:

,Trzygram,JK Bankructwo malego Dzeka
3415,pani powiedziała że,11
6376,że się nie,8
6761,żeby się nie,8
33596,bo nie ma,7
(...)

Tak wygląda plik .csv od środka. Pierwsza linijka to tytuły kolumn. Pierwsza kolumna zawiera numerację nadaną automatycznie przez Dataframe i ta kolumna nie ma nazwy, następne mamy nazwy naszych dwóch kolumn.

Teraz zamiast odczytywać i „obrabiać” za każdym razem pliki z książkami odczytajmy i wydrukujmy nasze .csv. Do otwarcia pliku .csv użyjemy funkcji read_csv(), której argumentem jest nazwa pliku wraz ze ścieżką. Program będzie wyglądał tak:

from os import listdir
import pandas as pd

sciezka = "F:\\python\\csv\\"
pliki = listdir(sciezka)

#dla każdego pliu w katalogu
for plik in pliki:
    #wydrukuj pierwsze dwa wiersze
    df = pd.read_csv(f"{sciezka}{plik}")
    print(df.head(2))

Prawda, że nasz program zrobił się dużo krótszy? W 4 linijce zmieniamy ścieżkę na taką, gdzie zapisałeś pliki .csv (i tylko pliki .csv, nic innego nie może się znaleźć w tym katalogu). W linijce 10 funkcją read_csv() odczytujemy pliki i w 11 linijce drukujemy pierwsze dwie linijki każdego z plików. Zobaczmy efekt:

====================== RESTART: F:/python38/wpis 15 csv.py =====================
   Unnamed: 0             Trzygram  JK Bankructwo malego Dzeka
0        3415  pani powiedziała że                          11
1        6376           że się nie                           8
   Unnamed: 0      Trzygram  JK Dzieci ulicy
0        1696  w tej chwili               13
1       14253    ja wiem że                8
   Unnamed: 0     Trzygram  JK Dziecko salonu
0        3107   co to jest                 17
1        2879  ja nie chcę                 13
   Unnamed: 0    Trzygram  JK Kajtus Czarodziej
0         295  nie wie co                     9
1        1607    co to za                     9
   Unnamed: 0      Trzygram  JK Kiedy znow bede maly
0         606  nie wiem czy                       18
1        3759   nie wiem co                       13
   Unnamed: 0              Trzygram  JK Krol Macius na wyspie bezludnej
0         483  wasza królewska mość                                  27
1       44457       dziś popr forma                                  22
   Unnamed: 0        Trzygram  TDM Bracia Dalcz i S-ka
0        1479  w każdym razie                       55
1        5833    w ten sposób                       40
   Unnamed: 0        Trzygram  TDM Dr. Murek zredukowany
0        2719  w każdym razie                         30
1         711     w związku z                         12
   Unnamed: 0        Trzygram  TDM Drugie zycie doktora Murka
0        5274  w każdym razie                              20
1       18308  okazało się że                              12
   Unnamed: 0                 Trzygram  TDM Kariera Nikodema Dyzmy
0        8614  kochany panie nikodemie                          23
1        3510              che che che                          18
   Unnamed: 0           Trzygram  TDM Kiwony
0        2206  do przekonania że          13
1        2783     w każdym razie          11
   Unnamed: 0        Trzygram  TDM Pamietnik pani Hanki
0        2257  w każdym razie                        40
1        2899        o tym że                        28
>>> 

Dataframe wygląda nieco inaczej niż ten sam przed zapisem. Indeksy wierszy stały się dodatkową kolumną (Unnamed: 0), a Python dodał nowe indeksy, to zupełnie nam nie przeszkadza. Jeśli chcesz być trochę mniej inferior a bardziej superior, możesz jako dodatkowy argument funkcji read_csv() określić, która kolumna pliku .csv jest indeksem. W naszym przypadku do Dataframe został dodany indeks i zapisany w pliku .csv jako pierwsza kolumna. Znamy już na tyle programowanie i wiemy, że numeracja kolumn zaczyna się od 0. Dodajmy argument wskazujący która kolumna jest indeksem:

from os import listdir
import pandas as pd

sciezka = "F:\\python\\csv\\"
pliki = listdir(sciezka)

#dla każdego pliu w katalogu
for plik in pliki:
    #wydrukuj pierwsze dwa pierwsze
    df = pd.read_csv(f"{sciezka}{plik}", index_col = [0])
    print(df.head(2))

Zauważ, że teraz funkcja read_csv() ma dwa argumenty read_csv(sciezka + plik, index_col = [0]), czyli prócz ścieżki podajemy, że kolumną z indeksem (index_col) jest 1 kolumna ( = [0]). Zobaczmy efekt:

====================== RESTART: F:/python38/wpis 15 csv.py =====================
                 Trzygram  JK Bankructwo malego Dzeka
3415  pani powiedziała że                          11
6376           że się nie                           8
           Trzygram  JK Dzieci ulicy
1696   w tej chwili               13
14253    ja wiem że                8
         Trzygram  JK Dziecko salonu
3107   co to jest                 17
2879  ja nie chcę                 13
        Trzygram  JK Kajtus Czarodziej
295   nie wie co                     9
1607    co to za                     9
          Trzygram  JK Kiedy znow bede maly
606   nie wiem czy                       18
3759   nie wiem co                       13
                   Trzygram  JK Krol Macius na wyspie bezludnej
483    wasza królewska mość                                  27
44457       dziś popr forma                                  22
            Trzygram  TDM Bracia Dalcz i S-ka
1479  w każdym razie                       55
5833    w ten sposób                       40
            Trzygram  TDM Dr. Murek zredukowany
2719  w każdym razie                         30
711      w związku z                         12
             Trzygram  TDM Drugie zycie doktora Murka
5274   w każdym razie                              20
18308  okazało się że                              12
                     Trzygram  TDM Kariera Nikodema Dyzmy
8614  kochany panie nikodemie                          23
3510              che che che                          18
               Trzygram  TDM Kiwony
2206  do przekonania że          13
2783     w każdym razie          11
            Trzygram  TDM Pamietnik pani Hanki
2257  w każdym razie                        40
2899        o tym że                        28
>>> 

Świetnie, wszystko działa. Pandas oferuje nie tylko eksport do .csv, możemy zapisać pliki w szeregach formatów, np. .xlsx, .html, .json. My skupimy się na formacie pliku pickle. Format pickle zapisuje Dataframe jako strumień bajtów (robi serializację). Zapis, odczyt, wprowadzanie zmian w plikach typu pickle są szybsze niż w .csv. O ile szybsze? Zaraz sprawdzimy, ale zacznijmy od zapisania Dataframe do plików pickle:

from os import listdir
import re
from collections import Counter
import pandas as pd

sciezka = "F:\\python\\ids\\"
sciezka_pickle = "F:\\python\\pickle\\"
pliki = listdir(sciezka)
#dla każdego pliu w katalogu
for plik in pliki:
    #otwórz plik
    ksiazka = open(sciezka + plik, mode="r", encoding = "utf-8")
    tekst = ksiazka.read()
    #usuń kłopotliwe przypisek T. D. M. i przypisy
    if plik == "Pamietnik pani Hanki - Tadeusz Dolega-Mostowicz.txt":
        tekst = tekst.replace("(Przypisek T. D. M.)","")
    elif plik == "Dziecko salonu - Janusz Korczak.txt" or \
         plik == "Kiedy znow bede maly - Janusz Korczak.txt" or \
         plik == "Bankructwo malego Dzeka - Janusz Korczak.txt":
        tekst = tekst.split("Przypisy:")[0]
    #przygotuj tekst do zliczania powtórzeń słów
    tekst = tekst.replace("\n", " ")
    tekst = tekst.lower()
    tekst = re.sub("[^a-z ąćęłńóśóżź]", "", tekst)
    tekst = re.sub(" +", " ", tekst)
    wyrazy = tekst.split()
    lista_trzech_wyrazow = []
    #utwórz listę trzech kolejnych wyrazów
    for indeks in range(len(wyrazy) - 2):
        trzy_wyrazy = " ".join(wyrazy[indeks:indeks + 3])
        lista_trzech_wyrazow.append(trzy_wyrazy)
    #utwórz słownik zliczający liczbę powtórzeń trzech kolejnych wyrazów
    liczba_trzygramow = Counter(lista_trzech_wyrazow)
    #nadaj tytuł kolumnie zliaczania równy inicjałom i tytułowi książki
    if plik.find("Tadeusz Dolega-Mostowicz") > -1:
        autortytul = (f"TDM {plik.split(' - Tadeusz Dolega-Mostowicz')[0]}")
    else:
        autortytul = (f"JK {plik.split(' - Janusz Korczak')[0]}")
    #utwórz Dataframe (df) ze słownika
    df = pd.DataFrame(liczba_trzygramow.items(),columns = ["Trzygram",autortytul])
    #posortuj od najliczniej występującego trzygramu
    df = df.sort_values(by = autortytul, ascending = False)
    #wyeksoprtuj dataframe 
    df.to_pickle(f"{sciezka_pickle}{autortytul}.pkl")

W 7 linijce zmieniliśmy ścieżkę na prowadzącą do katalogu, gdzie zapiszemy nasze pliki pickle (pamiętaj, najpierw utwórz ten katalog). W 44 linijce używamy funkcji to_pickle() w podobny sposób, jak funkcji to_csv() zmieniając jedynie rozszerzenie pliku na .pkl. Sprawdźmy, czy pliki się zapisały:

to_pickle()
to_pickle()

Podejrzymy zawarość takiego pliku w notatniku:

€•ż      Śpandas.core.frame”Ś	DataFrame”“”)”}”(Ś_data”Śpandas.core.internals.managers”ŚBlockManager”“”)”(]”(Śpandas.core.indexes.base”Ś
_new_Index”“”hŚIndex”“”}”(Śdata”Śnumpy.core.multiarray”Ś_reconstruct”“”Śnumpy”Śndarray”“”K …”Cb”‡”R”(KK…”hŚdtype”“”ŚO4”K K‡”R”(KŚ|”NNNJ˙˙˙˙J˙˙˙˙K?t”b‰]”(ŚTrzygram”ŚJK Kiedy znow bede maly”et”bŚname”Nu†”R”h
Śpandas.core.indexes.numeric”Ś
Int64Index”“”}”(hŚnumpy.core.numeric”Ś_frombuffer”“”(– ´     ^      Ż      ś      ń      ¸      ,   
(...)
KM„–†”h!‰]”(Śnie wiem czy”Śnie wiem co”ŚsiÄ™ zdaje ĹĽe”Śsam nie wiem”Ś	a ja siÄ™”Śkiedy byĹ‚em dorosĹ‚y”Ś	a on siÄ™”ŚĹĽeby siÄ™ nie”Ś	i nic nie”Św tej chwili”Śa ja nie”Śtak mi siÄ™”ŚĹĽe siÄ™ nie”Śkiedy byĹ‚em nauczycielem”Śmi siÄ™ ĹĽe”Ś
i tak siÄ™”Świem co robi攌
siÄ™ z nim”Ś
(...)

Dużo mniej przyjemny do zrozumienia, prawda? Ale nam wystarczy jeśli zrozumie go Python!

Tak samo jak poprzednio zaimportujmy pliki .pkl do Dataframe i wydrukujemy. Użyjemy do tego funkcji read_pickle(). Argumentem, jak poprzednio, będzie ścieżka wskazująca każdy z plików:

from os import listdir
import pandas as pd

sciezka = "F:\\python\\pickle\\"
pliki = listdir(sciezka)
#dla każdego pliu w katalogu
for plik in pliki:
    #wydrukuj pierwsze dwa pierwsze
    df = pd.read_pickle(f"{sciezka}{plik}")
    print(df.head(2))

Zobaczmy efekt:

====================== RESTART: F:/python38/wpis 15 pkl.py =====================
                 Trzygram  JK Bankructwo malego Dzeka
3415  pani powiedziała że                          11
6376           że się nie                           8
           Trzygram  JK Dzieci ulicy
1696   w tej chwili               13
14253    ja wiem że                8
         Trzygram  JK Dziecko salonu
3107   co to jest                 17
2879  ja nie chcę                 13
        Trzygram  JK Kajtus Czarodziej
295   nie wie co                     9
1607    co to za                     9
          Trzygram  JK Kiedy znow bede maly
606   nie wiem czy                       18
3759   nie wiem co                       13
                   Trzygram  JK Krol Macius na wyspie bezludnej
483    wasza królewska mość                                  27
44457       dziś popr forma                                  22
            Trzygram  TDM Bracia Dalcz i S-ka
1479  w każdym razie                       55
5833    w ten sposób                       40
            Trzygram  TDM Dr. Murek zredukowany
2719  w każdym razie                         30
711      w związku z                         12
             Trzygram  TDM Drugie zycie doktora Murka
5274   w każdym razie                              20
18308  okazało się że                              12
                     Trzygram  TDM Kariera Nikodema Dyzmy
8614  kochany panie nikodemie                          23
3510              che che che                          18
               Trzygram  TDM Kiwony
2206  do przekonania że          13
2783     w każdym razie          11
            Trzygram  TDM Pamietnik pani Hanki
2257  w każdym razie                        40
2899        o tym że                        28
>>> 

Już tutaj widzisz przydatną właściwość pickle, pickle pamięta co było indeksem, więc ty nie musisz pamiętać o dodatkowym argumencie, jak to było w przypadku read_csv(). Druga rzecz to szybkość, zmierzyłem w 100 powtórzeniach, jak szybko Python odczyta i wydrukuje pierwsze 10 linijek z naszych 12 plików w formacie .csv i .pkl. Średnia wyciągnięta ze 100 pomiarów:

Odczyt 12 .csv: 0.07213027899999998
Odczyt 12 .pkl: 0.07171955499999991

Wynik jest trochę dyskusyjny, mówimy tu o różnicach mniejszych niż tysięczne sekundy. Kolejna rzecz, jaką można porównać, to rozmiar plików. W przypadku .csv 12 naszych plików ma rozmiar 23 928 169 bajtów, w przypadku .pkl jest to 31 400 811 bajtów. Jeśli chcesz dowiedzieć się więcej na temat formatów, szybkości odczytu, zapisu, rozmiaru plików zajrzyj tutaj. A tymczasem to wszystko na dziś, w następnej części zajmiemy się wizualizacją danych, czyli wykresami!

Zadanie domowe

Podążanie za instrukcjami, nawet jeśli je wszystkie wykonujesz samodzielnie, nie zrobi z ciebie programisty. Zadania domowe mogą wydawać się na początku trudne. Tu nie ma rozwiązania podanego na talerzu, użyj dowolnych źródeł, by znaleźć odpowiedź.

  1. W tym rozdziale pisałem o komplikacjach, jakie niesie ze sobą tworzenie podfolderu w folderze z plikami książek w formacie .txt. W zadaniu domowym utwórz w folderze z książkami .txt dwa foldery nazwane csv i pkl, zmień program tak, by poprawnie odczytał książki i zapisał je do folderów odpowiadających ich formatowi.
  2. Wyeksportuj pliki do formatu .json i .html. Podejrzyj oba w notatniku, otwórz plik .html w przeglądarce internetowej, jak oceniasz prędkość eksportu do .html?
3 2 votes
Ocena artykułu

0 komentarzy
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
0
Chcesz podzielić się komentarzem?x