Python - wprowadzenie

06 - Biblioteka `pandas` cz. 2: `DataFrame`

Obiekt typu DataFrame, co się tłumaczy jako ramka danych, jak wspomniałem wcześniej, można w uproszczeniu porównać do tabel znanych np. z arkuszy kalkulacyjnych. Przechowują dane w kolumnach i rzędach, ale choć kolumny zawierają dane jednego typu, każda kolumna może zawierać dane innego typu. Kolumny, które odpowiadają obiektom typu Series mają ,,nagłówki’’ (a także indeksy), do których możemy się odwoływać np. przy selekcji danych. Rzędy mają również indeksy, a także mogą posiadać etykiety, których używamy tak jak w przypadku Series.

Tworzenie ramek danych

Ramki danych można utworzyć w różny sposób. Często tworzymy je przy odczycie danych z plików, np. w formacie csv co opiszę w dalszej części, ale można je też tworzyć ,,od zera’’. Zacznijmy od utworzenia pustej ramki danych:

import pandas as pd
ramka = pd.DataFrame()
print(ramka)
Empty DataFrame
Columns: []
Index: []

Teraz stwórzmy ramkę danych, używając słownika list. Na razie będzie ona zawierała kilka gatunków organizmów i liczby (2n) chromosomów:

import pandas as pd
slownik_list = {'Gatunek': ['Myrmecia pilosula',
               'Ophioglossum reticulatum',
               'Canis familiaris',
               'Homo sapiens',
               'Arabidopsis thaliana'
              ],
              'L_chr': [2, 1400, 36, 46, 10]
             }
dane = pd.DataFrame(slownik_list)
print(dane)
                    Gatunek  L_chr
0         Myrmecia pilosula      2
1  Ophioglossum reticulatum   1400
2          Canis familiaris     36
3              Homo sapiens     46
4      Arabidopsis thaliana     10

Jak widać, klucze w słowniku zostały przekształcone w nazwy kolumn. Uwaga: jeśli używasz Jupyter-lab, a wydrukowanie ramki jest ostatnim poleceniem w uruchamianej komórce, to zamiast print(ramka) możesz pisać po prostu ramka - wyświetli się w estetyczniejszej formie. Kolejne rzędy posiadają indeksy, ale można im przypisać etykiety rzędów, podobnie jak elementom obiektu Series:

dane.index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik']
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10

Oczywiście etykiety można przypisać od razu, przy tworzeniu ramki:

dane = pd.DataFrame(slownik_list, index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik'])
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10

Przy tworzeniu ramki, można zmienić kolejność kolumn, a także dodać nowe. Jeśli nie będą im przypisane dane, zostaną wypełnione wartościami NaN:

dane = pd.DataFrame(slownik_list, 
                    index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik'],
                    columns = ['L_chr', 'Gatunek', 'Wielkosc_genomu'])
print(dane)
             L_chr                   Gatunek Wielkosc_genomu
mrówka           2         Myrmecia pilosula             NaN
nasięźrzał    1400  Ophioglossum reticulatum             NaN
pies            36          Canis familiaris             NaN
człowiek        46              Homo sapiens             NaN
rzodkiewnik     10      Arabidopsis thaliana             NaN

Jeśli nie uwzględnimy nazwy, któregoś z elementu słownika, seria danych nie zostanie uwzględniona:

dane = pd.DataFrame(slownik_list, 
                    index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik'],
                    columns = ['Gatunek', 'Wielkosc_genomu'])
print(dane)
                              Gatunek Wielkosc_genomu
mrówka              Myrmecia pilosula             NaN
nasięźrzał   Ophioglossum reticulatum             NaN
pies                 Canis familiaris             NaN
człowiek                 Homo sapiens             NaN
rzodkiewnik      Arabidopsis thaliana             NaN

Ramki z danymi można utworzyć także na różne inne sposoby, np:

# Ramka danych z listy słowników:
lista_slownikow = [{'Gatunek': 'Myrmecia pilosula', 'L_chr': 2},
                   {'Gatunek': 'Ophioglossum reticulatum', 'L_chr': 1400}, 
                   {'Gatunek': 'Canis familiaris', 'L_chr': 36},
                   {'Gatunek': 'Homo sapiens', 'L_chr': 46},
                   {'Gatunek': 'Arabidopsis thaliana', 'L_chr': 10}]
dane = pd.DataFrame(lista_slownikow,
                    index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik'],)
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10
# Ramka danych z listy krotek:
lista_krotek = [('Myrmecia pilosula', 2), 
                ('Ophioglossum reticulatum', 1400), 
                ('Canis familiaris', 36), 
                ('Homo sapiens', 46), 
                ('Arabidopsis thaliana', 10)]
dane = pd.DataFrame(lista_krotek,
                    columns = ['Gatunek', 'L_chr'],
                    index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik'])
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10
# Ramka z obiektów Series
gatunki = pd.Series(['Myrmecia pilosula', 
                     'Ophioglossum reticulatum', 
                     'Canis familiaris', 
                     'Homo sapiens', 
                     'Arabidopsis thaliana'])
chromosomy = pd.Series([2, 1400, 36, 46, 10])
dane = pd.DataFrame({'Gatunek': gatunki, 'L_chr': chromosomy})
dane.index = ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 'rzodkiewnik']
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10

Proste pobieranie kolumn i rzędów

Obiekty DataFrame cieszą się dużą popularnością między innymi dlatego, ze biblioteka pandas umożliwia dużą swobodę i efektywność w selekcji danych. Na razie zobaczmy jak działa proste wybieranie kolumn i rzędów, później przejdziemy do bardziej złożonych zadań. Najpierw stwórzmy (ponownie) ramkę danych, na których będziemy dalej pracować:

import pandas as pd
slownik_list = {'Gatunek': ['Myrmecia pilosula',
               'Ophioglossum reticulatum',
               'Canis familiaris',
               'Homo sapiens',
               'Arabidopsis thaliana'
              ],
              'L_chr': [2, 1400, 36, 46, 10]
             }
dane = pd.DataFrame(slownik_list, 
                    index = ['mrówka', 'nasięźrzał', 
                             'pies', 'człowiek', 'rzodkiewnik'])
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10

Poszczególne kolumny możemy pobierać używając nazw kolumn, na dwa sposoby:

print(dane['Gatunek'])
print()
print(dane.L_chr)
mrówka                Myrmecia pilosula
nasięźrzał     Ophioglossum reticulatum
pies                   Canis familiaris
człowiek                   Homo sapiens
rzodkiewnik        Arabidopsis thaliana
Name: Gatunek, dtype: object

mrówka            2
nasięźrzał     1400
pies             36
człowiek         46
rzodkiewnik      10
Name: L_chr, dtype: int64

Ten drugi sposób (ramka.nazwa_kolumny) sprawdzi się jednak tylko wtedy, jeśli nazwa kolumny będzie zgodna z zasadami nazw zmiennych w języku Python. Zatem nie powinna zawierać np. spacji.

Przyjrzyj się, w jaki sposób wyświetlana jest zawartość kolumny, coś nam to przypomina. Sprawdźmy typ otrzymanego obiektu:

print(type(dane['Gatunek']))
print(type(dane['L_chr']))
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>

Jak widać, otrzymujemy obiekt typu Series, co wskazuje na sposób pracy z nim. Zwróć uwagę, że niezależnie od wybranej kolumny, dane w seriach które otrzymujemy, są oznaczone etykietami z ramki danych.

Rząd możemy pobrać używając atrybutu loc i etykiety lub iloc z wartością indeksu:

print(dane.loc['pies'])
print()
print(dane.iloc[3])
Gatunek    Canis familiaris
L_chr                    36
Name: pies, dtype: object

Gatunek    Homo sapiens
L_chr                46
Name: człowiek, dtype: object

Dodawanie, modyfikacja i usuwanie kolumn

Do ramki danych łatwo możemy dodać kolejne kolumny. Można to zrobić na różne sposoby. Najpierw odtwórzmy ramkę, jeśli to konieczne:

import pandas as pd
slownik_list = {'Gatunek': ['Myrmecia pilosula',
               'Ophioglossum reticulatum',
               'Canis familiaris',
               'Homo sapiens',
               'Arabidopsis thaliana'
              ],
              'L_chr': [2, 1400, 36, 46, 10]
             }
dane = pd.DataFrame(slownik_list, 
                    index = ['mrówka', 'nasięźrzał', 'pies', 
                             'człowiek', 'rzodkiewnik'])
print(dane)
                              Gatunek  L_chr
mrówka              Myrmecia pilosula      2
nasięźrzał   Ophioglossum reticulatum   1400
pies                 Canis familiaris     36
człowiek                 Homo sapiens     46
rzodkiewnik      Arabidopsis thaliana     10
# Sposób 1
nowe_dane_1 = ['Nie', 'Tak', 'Tak', 'Nie', 'Tak']
dane['Autotrof'] = nowe_dane_1
print()
print(dane)
                              Gatunek  L_chr Autotrof
mrówka              Myrmecia pilosula      2      Nie
nasięźrzał   Ophioglossum reticulatum   1400      Tak
pies                 Canis familiaris     36      Tak
człowiek                 Homo sapiens     46      Nie
rzodkiewnik      Arabidopsis thaliana     10      Tak
# Sposób 2
nowe_dane_2 = ['zwierzęta', 'rośliny', 'zwierzęta', 'zwierzęta', 'rośliny']
dane.insert(1, 'Krolestwo', nowe_dane_2, False)
print()
print(dane)
                              Gatunek  Krolestwo  L_chr Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2      Nie
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      Tak
pies                 Canis familiaris  zwierzęta     36      Tak
człowiek                 Homo sapiens  zwierzęta     46      Nie
rzodkiewnik      Arabidopsis thaliana    rośliny     10      Tak

Pierwszy sposób pozwala na dołączenie nowej kolumny na końcu ramki. Drugi jest nieco bardziej złożony. Metoda insert() przyjęła cztery argumenty: insert(indeks, nazwa kolumny, dane, możliwość_duplikatów) Indeks wskazuje, przed którą kolumną wstawić nowe dane. Ostatni argument (domyślnie jest to False, zatem można było go w tym przypadku pominąć) określa, czy wprowadzana kolumna może się duplikować. Sprawdź, co się stanie, jeśli zmienisz argument na True i ponownie uruchomisz kod.

Możemy łatwo zmodyfikować zawartość całej kolumny, odwołując się do jej nazwy i podając nowe wartości:

nowe_dane_2 = [False, True, True, False, True]
dane['Autotrof'] = nowe_dane_2
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True

Dodanie nowej kolumny można połączyć z wypełnianiem jej wartości z użyciem danych z innej kolumny, np. dodajmy kolumnę n zawierającą liczbę n (haploidalną) chromosomów. Możemy te wartości wyliczyć, dzieląc liczby pobrane z kolumny L-chromosomow, która zawiera liczby 2n chromosomów:

dane['n'] = dane['L_chr']/2
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof      n
mrówka              Myrmecia pilosula  zwierzęta      2     False    1.0
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True  700.0
pies                 Canis familiaris  zwierzęta     36      True   18.0
człowiek                 Homo sapiens  zwierzęta     46     False   23.0
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True    5.0

Jak widać, otrzymaliśmy nową kolumnę z danymi, jednak ma ona typ liczb zmiennoprzecinkowych co nie ma tu sensu. Przeprowadźmy zatem ponownie operację, tym razem ustawiając typ ‘int64`:

dane['n'] = (dane['L_chr']/2).astype('int64')
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof    n
mrówka              Myrmecia pilosula  zwierzęta      2     False    1
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True  700
pies                 Canis familiaris  zwierzęta     36      True   18
człowiek                 Homo sapiens  zwierzęta     46     False   23
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True    5

Niepotrzebną kolumnę można usunąć, używając metody drop(). Zwracana jest nowa ramka, zatem jeśli chcemy zachować starą nazwę, przypisujemy ramkę do niej. Parametr axis pozwala wybrać między kolumnami i rzędami. Wartość 1 oznacza kolumny, możemy też użyć wyrażenia axis = 'columns':

dane = dane.drop('n', axis = 1)
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True

Dodawanie, modyfikacja i usuwanie rzędów

Zacznijmy od odtworzenia ramki danych:

import pandas as pd
slownik_list = {'Gatunek': ['Myrmecia pilosula',
                'Ophioglossum reticulatum',
                'Canis familiaris',
                'Homo sapiens',
                'Arabidopsis thaliana'
              ],
                'Krolestwo': ['zwierzęta', 'rośliny', 'zwierzęta', 
                              'zwierzęta', 'rośliny'],
                'L_chr': [2, 1400, 36, 46, 10],
                'Autotrof': [False, True, True, False, True]
             }
dane = pd.DataFrame(slownik_list, 
                    index = ['mrówka', 'nasięźrzał', 'pies', 
                             'człowiek', 'rzodkiewnik'])
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True

Dodanie rzędu(ów), podobnie jak w przypadku kolumn, można wykonać na różne sposoby. Pierwszym jest zastosowanie funkcji concat(), pozwalającej połączyć ramki danych (dwie, lub więcej). Co ważne, funkcja concat() nie dołącza nowych danych do istniejącej ramki, ale łączy ramki, tworząc nową ramkę danych. Jeśli chcemy zachować starą nazwę ramki, to trzeba do niej przypisać wynik łączenia ramek. Tu jednak utworzymy nową ramkę:

# Funkcja concat() - łączenie ramek
nowy_rzad = pd.DataFrame({
             'Gatunek': 'Orobanche macrolepis', 
             'L_chr': 76, 
             'Krolestwo': 'rosliny',
             'Autotrof': False
            }, index=['zaraza'])
nowe_dane = pd.concat([dane, nowy_rzad])
print(nowe_dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True
zaraza           Orobanche macrolepis    rosliny     76     False

Zwróć uwagę, co się stanie, jeśli połączymy ramki nie posiadające etykiety rzędów, a tylko numery indeksów:

import pandas as pd
ramka_1 = pd.DataFrame({'Gatunek': ['Myrmecia pilosula',
               'Ophioglossum reticulatum',
              ],
              'L_chr': [2, 1400]
             })
ramka_2 = pd.DataFrame({'Gatunek': [
               'Canis familiaris',
               'Homo sapiens',
               'Arabidopsis thaliana'
              ],
              'L_chr': [36, 46, 10]
             })
dane_zlozone = pd.concat([ramka_1, ramka_2])
print(dane_zlozone)
                    Gatunek  L_chr
0         Myrmecia pilosula      2
1  Ophioglossum reticulatum   1400
0          Canis familiaris     36
1              Homo sapiens     46
2      Arabidopsis thaliana     10

Jak widać, numery indeksów, odziedziczone po pierwotnych ramkach, powtarzają się, co zazwyczaj nie jest pożądane.

Aby tego uniknąć, należy dodać argument ignore_index=True:

dane_zlozone = pd.concat([ramka_1, ramka_2], ignore_index=True)
print(dane_zlozone)
                    Gatunek  L_chr
0         Myrmecia pilosula      2
1  Ophioglossum reticulatum   1400
2          Canis familiaris     36
3              Homo sapiens     46
4      Arabidopsis thaliana     10

Wróćmy do ramki dane. Dodajmy do niej nowe dane (a właściwie stwórzmy nową ramkę z połączenia dane i nowej ramki):

nowy_rzad = pd.DataFrame({
             'Gatunek': 'Cuscuta campestris', 
             'Krolestwo': 'rośliny',
             'Autotrof': False
            }, index=['kanianka'])
nowe_dane = pd.concat([dane, nowy_rzad])
print(nowe_dane)
                              Gatunek  Krolestwo   L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzeta     2.0     False
nasiezrzal   Ophioglossum reticulatum    rosliny  1400.0      True
pies                 Canis familiaris  zwierzeta    36.0      True
czlowiek                 Homo sapiens  zwierzeta    46.0     False
rzodkiewnik      Arabidopsis thaliana    rosliny    10.0      True
kanianka           Cuscuta campestris    rosliny     NaN     False

Dodając Cuscuta campestris nie podaliśmy liczby chromosomów (tak naprawdę jest ona znana i wynosi 2n = 56). Zauważ, że w kolumnie L_chr pojawiła się wartość NaN (brak danych) a pozostałe liczby w kolumnie zmieniły się z całkowitych, na zmiennoprzecinkowe. Na razie jednak tylko wydrukowaliśmy uzupełnioną kopię ramki dane, nie zmieniliśmy jej.

Teraz użyjemy loc aby dodać dane dla drożdży. Tym razem efektem będzie modyfikacja oryginalnej ramki danych.

# loc
dane.loc['drożdże'] = {'Gatunek': 'Saccharomyces cerevisiae',
                       'L_chr': 32, 
                       'Krolestwo': 'grzyby',
                       'Autotrof': False}
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True
zaraza           Orobanche macrolepis    rosliny     76     False
drożdże      Saccharomyces cerevisiae     grzyby     32     False

Dzięki loc dodaliśmy na końcu rząd danych opatrzonych etykietą podaną w parze nawiasów kwadratowych.

loc można także użyć w celu podmiany danych:

dane.loc['zaraza'] = {'Gatunek': 'Orobanche cernua', 
             'L_chr': 38, 
             'Krolestwo': 'rosliny',
             'Autotrof': False
            }
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True
zaraza               Orobanche cernua    rosliny     38     False
drożdże      Saccharomyces cerevisiae     grzyby     32     False

iloc pozwala podmienić rząd danych w miejscu wskazanym przez indeks podany w parze nawiasów kwadratowych:

dane.iloc[5] = {'Gatunek': 'Cuscuta campestris',
                'Krolestwo': 'rośliny',
                'L_chr': 56,
                'Autotrof': False}
print(dane)
                              Gatunek  Krolestwo L_chr Autotrof
mrówka              Myrmecia pilosula  zwierzęta     2    False
nasięźrzał   Ophioglossum reticulatum    rośliny  1400     True
pies                 Canis familiaris  zwierzęta    36     True
człowiek                 Homo sapiens  zwierzęta    46    False
rzodkiewnik      Arabidopsis thaliana    rośliny    10     True
zaraza             Cuscuta campestris    rośliny    56    False
drożdże      Saccharomyces cerevisiae     grzyby    32    False

Zauważ, że choć zmieniły się dane, pozostała stara nazwa rzędu. Cuscuta to nie jest zaraza, ale kanianka, zatem powinniśmy zmienić etykietę:

dane.rename(index={'zaraza': 'kanianka'}, inplace=True)
print(dane)
                              Gatunek  Krolestwo L_chr Autotrof
mrówka              Myrmecia pilosula  zwierzęta     2    False
nasięźrzał   Ophioglossum reticulatum    rośliny  1400     True
pies                 Canis familiaris  zwierzęta    36     True
człowiek                 Homo sapiens  zwierzęta    46    False
rzodkiewnik      Arabidopsis thaliana    rośliny    10     True
kanianka           Cuscuta campestris    rośliny    56    False
drożdże      Saccharomyces cerevisiae     grzyby    32    False

Ustawienie inplace=True powoduje, że zmienia się ramka, ustawienie inplace=False (False jest domyślną wartością parametru inplace), powoduje, że ramka się nie zmienia, ale zwracana jest zmodyfikowana kopia ramki.

Rząd można usunąć za pomocą metody drop(), w tym przypadku jednak axis powinien przyjąć wartość 0, co odpowiada rzędom, można też wpisać rows, można także po prostu pominąć ten parametr, ponieważ jego domyślna wartość to właśnie 0:

print(dane.drop('pies', axis = 'rows'))
                              Gatunek  Krolestwo L_chr Autotrof
mrówka              Myrmecia pilosula  zwierzęta     2    False
nasięźrzał   Ophioglossum reticulatum    rośliny  1400     True
człowiek                 Homo sapiens  zwierzęta    46    False
rzodkiewnik      Arabidopsis thaliana    rośliny    10     True
kanianka           Cuscuta campestris    rośliny    56    False
drożdże      Saccharomyces cerevisiae     grzyby    32    False

Pobieranie i modyfikacja danych w konkretnych komórkach

Zacznijmy od utworzenia ramki (o ile to konieczne):

import pandas as pd
slownik_list = {'Gatunek': ['Myrmecia pilosula', 
                            'Ophioglossum reticulatum', 
                            'Canis familiaris', 
                            'Homo sapiens', 
                            'Arabidopsis thaliana', 
                            'Cuscuta campestris', 
                            'Saccharomyces cerevisiae'], 
                'Krolestwo': ['zwierzęta', 'rośliny', 'zwierzęta', 'zwierzęta', 
                              'rośliny', 'rośliny', 'grzyby'], 
                'L_chr': [2, 1400, 36, 46, 10, 56, 32], 
                'Autotrof': [False, True, True, False, True, False, False]}

dane = pd.DataFrame(slownik_list, 
                    index =  ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 
                              'rzodkiewnik', 'kanianka', 'drożdże'])
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies                 Canis familiaris  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True
kanianka           Cuscuta campestris    rośliny     56     False
drożdże      Saccharomyces cerevisiae     grzyby     32     False

Pobierać dane z komórek można na różne sposoby:

# Podajemy nazwę kolumny i indeks rzędu
print(dane['Gatunek'][2])
Canis familiaris
# Podajemy nazwę kolumny i etykietę rzędu
print(dane['Gatunek']['pies'])
Canis familiaris
# loc - podajemy etykietę rzędu i nazwę kolumny
print(dane.loc['pies', 'Gatunek'])
Canis familiaris
# iloc - Podajemy indeks rzędu i indeks kolumny
print(dane.iloc[2,0])
Canis familiaris

Modyfikować zawartość komórki możemy np. tak:

# Podajemy etykietę rzędu i nazwę kolumny
dane.at['pies', 'Gatunek'] = 'Pieskus domestucus'
print(dane)
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
nasięźrzał   Ophioglossum reticulatum    rośliny   1400      True
pies               Pieskus domestucus  zwierzęta     36      True
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True
kanianka           Cuscuta campestris    rośliny     56     False
drożdże      Saccharomyces cerevisiae     grzyby     32     False
# Podajemy etykietę rzędu i nazwę kolumny
dane.loc['pies', 'Gatunek'] = 'Pieszczochus kanapus'
print(dane.loc['pies'])
Gatunek     Pieszczochus kanapus 
Krolestwo           zwierzęta
L_chr                      36
Autotrof                 True
Name: pies, dtype: object

Przywróćmy właściwą nazwę psu, używając odwołania do indeksu rzędu i kolumny:

# Podajemy indeks rzędu i indeks kolumny
dane.iloc[2,0] = "Canis familiaris"
print(dane.loc['pies'])
Gatunek      Canis familiaris
Krolestwo           zwierzęta
L_chr                      36
Autotrof                 True
Name: pies, dtype: object

Przy okazji naprawmy rażący błąd w naszych danych: pies przecież nie jest autotrofem!

dane.iloc[2,3] = False
print(dane.loc['pies'])
Gatunek      Canis familiaris
Krolestwo           zwierzęta
L_chr                      36
Autotrof                False
Name: pies, dtype: object

Selekcja danych w kolumnach i rzędach

Tradycyjnie zacznijmy od odtworzenia ramki:

import pandas as pd
slownik_list = {'Gatunek': ['Myrmecia pilosula', 
                            'Ophioglossum reticulatum', 
                            'Canis familiaris', 
                            'Homo sapiens', 
                            'Arabidopsis thaliana', 
                            'Cuscuta campestris', 
                            'Saccharomyces cerevisiae'], 
                'Krolestwo': ['zwierzęta', 'rośliny', 'zwierzęta', 'zwierzęta', 
                              'rośliny', 'rośliny', 'grzyby'], 
                'L_chr': [2, 1400, 36, 46, 10, 56, 32], 
                'Autotrof': [False, True, False, False, True, False, False]}

dane = pd.DataFrame(slownik_list, 
                    index =  ['mrówka', 'nasięźrzał', 'pies', 'człowiek', 
                              'rzodkiewnik', 'kanianka', 'drożdże'])

Stosunkowo łatwo można wyselekcjonować kilka wybranych kolumn z ramki:

Z użyciem nazw kolumn:

print(dane[['Gatunek', 'Autotrof']])
                              Gatunek  Autotrof
mrówka              Myrmecia pilosula     False
nasięźrzał   Ophioglossum reticulatum      True
pies                 Canis familiaris     False
człowiek                 Homo sapiens     False
rzodkiewnik      Arabidopsis thaliana      True
kanianka           Cuscuta campestris     False
drożdże      Saccharomyces cerevisiae     False

Z użyciem indeksów:

print(dane[dane.columns[[0,3]]])
                              Gatunek  Autotrof
mrówka              Myrmecia pilosula     False
nasięźrzał   Ophioglossum reticulatum      True
pies                 Canis familiaris     False
człowiek                 Homo sapiens     False
rzodkiewnik      Arabidopsis thaliana      True
kanianka           Cuscuta campestris     False
drożdże      Saccharomyces cerevisiae     False

Także można podać zakres kolumn:

print(dane[dane.columns[0:3]])
                              Gatunek  Krolestwo  L_chr
mrówka              Myrmecia pilosula  zwierzęta      2
nasięźrzał   Ophioglossum reticulatum    rośliny   1400
pies                 Canis familiaris  zwierzęta     36
człowiek                 Homo sapiens  zwierzęta     46
rzodkiewnik      Arabidopsis thaliana    rośliny     10
kanianka           Cuscuta campestris    rośliny     56
drożdże      Saccharomyces cerevisiae     grzyby     32

W wybieraniu wielu rzędów przydadzą nam się nasi znajomi: loc oraz iloc ale można też użyć zakresu indeksów w parze nawiasów kwadratowych:

print(dane[2:5])
                          Gatunek  Krolestwo  L_chr  Autotrof
pies             Canis familiaris  zwierzęta     36     False
człowiek             Homo sapiens  zwierzęta     46     False
rzodkiewnik  Arabidopsis thaliana    rośliny     10      True
print(dane.loc[['pies', 'mrówka', 'drożdże']])
                          Gatunek  Krolestwo  L_chr  Autotrof
pies             Canis familiaris  zwierzęta     36     False
mrówka          Myrmecia pilosula  zwierzęta      2     False
drożdże  Saccharomyces cerevisiae     grzyby     32     False
print(dane.iloc[[2, 5, 1]])
                             Gatunek  Krolestwo  L_chr  Autotrof
pies                Canis familiaris  zwierzęta     36     False
kanianka          Cuscuta campestris    rośliny     56     False
nasięźrzał  Ophioglossum reticulatum    rośliny   1400      True
print(dane.iloc[2:5])
                          Gatunek  Krolestwo  L_chr  Autotrof
pies             Canis familiaris  zwierzęta     36     False
człowiek             Homo sapiens  zwierzęta     46     False
rzodkiewnik  Arabidopsis thaliana    rośliny     10      True

Można też zawężać wybór do wybranych kolumn i rzędów na raz.

Za pomocą etykiet wierszy i nazw kolumn (loc)

print(dane.loc[['pies', 'mrówka', 'kanianka'], 
               ['Krolestwo', 'Autotrof']])
          Krolestwo  Autotrof
pies      zwierzęta     False
mrówka    zwierzęta     False
kanianka    rośliny     False
print(dane.loc['pies': 'kanianka', 
               :'Krolestwo'])
                          Gatunek  Krolestwo
pies             Canis familiaris  zwierzęta
człowiek             Homo sapiens  zwierzęta
rzodkiewnik  Arabidopsis thaliana    rośliny
kanianka       Cuscuta campestris    rośliny

A także indeksów (iloc)

print(dane.iloc[[2, 0, 5], [2, 0]])
          L_chr             Gatunek
pies         36    Canis familiaris
mrówka        2   Myrmecia pilosula
kanianka     56  Cuscuta campestris

Selekcja danych może się także odbywać na podstawie wartości:

print(dane[dane['L_chr'] > 40])
                             Gatunek  Krolestwo  L_chr  Autotrof
nasięźrzał  Ophioglossum reticulatum    rośliny   1400      True
człowiek                Homo sapiens  zwierzęta     46     False
kanianka          Cuscuta campestris    rośliny     56     False
print(dane[dane['Krolestwo'] == 'rośliny'])
                              Gatunek Krolestwo  L_chr  Autotrof
nasięźrzał   Ophioglossum reticulatum   rośliny   1400      True
rzodkiewnik      Arabidopsis thaliana   rośliny     10      True
kanianka           Cuscuta campestris   rośliny     56     False

Można też tworzyć bardziej złożone wyrażenia, używając operatorów & (and) czy | (or).

print(dane[(dane['Krolestwo'] == 'rośliny') & (dane['Autotrof'] == True)])
                              Gatunek Krolestwo  L_chr  Autotrof
nasięźrzał   Ophioglossum reticulatum   rośliny   1400      True
rzodkiewnik      Arabidopsis thaliana   rośliny     10      True

Znak tyldy (~) odwraca wartości boolowskie w serii, zatem możemy go użyć w celu odwrócenia filtrowania wartości, na przeciwny:

print(dane[(dane['Krolestwo'] == 'rośliny') & (~dane['Autotrof'] == True)])
                     Gatunek Krolestwo  L_chr  Autotrof
kanianka  Cuscuta campestris   rośliny     56     False
print(dane[(dane['Krolestwo'] == 'zwierzęta') | (dane['L_chr'] < 40)])
                              Gatunek  Krolestwo  L_chr  Autotrof
mrówka              Myrmecia pilosula  zwierzęta      2     False
pies                 Canis familiaris  zwierzęta     36     False
człowiek                 Homo sapiens  zwierzęta     46     False
rzodkiewnik      Arabidopsis thaliana    rośliny     10      True
drożdże      Saccharomyces cerevisiae     grzyby     32     False

W końcu możemy połączyć selekcję rzędów i kolumn na podstawie indeksów (lub nazw) oraz wartości w komórkach:

print(dane.iloc[:, [0, 3]][dane['Autotrof'] == False])
                           Gatunek  Autotrof
mrówka           Myrmecia pilosula     False
pies              Canis familiaris     False
człowiek              Homo sapiens     False
kanianka        Cuscuta campestris     False
drożdże   Saccharomyces cerevisiae     False
print(dane.loc[:, ['Krolestwo', 'Autotrof']][dane['Autotrof'] == False])
          Krolestwo  Autotrof
mrówka    zwierzęta     False
pies      zwierzęta     False
człowiek  zwierzęta     False
kanianka    rośliny     False
drożdże      grzyby     False

Pobieranie fragmentu ramki przy pomocy loc i iloc jest bardziej efektywne.

Przy pobieraniu wycinka ramki, a następnie próbie zmiany w niej wartości, możemy natrafić na pewien problem:

wybrane = dane[dane['Autotrof'] == False]
wybrane['Autotrof']['pies'] = True
print(wybrane)
                           Gatunek  Krolestwo  L_chr  Autotrof
mrówka           Myrmecia pilosula  zwierzęta      2     False
pies              Canis familiaris  zwierzęta     36      True
człowiek              Homo sapiens  zwierzęta     46     False
kanianka        Cuscuta campestris    rośliny     56     False
drożdże   Saccharomyces cerevisiae     grzyby     32     False



/tmp/ipykernel_49770/682686734.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  wybrane['Autotrof']['pies'] = True

Jak widać operacja się w zasadzie udała ale otrzymaliśmy ostrzeżenie (można więcej na ten temat poczytać w wyświetlanym linku). Aby uniknąć podobnych ostrzeżeń, powinniśmy zrobić dwie rzeczy. Po pierwsze skopiować wyselekcjonowane dane do osobnej ramki. Po drugie użyć loc (lub iloc) do modyfikacji danych:

wybrane = dane[dane['Autotrof'] == False].copy()
wybrane.loc['pies','Autotrof'] = True
print(wybrane)
                           Gatunek  Krolestwo  L_chr  Autotrof
mrówka           Myrmecia pilosula  zwierzęta      2     False
pies              Canis familiaris  zwierzęta     36      True
człowiek              Homo sapiens  zwierzęta     46     False
kanianka        Cuscuta campestris    rośliny     56     False
drożdże   Saccharomyces cerevisiae     grzyby     32     False

Oczywiście pamiętajmy, że pies NIE jest autotrofem ;-)

Z pobrane serii danych możemy, podobnie jak to robiliśmy na obiektach Series, wyliczyć różne wartości, używając odpowiednich funkcji, lub metod, np:

chrom_2n = dane['L_chr']
print(chrom_2n)
mrówka            2
nasięźrzał     1400
pies             36
człowiek         46
rzodkiewnik      10
kanianka         56
drożdże          32
Name: L_chr, dtype: int64
print(f'suma:       {chrom_2n.sum()}')
print(f'największa: {chrom_2n.max()}')
print(f'średnia:    {chrom_2n.mean()}')
print(f'mediana:    {chrom_2n.median()}')
suma:       1582
największa: 1400
średnia:    226.0
mediana:    36.0

Można też przeprowadzić te operacje bezpośrednio na kolumnie ramki danych:

print(f'suma:       {dane["L_chr"].sum()}')
print(f'największa: {dane["L_chr"].max()}')
print(f'średnia:    {dane["L_chr"].mean()}')
print(f'mediana:    {dane["L_chr"].median()}')
suma:       1582
najwieksza: 1400
srednia:    226.0
mediana:    36.0

Transpozycja ramki

Ramki (podobnie zresztą jak tablice ndarray) można łatwo przekształcić tak, że kolumny stają się wierszami a wiersze kolumnami. Taką operację nazywamy transponowaniem. Można to wykonać, wykorzystując atrybut T Odtwórzmy ramkę danych, tym razem nieco mniejszą:

import pandas as pd
slownik_list = {'Gatunek': ['M. pilosula', 
                            'C. familiaris',
                            'C. campestris', 
                            'S. cerevisiae'],
                'Krolestwo': ['zwierzęta', 'zwierzęta', 
                              'rośliny', 'grzyby'], 
                'L_chr': [2, 36, 56, 32], 
                'Autotrof': [False, False, False, False]}

dane = pd.DataFrame(slownik_list, 
                    index =  ['mrówka', 'pies', 
                              'kanianka', 'drożdże'])
print(dane)
                Gatunek  Krolestwo  L_chr  Autotrof
mrówka      M. pilosula  zwierzęta      2     False
pies      C. familiaris  zwierzęta     36     False
kanianka  C. campestris    rośliny     56     False
drożdże   S. cerevisiae     grzyby     32     False
dane_t = dane.T
print(dane_t)
                mrówka           pies       kanianka        drożdże
Gatunek    M. pilosula  C. familiaris  C. campestris  S. cerevisiae
Krolestwo    zwierzęta      zwierzęta        rośliny         grzyby
L_chr                2             36             56             32
Autotrof         False          False          False          False
Last updated on 26 Mar 2024
Published on 26 Mar 2024