Основы программирования в Python

Алла Тамбовцева, НИУ ВШЭ

Датафреймы pandas

Продолжим работать с таблицами в Python. Для начала импортируем саму библиотеку.

In [1]:
import pandas as pd

Загрузим таблицу:

In [2]:
df = pd.read_csv("http://math-info.hse.ru/f/2017-18/py-prog/scores2.csv")

И посмотрим на её структуру более внимательно. Выберем первый столбец с id:

In [3]:
df['id']
Out[3]:
0     М141БПЛТЛ024
1     М141БПЛТЛ031
2     М141БПЛТЛ075
3     М141БПЛТЛ017
4     М141БПЛТЛ069
5     М141БПЛТЛ072
6     М141БПЛТЛ020
7     М141БПЛТЛ026
8     М141БПЛТЛ073
9     М141БПЛТЛ078
10    М141БПЛТЛ060
11    М141БПЛТЛ040
12    М141БПЛТЛ065
13    М141БПЛТЛ053
14    М141БПЛТЛ015
15    М141БПЛТЛ021
16    М141БПЛТЛ018
17    М141БПЛТЛ039
18    М141БПЛТЛ036
19    М141БПЛТЛ049
20        06114043
21    М141БПЛТЛ048
22    М141БПЛТЛ034
23    М141БПЛТЛ045
24    М141БПЛТЛ033
25    М141БПЛТЛ083
26    М141БПЛТЛ008
27    М141БПЛТЛ001
28    М141БПЛТЛ038
29    М141БПЛТЛ052
30    М141БПЛТЛ011
31    М141БПЛТЛ004
32    М141БПЛТЛ010
33    М141БПЛТЛ071
34    М141БПЛТЛ035
35    М141БПЛТЛ030
36    М141БПЛТЛ070
37    М141БПЛТЛ051
38    М141БПЛТЛ046
39    М141БПЛТЛ047
40    М141БПЛТЛ063
41    М141БПЛТЛ029
42    М141БПЛТЛ064
43    М141БПЛТЛ076
44    М141БПЛТЛ062
45    М141БПЛТЛ074
46       130232038
47    М141БПЛТЛ023
48    М141БПЛТЛ054
49    М141БПЛТЛ012
50    М141БПЛТЛ006
51    М141БПЛТЛ055
52    М141БПЛТЛ007
53    М141БПЛТЛ050
54    М141БПЛТЛ066
55    М141БПЛТЛ043
56    М141БПЛТЛ084
57    М141БПЛТЛ005
58    М141БПЛТЛ044
59        13051038
Name: id, dtype: object

Столбец датафрейма df имеет особый тип Series. Внешне Series отличается от обычного списка значений, потому что, во-первых, при вызове столбца на экран выводятся не только сами элементы, но их номер (номер строки), а во-вторых, на экран выводится строка с названием столбца (Name: id) и его тип (dtype: object, текстовый). Первая особенность роднит Series со словарями: он представляет собой пары ключ-значение, то есть номер-значение. Вторая особенность роднит Series с массивами numpy: элементы обычно должны быть одного типа.

Библиотеку numpy мы еще не обсуждали, но обязательно обсудим позже, так как во многих задачах использовать массивы numpy гораздо удобнее, чем списки.

Можно вывести первые или последние строки таблицы, используя методы .head() и .tail().

In [4]:
df.head()
Out[4]:
id catps mstat soc econ eng polth mstat2 phist law phil polsoc ptheo preg compp game wpol male
0 М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
1 М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
2 М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
3 М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
4 М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1
In [5]:
df.tail()
Out[5]:
id catps mstat soc econ eng polth mstat2 phist law phil polsoc ptheo preg compp game wpol male
55 М141БПЛТЛ043 5 5 6 5 8 5 6 5.0 6 4 5 4.0 5 NaN 4 6 0
56 М141БПЛТЛ084 6 7 8 4 8 5 5 NaN 8 4 4 4.0 4 4.0 6 7 1
57 М141БПЛТЛ005 5 7 5 5 7 4 7 4.0 5 4 5 5.0 4 4.0 4 8 1
58 М141БПЛТЛ044 4 5 7 4 6 4 4 5.0 4 4 4 4.0 6 NaN 5 5 1
59 13051038 5 4 4 4 9 5 5 5.0 5 4 4 NaN 7 4.0 4 4 1

Внимание: это просто первые и последние строки таблицы «как есть». Никакой сортировки не происходит!

По умолчанию эти методы выводят пять строк, но при желании это легко изменить. Достаточно в скобках указать желаемое число строк.

In [6]:
df.head(10) # первые 10 строк
Out[6]:
id catps mstat soc econ eng polth mstat2 phist law phil polsoc ptheo preg compp game wpol male
0 М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
1 М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
2 М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
3 М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
4 М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1
5 М141БПЛТЛ072 10 9 8 10 9 8 9 8.0 8 10 9 7.0 8 8.0 9 9 0
6 М141БПЛТЛ020 8 7 7 6 9 10 8 8.0 7 7 9 7.0 8 6.0 8 9 1
7 М141БПЛТЛ026 7 10 8 7 10 7 9 8.0 8 8 8 8.0 8 7.0 7 8 0
8 М141БПЛТЛ073 7 9 8 8 9 8 9 8.0 8 9 9 7.0 7 6.0 10 9 1
9 М141БПЛТЛ078 6 6 9 5 6 10 7 6.0 8 6 9 6.0 8 8.0 6 7 0

Давайте кое-что подкорректруем. Сделаем так, чтобы строки в таблице назывались в соответствии с id. Другими словами, сделаем так, чтобы первый столбец считался индексом строки:

In [7]:
df = pd.read_csv("http://math-info.hse.ru/f/2017-18/py-prog/scores2.csv", index_col = 0)
In [8]:
df.head() # теперь так
Out[8]:
catps mstat soc econ eng polth mstat2 phist law phil polsoc ptheo preg compp game wpol male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1

Иногда такой подход может быть полезен. Представьте, что все переменные в таблице, кроме id, измерены в количественной шкале, и мы планируем реализовать на них статистический метод, который работает исключительно с числовыми данными. Если мы просто выкинем столбец с id, мы потеряем информацию о наблюдении, если мы его оставим, нам придется собирать в отдельную таблицу показатели, к которым будем применять метод, так как сохраненный в исходной таблице текст будет мешать. Если же мы назовем строки в соответствии с id, мы убьем сразу двух зайцев: избавимся от столбца с текстом и не потеряем информацию о наблюдении (код, имя респондента, название страны и прочее).

Когда таблица большая, увидеть все столбцы разом не получится. Поэтому полезно знать, как получить список названий столбцов.

In [9]:
df.columns
Out[9]:
Index(['catps', 'mstat', 'soc', 'econ', 'eng', 'polth', 'mstat2', 'phist',
       'law', 'phil', 'polsoc', 'ptheo', 'preg', 'compp', 'game', 'wpol',
       'male'],
      dtype='object')

Обратите внимание: полученный объект не является обычным списком:

In [10]:
type(df.columns) # это Index из pandas
Out[10]:
pandas.core.indexes.base.Index

Чтобы получить список названий, достаточно сконвертировать тип с помощью привычного list():

In [11]:
c = list(df.columns)
print(c)
['catps', 'mstat', 'soc', 'econ', 'eng', 'polth', 'mstat2', 'phist', 'law', 'phil', 'polsoc', 'ptheo', 'preg', 'compp', 'game', 'wpol', 'male']

Аналогичная история со строками:

In [12]:
df.index
Out[12]:
Index(['М141БПЛТЛ024', 'М141БПЛТЛ031', 'М141БПЛТЛ075', 'М141БПЛТЛ017',
       'М141БПЛТЛ069', 'М141БПЛТЛ072', 'М141БПЛТЛ020', 'М141БПЛТЛ026',
       'М141БПЛТЛ073', 'М141БПЛТЛ078', 'М141БПЛТЛ060', 'М141БПЛТЛ040',
       'М141БПЛТЛ065', 'М141БПЛТЛ053', 'М141БПЛТЛ015', 'М141БПЛТЛ021',
       'М141БПЛТЛ018', 'М141БПЛТЛ039', 'М141БПЛТЛ036', 'М141БПЛТЛ049',
       '06114043', 'М141БПЛТЛ048', 'М141БПЛТЛ034', 'М141БПЛТЛ045',
       'М141БПЛТЛ033', 'М141БПЛТЛ083', 'М141БПЛТЛ008', 'М141БПЛТЛ001',
       'М141БПЛТЛ038', 'М141БПЛТЛ052', 'М141БПЛТЛ011', 'М141БПЛТЛ004',
       'М141БПЛТЛ010', 'М141БПЛТЛ071', 'М141БПЛТЛ035', 'М141БПЛТЛ030',
       'М141БПЛТЛ070', 'М141БПЛТЛ051', 'М141БПЛТЛ046', 'М141БПЛТЛ047',
       'М141БПЛТЛ063', 'М141БПЛТЛ029', 'М141БПЛТЛ064', 'М141БПЛТЛ076',
       'М141БПЛТЛ062', 'М141БПЛТЛ074', '130232038', 'М141БПЛТЛ023',
       'М141БПЛТЛ054', 'М141БПЛТЛ012', 'М141БПЛТЛ006', 'М141БПЛТЛ055',
       'М141БПЛТЛ007', 'М141БПЛТЛ050', 'М141БПЛТЛ066', 'М141БПЛТЛ043',
       'М141БПЛТЛ084', 'М141БПЛТЛ005', 'М141БПЛТЛ044', '13051038'],
      dtype='object', name='id')

Переименование столбцов и строк

Раз названия столбцов можно вывести в виде некоторого перечня, то этот перечень можно редактировать. Посмотрим на названия столбцов ещё раз.

In [13]:
df.columns
Out[13]:
Index(['catps', 'mstat', 'soc', 'econ', 'eng', 'polth', 'mstat2', 'phist',
       'law', 'phil', 'polsoc', 'ptheo', 'preg', 'compp', 'game', 'wpol',
       'male'],
      dtype='object')

Давайте переименуем переменную catps в cps, чтобы думать о политической науке, а не о котах :) Для этого сохраним названия в список my_cols и изменим в списке первый элемент:

In [14]:
my_cols = list(df.columns)
my_cols[0] = "cps"
In [15]:
df.columns = my_cols # сохраним изменения в самой базе df
df.columns # все обновилось!
Out[15]:
Index(['cps', 'mstat', 'soc', 'econ', 'eng', 'polth', 'mstat2', 'phist', 'law',
       'phil', 'polsoc', 'ptheo', 'preg', 'compp', 'game', 'wpol', 'male'],
      dtype='object')

Обратите внимание: для того, чтобы изменить одно или несколько названий, совсем необязательно создавать новый список «с нуля». Достаточно определить индексы нужных элементов и поправить только необходимые названия.

Точно так же можно было поступить со строками. Но давайте лучше попробуем внести изменения в названия всех столбцов: сделаем так, чтобы все названия столбцов начинались с большой буквы. Для начала напишем функцию, которая принимает на вход датафрейм, обращается к его столбцам и переименовывает их, делая первую букву заглавной.

In [16]:
def rename_cols(df):
    oldnames = list(df.columns) # список старых названий 
    newnames = [i.capitalize() for i in oldnames] # список новых названий
    df.columns = newnames # сохранение изменений
    return df

Теперь применим нашу функцию rename_cols() к базе df:

In [17]:
df2 = rename_cols(df)
df2.head()
Out[17]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1

Кажется, что таким способом мы сохранили изменения в новой базе df2, а старую базу df не тронули. Однако, если мы посмотрим на базу df, мы увидим, что она тоже изменилась!

In [18]:
df.head()
Out[18]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1

Почему это произошло? Потому что датафреймы являются изменяемой структурой данных (да-да, как списки). Поэтому, применяя методы к объекту типа DataFrame, мы меняем исходный датафрейм, и к этому надо быть готовым. Если вы не планируете вносить изменения в исходную базу, имеет смысл сделать её копию и работать с ней. Например, вот так:

In [19]:
# метод copy
df_new = df.copy()

# вносим изменения в df_new - переименовываем один столбец
new_cols = list(df_new.columns)
new_cols[1] = "Matstat"
df_new.columns = new_cols
In [20]:
# сравниваем

print(df.head(2))
print("\n") # для пустой строчки между df и df_new
print(df_new.head(2))
              Cps  Mstat  Soc  Econ  Eng  Polth  Mstat2  Phist  Law  Phil  \
id                                                                          
М141БПЛТЛ024    7      9    8     8    9      8      10    8.0    7     9   
М141БПЛТЛ031    8     10   10    10   10     10      10    9.0    9    10   

              Polsoc  Ptheo  Preg  Compp  Game  Wpol  Male  
id                                                          
М141БПЛТЛ024       9    7.0     8    8.0     6    10     1  
М141БПЛТЛ031      10    9.0     8    8.0     9    10     1  


              Cps  Matstat  Soc  Econ  Eng  Polth  Mstat2  Phist  Law  Phil  \
id                                                                            
М141БПЛТЛ024    7        9    8     8    9      8      10    8.0    7     9   
М141БПЛТЛ031    8       10   10    10   10     10      10    9.0    9    10   

              Polsoc  Ptheo  Preg  Compp  Game  Wpol  Male  
id                                                          
М141БПЛТЛ024       9    7.0     8    8.0     6    10     1  
М141БПЛТЛ031      10    9.0     8    8.0     9    10     1  

Обратите внимание: создать копию обычным присваиванием не получится, код вида df_new = df создаст новую ссылку на датафрейм, но не новый датафрейм. Поэтому при изменении df_new база df также изменится (вспомните историю о коварстве списков).

Выбор столбцов и строк таблицы

Выбор столбцов по названию

Часто удобнее всего выбирать столбец по названию. Для этого достаточно указать название столбца в квадратных скобках (и обязательно в кавычках, так как название является строкой):

In [21]:
df['Mstat']
Out[21]:
id
М141БПЛТЛ024     9
М141БПЛТЛ031    10
М141БПЛТЛ075     9
М141БПЛТЛ017     9
М141БПЛТЛ069    10
М141БПЛТЛ072     9
М141БПЛТЛ020     7
М141БПЛТЛ026    10
М141БПЛТЛ073     9
М141БПЛТЛ078     6
М141БПЛТЛ060     8
М141БПЛТЛ040     9
М141БПЛТЛ065     9
М141БПЛТЛ053     7
М141БПЛТЛ015     9
М141БПЛТЛ021     9
М141БПЛТЛ018     7
М141БПЛТЛ039     8
М141БПЛТЛ036    10
М141БПЛТЛ049     7
06114043         8
М141БПЛТЛ048     6
М141БПЛТЛ034     9
М141БПЛТЛ045     8
М141БПЛТЛ033     9
М141БПЛТЛ083     5
М141БПЛТЛ008     8
М141БПЛТЛ001     7
М141БПЛТЛ038     9
М141БПЛТЛ052     7
М141БПЛТЛ011     6
М141БПЛТЛ004     7
М141БПЛТЛ010     6
М141БПЛТЛ071     9
М141БПЛТЛ035     6
М141БПЛТЛ030     6
М141БПЛТЛ070     5
М141БПЛТЛ051     9
М141БПЛТЛ046     7
М141БПЛТЛ047     8
М141БПЛТЛ063     5
М141БПЛТЛ029     8
М141БПЛТЛ064     8
М141БПЛТЛ076     7
М141БПЛТЛ062     7
М141БПЛТЛ074     6
130232038        7
М141БПЛТЛ023     9
М141БПЛТЛ054     8
М141БПЛТЛ012     6
М141БПЛТЛ006     5
М141БПЛТЛ055     5
М141БПЛТЛ007     7
М141БПЛТЛ050     6
М141БПЛТЛ066    10
М141БПЛТЛ043     5
М141БПЛТЛ084     7
М141БПЛТЛ005     7
М141БПЛТЛ044     5
13051038         4
Name: Mstat, dtype: int64

Ещё столбец можно выбрать, не используя квадратные скобки, а просто указав его название через точку:

In [22]:
df.Mstat
Out[22]:
id
М141БПЛТЛ024     9
М141БПЛТЛ031    10
М141БПЛТЛ075     9
М141БПЛТЛ017     9
М141БПЛТЛ069    10
М141БПЛТЛ072     9
М141БПЛТЛ020     7
М141БПЛТЛ026    10
М141БПЛТЛ073     9
М141БПЛТЛ078     6
М141БПЛТЛ060     8
М141БПЛТЛ040     9
М141БПЛТЛ065     9
М141БПЛТЛ053     7
М141БПЛТЛ015     9
М141БПЛТЛ021     9
М141БПЛТЛ018     7
М141БПЛТЛ039     8
М141БПЛТЛ036    10
М141БПЛТЛ049     7
06114043         8
М141БПЛТЛ048     6
М141БПЛТЛ034     9
М141БПЛТЛ045     8
М141БПЛТЛ033     9
М141БПЛТЛ083     5
М141БПЛТЛ008     8
М141БПЛТЛ001     7
М141БПЛТЛ038     9
М141БПЛТЛ052     7
М141БПЛТЛ011     6
М141БПЛТЛ004     7
М141БПЛТЛ010     6
М141БПЛТЛ071     9
М141БПЛТЛ035     6
М141БПЛТЛ030     6
М141БПЛТЛ070     5
М141БПЛТЛ051     9
М141БПЛТЛ046     7
М141БПЛТЛ047     8
М141БПЛТЛ063     5
М141БПЛТЛ029     8
М141БПЛТЛ064     8
М141БПЛТЛ076     7
М141БПЛТЛ062     7
М141БПЛТЛ074     6
130232038        7
М141БПЛТЛ023     9
М141БПЛТЛ054     8
М141БПЛТЛ012     6
М141БПЛТЛ006     5
М141БПЛТЛ055     5
М141БПЛТЛ007     7
М141БПЛТЛ050     6
М141БПЛТЛ066    10
М141БПЛТЛ043     5
М141БПЛТЛ084     7
М141БПЛТЛ005     7
М141БПЛТЛ044     5
13051038         4
Name: Mstat, dtype: int64

Однако такой способ не универсален. В случае, если в названии столбца используются недопустимые для переменных символы (пробелы, тире, кириллические буквы), этот метод не подойдет.

Если нам нужно выбрать более одного столбца, то названия столбцов указываются внутри списка – появляются двойные квадратные скобки:

In [23]:
df2[["Soc", "Polsoc"]]
Out[23]:
Soc Polsoc
id
М141БПЛТЛ024 8 9
М141БПЛТЛ031 10 10
М141БПЛТЛ075 9 9
М141БПЛТЛ017 8 9
М141БПЛТЛ069 10 9
М141БПЛТЛ072 8 9
М141БПЛТЛ020 7 9
М141БПЛТЛ026 8 8
М141БПЛТЛ073 8 9
М141БПЛТЛ078 9 9
М141БПЛТЛ060 7 8
М141БПЛТЛ040 8 8
М141БПЛТЛ065 8 10
М141БПЛТЛ053 7 8
М141БПЛТЛ015 7 7
М141БПЛТЛ021 8 6
М141БПЛТЛ018 9 8
М141БПЛТЛ039 9 9
М141БПЛТЛ036 7 7
М141БПЛТЛ049 6 9
06114043 10 9
М141БПЛТЛ048 8 8
М141БПЛТЛ034 7 6
М141БПЛТЛ045 8 8
М141БПЛТЛ033 8 8
М141БПЛТЛ083 6 7
М141БПЛТЛ008 8 9
М141БПЛТЛ001 7 8
М141БПЛТЛ038 6 8
М141БПЛТЛ052 7 7
М141БПЛТЛ011 8 7
М141БПЛТЛ004 6 6
М141БПЛТЛ010 7 8
М141БПЛТЛ071 7 7
М141БПЛТЛ035 7 7
М141БПЛТЛ030 6 5
М141БПЛТЛ070 6 5
М141БПЛТЛ051 8 6
М141БПЛТЛ046 7 7
М141БПЛТЛ047 6 6
М141БПЛТЛ063 6 5
М141БПЛТЛ029 8 8
М141БПЛТЛ064 6 4
М141БПЛТЛ076 8 8
М141БПЛТЛ062 7 6
М141БПЛТЛ074 7 8
130232038 6 5
М141БПЛТЛ023 6 7
М141БПЛТЛ054 6 8
М141БПЛТЛ012 7 7
М141БПЛТЛ006 6 7
М141БПЛТЛ055 6 6
М141БПЛТЛ007 7 6
М141БПЛТЛ050 6 6
М141БПЛТЛ066 7 6
М141БПЛТЛ043 6 5
М141БПЛТЛ084 8 4
М141БПЛТЛ005 5 5
М141БПЛТЛ044 7 4
13051038 4 4

Если нам нужно несколько столбцов подряд, начиная с одного названия и заканчивая другим, можно воспользоваться методом .loc:

In [24]:
df.loc[:, 'Econ' : 'Law']
Out[24]:
Econ Eng Polth Mstat2 Phist Law
id
М141БПЛТЛ024 8 9 8 10 8.0 7
М141БПЛТЛ031 10 10 10 10 9.0 9
М141БПЛТЛ075 10 9 10 9 8.0 9
М141БПЛТЛ017 8 9 9 10 6.0 9
М141БПЛТЛ069 10 10 10 9 8.0 8
М141БПЛТЛ072 10 9 8 9 8.0 8
М141БПЛТЛ020 6 9 10 8 8.0 7
М141БПЛТЛ026 7 10 7 9 8.0 8
М141БПЛТЛ073 8 9 8 9 8.0 8
М141БПЛТЛ078 5 6 10 7 6.0 8
М141БПЛТЛ060 7 9 8 8 5.0 7
М141БПЛТЛ040 6 9 7 8 6.0 9
М141БПЛТЛ065 4 8 8 7 9.0 8
М141БПЛТЛ053 5 9 8 7 8.0 8
М141БПЛТЛ015 6 9 7 9 4.0 7
М141БПЛТЛ021 8 9 8 8 7.0 7
М141БПЛТЛ018 7 9 7 8 6.0 6
М141БПЛТЛ039 8 8 8 6 8.0 7
М141БПЛТЛ036 8 8 6 9 4.0 8
М141БПЛТЛ049 6 8 6 8 4.0 8
06114043 5 8 8 8 10.0 7
М141БПЛТЛ048 6 9 6 4 4.0 6
М141БПЛТЛ034 6 9 6 8 6.0 7
М141БПЛТЛ045 7 8 6 7 6.0 7
М141БПЛТЛ033 7 9 7 9 7.0 7
М141БПЛТЛ083 5 8 7 6 5.0 7
М141БПЛТЛ008 9 8 10 9 8.0 9
М141БПЛТЛ001 4 10 7 7 6.0 8
М141БПЛТЛ038 4 9 6 7 6.0 7
М141БПЛТЛ052 7 8 6 6 6.0 8
М141БПЛТЛ011 6 9 6 6 5.0 6
М141БПЛТЛ004 6 8 6 6 5.0 5
М141БПЛТЛ010 6 9 7 7 6.0 7
М141БПЛТЛ071 7 9 6 8 4.0 6
М141БПЛТЛ035 6 8 5 5 4.0 6
М141БПЛТЛ030 6 7 6 6 4.0 8
М141БПЛТЛ070 4 8 6 5 5.0 6
М141БПЛТЛ051 6 8 7 6 7.0 6
М141БПЛТЛ046 4 7 5 8 5.0 7
М141БПЛТЛ047 4 7 5 9 5.0 6
М141БПЛТЛ063 4 8 4 4 4.0 5
М141БПЛТЛ029 7 9 5 6 7.0 6
М141БПЛТЛ064 7 6 6 8 4.0 6
М141БПЛТЛ076 6 8 6 6 6.0 8
М141БПЛТЛ062 6 9 6 6 5.0 6
М141БПЛТЛ074 4 7 6 5 6.0 6
130232038 5 8 4 8 4.0 8
М141БПЛТЛ023 8 9 6 9 4.0 7
М141БПЛТЛ054 4 8 6 4 4.0 6
М141БПЛТЛ012 4 10 6 5 4.0 7
М141БПЛТЛ006 5 8 5 5 5.0 6
М141БПЛТЛ055 4 7 7 4 8.0 5
М141БПЛТЛ007 6 7 6 7 4.0 5
М141БПЛТЛ050 6 8 4 5 4.0 5
М141БПЛТЛ066 7 9 5 8 4.0 6
М141БПЛТЛ043 5 8 5 6 5.0 6
М141БПЛТЛ084 4 8 5 5 NaN 8
М141БПЛТЛ005 5 7 4 7 4.0 5
М141БПЛТЛ044 4 6 4 4 5.0 4
13051038 4 9 5 5 5.0 5

Откуда в квадратных скобках взялось двоеточие? Дело в том, что метод .loc – более универсальный, и позволяет выбирать не только столбцы, но и строки. При этом нужные строки указываются на первом месте, а столбцы – на втором. Когда мы пишем .loc[:, 1], мы сообщаем Python, что нам нужны все строки (:) и столбцы, начиная с Econ и до Law включительно.

Внимание: выбор столбцов по названиям через двоеточие очень напоминает срезы (slices) в списках. Но есть важное отличие. В случае текстовых названий, оба конца среза (левый и правый) включаются. Если бы срезы по названиям были бы устроены как срезы по числовым индексам, код выше выдавал бы столбцы с Econ и до Phist, не включая колонку Law, так как в обычных срезах правый конец исключается.

Выбор столбцов по номеру

Иногда может возникнуть необходимость выбрать столбец по его порядковому номеру. Например, когда названий столбцов нет как таковых или когда названия слишком длинные, а переименовывать их нежелательно. Сделать это можно с помощью метода .iloc:

In [25]:
df.iloc[:, 1]
Out[25]:
id
М141БПЛТЛ024     9
М141БПЛТЛ031    10
М141БПЛТЛ075     9
М141БПЛТЛ017     9
М141БПЛТЛ069    10
М141БПЛТЛ072     9
М141БПЛТЛ020     7
М141БПЛТЛ026    10
М141БПЛТЛ073     9
М141БПЛТЛ078     6
М141БПЛТЛ060     8
М141БПЛТЛ040     9
М141БПЛТЛ065     9
М141БПЛТЛ053     7
М141БПЛТЛ015     9
М141БПЛТЛ021     9
М141БПЛТЛ018     7
М141БПЛТЛ039     8
М141БПЛТЛ036    10
М141БПЛТЛ049     7
06114043         8
М141БПЛТЛ048     6
М141БПЛТЛ034     9
М141БПЛТЛ045     8
М141БПЛТЛ033     9
М141БПЛТЛ083     5
М141БПЛТЛ008     8
М141БПЛТЛ001     7
М141БПЛТЛ038     9
М141БПЛТЛ052     7
М141БПЛТЛ011     6
М141БПЛТЛ004     7
М141БПЛТЛ010     6
М141БПЛТЛ071     9
М141БПЛТЛ035     6
М141БПЛТЛ030     6
М141БПЛТЛ070     5
М141БПЛТЛ051     9
М141БПЛТЛ046     7
М141БПЛТЛ047     8
М141БПЛТЛ063     5
М141БПЛТЛ029     8
М141БПЛТЛ064     8
М141БПЛТЛ076     7
М141БПЛТЛ062     7
М141БПЛТЛ074     6
130232038        7
М141БПЛТЛ023     9
М141БПЛТЛ054     8
М141БПЛТЛ012     6
М141БПЛТЛ006     5
М141БПЛТЛ055     5
М141БПЛТЛ007     7
М141БПЛТЛ050     6
М141БПЛТЛ066    10
М141БПЛТЛ043     5
М141БПЛТЛ084     7
М141БПЛТЛ005     7
М141БПЛТЛ044     5
13051038         4
Name: Mstat, dtype: int64

Синтаксис кода с .iloc несильно отличается от синтаксиса .loc. В чем разница? Разница заключается в том, что метод .loc работает с текстовыми названиями, а метод .iloc – с числовыми индексами. Отсюда и префикс i в названии (i – индекс, loc – location). Если мы попытаемся в .iloc указать названия столбцов, Python выдаст ошибку:

In [26]:
df.iloc[:, 'Mstat': 'Econ']
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-26-25ef00bcf778> in <module>()
----> 1 df.iloc[:, 'Mstat': 'Econ']

/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py in __getitem__(self, key)
   1470             except (KeyError, IndexError):
   1471                 pass
-> 1472             return self._getitem_tuple(key)
   1473         else:
   1474             # we by definition only have the 0th axis

/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py in _getitem_tuple(self, tup)
   2027                 continue
   2028 
-> 2029             retval = getattr(retval, self.name)._getitem_axis(key, axis=axis)
   2030 
   2031             # if the dim was reduced, then pass a lower-dim the next time

/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py in _getitem_axis(self, key, axis)
   2078 
   2079         if isinstance(key, slice):
-> 2080             return self._get_slice_axis(key, axis=axis)
   2081 
   2082         if isinstance(key, list):

/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py in _get_slice_axis(self, slice_obj, axis)
   2046             return obj.copy(deep=False)
   2047 
-> 2048         slice_obj = self._convert_slice_indexer(slice_obj, axis)
   2049         if isinstance(slice_obj, slice):
   2050             return self._slice(slice_obj, axis=axis, kind='iloc')

/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py in _convert_slice_indexer(self, key, axis)
    264         # if we are accessing via lowered dim, use the last dim
    265         ax = self.obj._get_axis(min(axis, self.ndim - 1))
--> 266         return ax._convert_slice_indexer(key, kind=self.name)
    267 
    268     def _has_valid_setitem_indexer(self, indexer):

/anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in _convert_slice_indexer(self, key, kind)
   1688         # validate iloc
   1689         if kind == 'iloc':
-> 1690             return slice(self._validate_indexer('slice', key.start, kind),
   1691                          self._validate_indexer('slice', key.stop, kind),
   1692                          self._validate_indexer('slice', key.step, kind))

/anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in _validate_indexer(self, form, key, kind)
   4126             pass
   4127         elif kind in ['iloc', 'getitem']:
-> 4128             self._invalid_indexer(form, key)
   4129         return key
   4130 

/anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in _invalid_indexer(self, form, key)
   1846                         "indexers [{key}] of {kind}".format(
   1847                             form=form, klass=type(self), key=key,
-> 1848                             kind=type(key)))
   1849 
   1850     def get_duplicates(self):

TypeError: cannot do slice indexing on <class 'pandas.core.indexes.base.Index'> with these indexers [Mstat] of <class 'str'>

Python пишет, что невозможно взять срез по индексам, которые имеют строковый тип (class 'str'), так как в квадратных скобках ожидаются числовые (целочисленные) индексы.

Если нужно выбрать несколько столбцов подряд, можно воспользоваться срезами:

In [27]:
df.iloc[:, 1:3]
Out[27]:
Mstat Soc
id
М141БПЛТЛ024 9 8
М141БПЛТЛ031 10 10
М141БПЛТЛ075 9 9
М141БПЛТЛ017 9 8
М141БПЛТЛ069 10 10
М141БПЛТЛ072 9 8
М141БПЛТЛ020 7 7
М141БПЛТЛ026 10 8
М141БПЛТЛ073 9 8
М141БПЛТЛ078 6 9
М141БПЛТЛ060 8 7
М141БПЛТЛ040 9 8
М141БПЛТЛ065 9 8
М141БПЛТЛ053 7 7
М141БПЛТЛ015 9 7
М141БПЛТЛ021 9 8
М141БПЛТЛ018 7 9
М141БПЛТЛ039 8 9
М141БПЛТЛ036 10 7
М141БПЛТЛ049 7 6
06114043 8 10
М141БПЛТЛ048 6 8
М141БПЛТЛ034 9 7
М141БПЛТЛ045 8 8
М141БПЛТЛ033 9 8
М141БПЛТЛ083 5 6
М141БПЛТЛ008 8 8
М141БПЛТЛ001 7 7
М141БПЛТЛ038 9 6
М141БПЛТЛ052 7 7
М141БПЛТЛ011 6 8
М141БПЛТЛ004 7 6
М141БПЛТЛ010 6 7
М141БПЛТЛ071 9 7
М141БПЛТЛ035 6 7
М141БПЛТЛ030 6 6
М141БПЛТЛ070 5 6
М141БПЛТЛ051 9 8
М141БПЛТЛ046 7 7
М141БПЛТЛ047 8 6
М141БПЛТЛ063 5 6
М141БПЛТЛ029 8 8
М141БПЛТЛ064 8 6
М141БПЛТЛ076 7 8
М141БПЛТЛ062 7 7
М141БПЛТЛ074 6 7
130232038 7 6
М141БПЛТЛ023 9 6
М141БПЛТЛ054 8 6
М141БПЛТЛ012 6 7
М141БПЛТЛ006 5 6
М141БПЛТЛ055 5 6
М141БПЛТЛ007 7 7
М141БПЛТЛ050 6 6
М141БПЛТЛ066 10 7
М141БПЛТЛ043 5 6
М141БПЛТЛ084 7 8
М141БПЛТЛ005 7 5
М141БПЛТЛ044 5 7
13051038 4 4

Числовые срезы в pandas уже ничем не отличаются от списковых срезов: правый конец среза не включается. В нашем случае мы выбрали только столбцы с индексами 1 и 2.

Выбор строк по названию

Выбор строки по названию происходит аналогичным образом, только здесь метод .loc уже обязателен.

In [28]:
df.loc['М141БПЛТЛ031'] # строка для студента с номером М141БПЛТЛ031
Out[28]:
Cps        8.0
Mstat     10.0
Soc       10.0
Econ      10.0
Eng       10.0
Polth     10.0
Mstat2    10.0
Phist      9.0
Law        9.0
Phil      10.0
Polsoc    10.0
Ptheo      9.0
Preg       8.0
Compp      8.0
Game       9.0
Wpol      10.0
Male       1.0
Name: М141БПЛТЛ031, dtype: float64

При этом ставить запятую и двоеточие, показывая, что нам нужна одна строка и все столбцы, уже не нужно. Если нам нужно выбрать несколько строк подряд, то .loc не нужен:

In [29]:
df["М141БПЛТЛ024":'М141БПЛТЛ069']
Out[29]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1

Как Python понимает, что мы просим вывести именно строки с такими названиями, а не столбцы? Потому что у нас стоят одинарные квадратные скобки, а не двойные, как в случае со столбцами. (Да, в pandas много всяких тонкостей, но чтобы хорошо в них разбираться, нужно просто попрактиковаться и привыкнуть).

Обратите внимание: разницы между двойными и одинарными кавычками нет, строки можно вводить в любых кавычках, как в примере выше.

Выбор строк по номеру

В этом случае достаточно указать номер в квадратных скобках в .iloc:

In [30]:
df.iloc[2]
Out[30]:
Cps        9.0
Mstat      9.0
Soc        9.0
Econ      10.0
Eng        9.0
Polth     10.0
Mstat2     9.0
Phist      8.0
Law        9.0
Phil      10.0
Polsoc     9.0
Ptheo      9.0
Preg       8.0
Compp      8.0
Game       7.0
Wpol       9.0
Male       1.0
Name: М141БПЛТЛ075, dtype: float64

Если нужно несколько строк подряд, можно воспользоваться срезами:

In [31]:
df[1:3] # и без iloc
Out[31]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1

Если нужно несколько строк не подряд, можно просто перечислить внутри списка в .iloc:

In [32]:
df.iloc[[1, 2, 5, 10]]
Out[32]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ072 10 9 8 10 9 8 9 8.0 8 10 9 7.0 8 8.0 9 9 0
М141БПЛТЛ060 7 8 7 7 9 8 8 5.0 7 5 8 5.0 7 8.0 7 9 1

Выбор наблюдений по названиям строк и столбцов

Если нам нужно выбрать одно наблюдение на пересечении строки и столбца, можно воспользоваться методом .at: сначала указать название строки, потом ‒ столбца:

In [33]:
df.at['М141БПЛТЛ078', 'Game'] # оценка по теории игр у студента М141БПЛТЛ078
Out[33]:
6

Кроме того, можно применить метод .loc:

In [34]:
df.loc["М141БПЛТЛ075", "Soc"] # оценка по социологии у студента М141БПЛТЛ075
Out[34]:
9

В чем разница между .at и .loc? Метод .loc более универсален. В то время как .at используется для нахождения одного наблюдения на пересечении строки и столбца, .loc позволяет выбрать несколько наблюдений (строк и столбцов) сразу. Например, так:

In [35]:
df.loc["М141БПЛТЛ024":"М141БПЛТЛ073", "Mstat"]
Out[35]:
id
М141БПЛТЛ024     9
М141БПЛТЛ031    10
М141БПЛТЛ075     9
М141БПЛТЛ017     9
М141БПЛТЛ069    10
М141БПЛТЛ072     9
М141БПЛТЛ020     7
М141БПЛТЛ026    10
М141БПЛТЛ073     9
Name: Mstat, dtype: int64

Если нужно выбрать какое-то одно значение, метод .at будет работать более быстро, чем .loc.

Выбор наблюдения по номеру строки и столбца

Выбор наблюдения по номеру строки и столбца осуществляется аналогичным образом, только теперь мы используем методы с префиксом i для индексов: .iat и .iloc.

In [36]:
df.iat[4, 6] # оценка на пересечении строки 4 и столбца 6
Out[36]:
9
In [37]:
df.iloc[8, 4] # оценка на пересечении строки 8 и столбца 4
Out[37]:
9

Убедимся, что все верно:

In [38]:
df.head(8)
Out[38]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1
М141БПЛТЛ072 10 9 8 10 9 8 9 8.0 8 10 9 7.0 8 8.0 9 9 0
М141БПЛТЛ020 8 7 7 6 9 10 8 8.0 7 7 9 7.0 8 6.0 8 9 1
М141БПЛТЛ026 7 10 8 7 10 7 9 8.0 8 8 8 8.0 8 7.0 7 8 0

Выбор строк по условию (фильтрация наблюдений)

Часто в исследованиях нас не интересует выбор отдельных строк по названию или номеру, мы хотим отбирать строки в таблице согласно некорому условию (условиям). Другими словами, проводить фильтрацию наблюдений. Для этого интересующее нас условие необходимо указать в квадратных скобках. Выберем из датафрейма dfстроки, которые соответствуют студентам с оценкой по экономике выше 6.

In [39]:
df[df["Econ"] > 6]
Out[39]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1
М141БПЛТЛ072 10 9 8 10 9 8 9 8.0 8 10 9 7.0 8 8.0 9 9 0
М141БПЛТЛ026 7 10 8 7 10 7 9 8.0 8 8 8 8.0 8 7.0 7 8 0
М141БПЛТЛ073 7 9 8 8 9 8 9 8.0 8 9 9 7.0 7 6.0 10 9 1
М141БПЛТЛ060 7 8 7 7 9 8 8 5.0 7 5 8 5.0 7 8.0 7 9 1
М141БПЛТЛ021 8 9 8 8 9 8 8 7.0 7 7 6 6.0 8 6.0 7 8 0
М141БПЛТЛ018 7 7 9 7 9 7 8 6.0 6 7 8 7.0 7 7.0 7 8 0
М141БПЛТЛ039 9 8 9 8 8 8 6 8.0 7 6 9 6.0 7 8.0 4 9 1
М141БПЛТЛ036 8 10 7 8 8 6 9 4.0 8 8 7 6.0 7 6.0 7 8 1
М141БПЛТЛ045 5 8 8 7 8 6 7 6.0 7 7 8 6.0 8 6.0 5 8 0
М141БПЛТЛ033 5 9 8 7 9 7 9 7.0 7 8 8 7.0 8 5.0 7 8 0
М141БПЛТЛ008 10 8 8 9 8 10 9 8.0 9 10 9 8.0 5 5.0 10 4 1
М141БПЛТЛ052 7 7 7 7 8 6 6 6.0 8 6 7 5.0 8 6.0 5 7 1
М141БПЛТЛ071 6 9 7 7 9 6 8 4.0 6 7 7 6.0 5 NaN 5 7 0
М141БПЛТЛ029 6 8 8 7 9 5 6 7.0 6 5 8 5.0 7 4.0 5 7 0
М141БПЛТЛ064 7 8 6 7 6 6 8 4.0 6 4 4 4.0 6 5.0 4 7 0
М141БПЛТЛ023 7 9 6 8 9 6 9 4.0 7 7 7 6.0 4 4.0 7 5 1
М141БПЛТЛ066 7 10 7 7 9 5 8 4.0 6 5 6 4.0 6 4.0 5 6 0

Почему нельзя было написать проще, то есть df["Econ"] > 6? Давайте напишем, и посмотрим, что получится:

In [40]:
df["Econ"] > 6
Out[40]:
id
М141БПЛТЛ024     True
М141БПЛТЛ031     True
М141БПЛТЛ075     True
М141БПЛТЛ017     True
М141БПЛТЛ069     True
М141БПЛТЛ072     True
М141БПЛТЛ020    False
М141БПЛТЛ026     True
М141БПЛТЛ073     True
М141БПЛТЛ078    False
М141БПЛТЛ060     True
М141БПЛТЛ040    False
М141БПЛТЛ065    False
М141БПЛТЛ053    False
М141БПЛТЛ015    False
М141БПЛТЛ021     True
М141БПЛТЛ018     True
М141БПЛТЛ039     True
М141БПЛТЛ036     True
М141БПЛТЛ049    False
06114043        False
М141БПЛТЛ048    False
М141БПЛТЛ034    False
М141БПЛТЛ045     True
М141БПЛТЛ033     True
М141БПЛТЛ083    False
М141БПЛТЛ008     True
М141БПЛТЛ001    False
М141БПЛТЛ038    False
М141БПЛТЛ052     True
М141БПЛТЛ011    False
М141БПЛТЛ004    False
М141БПЛТЛ010    False
М141БПЛТЛ071     True
М141БПЛТЛ035    False
М141БПЛТЛ030    False
М141БПЛТЛ070    False
М141БПЛТЛ051    False
М141БПЛТЛ046    False
М141БПЛТЛ047    False
М141БПЛТЛ063    False
М141БПЛТЛ029     True
М141БПЛТЛ064     True
М141БПЛТЛ076    False
М141БПЛТЛ062    False
М141БПЛТЛ074    False
130232038       False
М141БПЛТЛ023     True
М141БПЛТЛ054    False
М141БПЛТЛ012    False
М141БПЛТЛ006    False
М141БПЛТЛ055    False
М141БПЛТЛ007    False
М141БПЛТЛ050    False
М141БПЛТЛ066     True
М141БПЛТЛ043    False
М141БПЛТЛ084    False
М141БПЛТЛ005    False
М141БПЛТЛ044    False
13051038        False
Name: Econ, dtype: bool

Что мы увидели? Просто результат проверки условия, набор из True и False. Когда мы подставляем это выражение в квадратные скобки, Python выбирает из df те строки, где выражение принимает значение True.

Все операторы проверки условий работают как обычно:

In [41]:
df[df["Econ"] == 9] # двойное равенство для равенства
Out[41]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ008 10 8 8 9 8 10 9 8.0 9 10 9 8.0 5 5.0 10 4 1

Можно формулировать сложные условия. Выберем студентов с оценкой по экономике от 6 до 8 (8 не включается).

In [42]:
df[(df["Econ"] >= 6) & (df["Econ"] < 8)] 
Out[42]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ020 8 7 7 6 9 10 8 8.0 7 7 9 7.0 8 6.0 8 9 1
М141БПЛТЛ026 7 10 8 7 10 7 9 8.0 8 8 8 8.0 8 7.0 7 8 0
М141БПЛТЛ060 7 8 7 7 9 8 8 5.0 7 5 8 5.0 7 8.0 7 9 1
М141БПЛТЛ040 6 9 8 6 9 7 8 6.0 9 5 8 5.0 8 5.0 7 10 0
М141БПЛТЛ015 6 9 7 6 9 7 9 4.0 7 7 7 6.0 7 7.0 10 7 0
М141БПЛТЛ018 7 7 9 7 9 7 8 6.0 6 7 8 7.0 7 7.0 7 8 0
М141БПЛТЛ049 6 7 6 6 8 6 8 4.0 8 5 9 6.0 8 5.0 6 8 0
М141БПЛТЛ048 8 6 8 6 9 6 4 4.0 6 4 8 4.0 6 7.0 7 8 0
М141БПЛТЛ034 6 9 7 6 9 6 8 6.0 7 6 6 5.0 8 5.0 8 9 0
М141БПЛТЛ045 5 8 8 7 8 6 7 6.0 7 7 8 6.0 8 6.0 5 8 0
М141БПЛТЛ033 5 9 8 7 9 7 9 7.0 7 8 8 7.0 8 5.0 7 8 0
М141БПЛТЛ052 7 7 7 7 8 6 6 6.0 8 6 7 5.0 8 6.0 5 7 1
М141БПЛТЛ011 7 6 8 6 9 6 6 5.0 6 6 7 6.0 8 6.0 5 8 0
М141БПЛТЛ004 7 7 6 6 8 6 6 5.0 5 5 6 5.0 7 5.0 8 8 0
М141БПЛТЛ010 6 6 7 6 9 7 7 6.0 7 5 8 6.0 8 6.0 5 8 1
М141БПЛТЛ071 6 9 7 7 9 6 8 4.0 6 7 7 6.0 5 NaN 5 7 0
М141БПЛТЛ035 5 6 7 6 8 5 5 4.0 6 6 7 5.0 8 7.0 6 7 0
М141БПЛТЛ030 7 6 6 6 7 6 6 4.0 8 5 5 5.0 8 5.0 7 9 1
М141БПЛТЛ051 8 9 8 6 8 7 6 7.0 6 6 6 5.0 4 4.0 5 5 1
М141БПЛТЛ029 6 8 8 7 9 5 6 7.0 6 5 8 5.0 7 4.0 5 7 0
М141БПЛТЛ064 7 8 6 7 6 6 8 4.0 6 4 4 4.0 6 5.0 4 7 0
М141БПЛТЛ076 7 7 8 6 8 6 6 6.0 8 6 8 5.0 7 4.0 4 6 0
М141БПЛТЛ062 7 7 7 6 9 6 6 5.0 6 5 6 4.0 5 5.0 4 6 0
М141БПЛТЛ007 6 7 7 6 7 6 7 4.0 5 5 6 5.0 4 5.0 4 7 1
М141БПЛТЛ050 8 6 6 6 8 4 5 4.0 5 5 6 4.0 5 4.0 6 6 0
М141БПЛТЛ066 7 10 7 7 9 5 8 4.0 6 5 6 4.0 6 4.0 5 6 0

В качестве символа для одновременного выполнения условий используется оператор &. И не забудьте про круглые скобки. А теперь выберем студентов с оценкой по английскому выше 9 и оценкой по праву ниже 9:

In [43]:
df[(df["Eng"] > 9) & (df["Law"] < 9)] 
Out[43]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1
М141БПЛТЛ026 7 10 8 7 10 7 9 8.0 8 8 8 8.0 8 7.0 7 8 0
М141БПЛТЛ001 6 7 7 4 10 7 7 6.0 8 6 8 4.0 6 6.0 4 8 0
М141БПЛТЛ012 6 6 7 4 10 6 5 4.0 7 5 7 4.0 5 4.0 4 8 1

При формулировании сложных (составных) условий обращайте особое внимание на порядок круглых скобках, потому что, если вы расставите скобки неправильно, результат получится неверный:

In [44]:
df[(df["Eng"]) > 9 & (df["Law"] < 9)] # первая закрывающая скобка не после 9
Out[44]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1
М141БПЛТЛ072 10 9 8 10 9 8 9 8.0 8 10 9 7.0 8 8.0 9 9 0
М141БПЛТЛ020 8 7 7 6 9 10 8 8.0 7 7 9 7.0 8 6.0 8 9 1
М141БПЛТЛ026 7 10 8 7 10 7 9 8.0 8 8 8 8.0 8 7.0 7 8 0
М141БПЛТЛ073 7 9 8 8 9 8 9 8.0 8 9 9 7.0 7 6.0 10 9 1
М141БПЛТЛ078 6 6 9 5 6 10 7 6.0 8 6 9 6.0 8 8.0 6 7 0
М141БПЛТЛ060 7 8 7 7 9 8 8 5.0 7 5 8 5.0 7 8.0 7 9 1
М141БПЛТЛ040 6 9 8 6 9 7 8 6.0 9 5 8 5.0 8 5.0 7 10 0
М141БПЛТЛ065 9 9 8 4 8 8 7 9.0 8 5 10 9.0 8 8.0 6 9 1
М141БПЛТЛ053 6 7 7 5 9 8 7 8.0 8 6 8 7.0 8 6.0 9 9 0
М141БПЛТЛ015 6 9 7 6 9 7 9 4.0 7 7 7 6.0 7 7.0 10 7 0
М141БПЛТЛ021 8 9 8 8 9 8 8 7.0 7 7 6 6.0 8 6.0 7 8 0
М141БПЛТЛ018 7 7 9 7 9 7 8 6.0 6 7 8 7.0 7 7.0 7 8 0
М141БПЛТЛ039 9 8 9 8 8 8 6 8.0 7 6 9 6.0 7 8.0 4 9 1
М141БПЛТЛ036 8 10 7 8 8 6 9 4.0 8 8 7 6.0 7 6.0 7 8 1
М141БПЛТЛ049 6 7 6 6 8 6 8 4.0 8 5 9 6.0 8 5.0 6 8 0
06114043 8 8 10 5 8 8 8 10.0 7 7 9 NaN 7 8.0 7 8 1
М141БПЛТЛ048 8 6 8 6 9 6 4 4.0 6 4 8 4.0 6 7.0 7 8 0
М141БПЛТЛ034 6 9 7 6 9 6 8 6.0 7 6 6 5.0 8 5.0 8 9 0
М141БПЛТЛ045 5 8 8 7 8 6 7 6.0 7 7 8 6.0 8 6.0 5 8 0
М141БПЛТЛ033 5 9 8 7 9 7 9 7.0 7 8 8 7.0 8 5.0 7 8 0
М141БПЛТЛ083 5 5 6 5 8 7 6 5.0 7 5 7 5.0 7 5.0 4 7 0
М141БПЛТЛ008 10 8 8 9 8 10 9 8.0 9 10 9 8.0 5 5.0 10 4 1
М141БПЛТЛ001 6 7 7 4 10 7 7 6.0 8 6 8 4.0 6 6.0 4 8 0
М141БПЛТЛ038 7 9 6 4 9 6 7 6.0 7 4 8 4.0 5 4.0 9 7 1
М141БПЛТЛ052 7 7 7 7 8 6 6 6.0 8 6 7 5.0 8 6.0 5 7 1
М141БПЛТЛ011 7 6 8 6 9 6 6 5.0 6 6 7 6.0 8 6.0 5 8 0
М141БПЛТЛ004 7 7 6 6 8 6 6 5.0 5 5 6 5.0 7 5.0 8 8 0
М141БПЛТЛ010 6 6 7 6 9 7 7 6.0 7 5 8 6.0 8 6.0 5 8 1
М141БПЛТЛ071 6 9 7 7 9 6 8 4.0 6 7 7 6.0 5 NaN 5 7 0
М141БПЛТЛ035 5 6 7 6 8 5 5 4.0 6 6 7 5.0 8 7.0 6 7 0
М141БПЛТЛ030 7 6 6 6 7 6 6 4.0 8 5 5 5.0 8 5.0 7 9 1
М141БПЛТЛ070 5 5 6 4 8 6 5 5.0 6 4 5 6.0 8 5.0 6 7 0
М141БПЛТЛ051 8 9 8 6 8 7 6 7.0 6 6 6 5.0 4 4.0 5 5 1
М141БПЛТЛ046 5 7 7 4 7 5 8 5.0 7 5 7 5.0 8 4.0 5 7 0
М141БПЛТЛ047 5 8 6 4 7 5 9 5.0 6 4 6 4.0 7 4.0 8 8 0
М141БПЛТЛ063 5 5 6 4 8 4 4 4.0 5 4 5 4.0 7 5.0 8 8 0
М141БПЛТЛ029 6 8 8 7 9 5 6 7.0 6 5 8 5.0 7 4.0 5 7 0
М141БПЛТЛ064 7 8 6 7 6 6 8 4.0 6 4 4 4.0 6 5.0 4 7 0
М141БПЛТЛ076 7 7 8 6 8 6 6 6.0 8 6 8 5.0 7 4.0 4 6 0
М141БПЛТЛ062 7 7 7 6 9 6 6 5.0 6 5 6 4.0 5 5.0 4 6 0
М141БПЛТЛ074 5 6 7 4 7 6 5 6.0 6 6 8 6.0 6 6.0 8 8 1
130232038 6 7 6 5 8 4 8 4.0 8 4 5 5.0 6 4.0 5 6 0
М141БПЛТЛ023 7 9 6 8 9 6 9 4.0 7 7 7 6.0 4 4.0 7 5 1
М141БПЛТЛ054 7 8 6 4 8 6 4 4.0 6 4 8 4.0 4 4.0 4 8 1
М141БПЛТЛ012 6 6 7 4 10 6 5 4.0 7 5 7 4.0 5 4.0 4 8 1
М141БПЛТЛ006 6 5 6 5 8 5 5 5.0 6 4 7 5.0 7 5.0 6 8 0
М141БПЛТЛ055 6 5 6 4 7 7 4 8.0 5 4 6 4.0 6 5.0 4 5 1
М141БПЛТЛ007 6 7 7 6 7 6 7 4.0 5 5 6 5.0 4 5.0 4 7 1
М141БПЛТЛ050 8 6 6 6 8 4 5 4.0 5 5 6 4.0 5 4.0 6 6 0
М141БПЛТЛ066 7 10 7 7 9 5 8 4.0 6 5 6 4.0 6 4.0 5 6 0
М141БПЛТЛ043 5 5 6 5 8 5 6 5.0 6 4 5 4.0 5 NaN 4 6 0
М141БПЛТЛ084 6 7 8 4 8 5 5 NaN 8 4 4 4.0 4 4.0 6 7 1
М141БПЛТЛ005 5 7 5 5 7 4 7 4.0 5 4 5 5.0 4 4.0 4 8 1
М141БПЛТЛ044 4 5 7 4 6 4 4 5.0 4 4 4 4.0 6 NaN 5 5 1
13051038 5 4 4 4 9 5 5 5.0 5 4 4 NaN 7 4.0 4 4 1

Результат получился совсем неверным. Потому что Python понял наше условие не так, как нужно. Теперь выберем студентов с оценкой по политической истории ниже 5 или с оценкой по истории политических учений ниже 5:

In [45]:
df[(df["Phist"] < 5) | (df["Polth"] < 5)] # оператор | для условия или 
Out[45]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male
id
М141БПЛТЛ015 6 9 7 6 9 7 9 4.0 7 7 7 6.0 7 7.0 10 7 0
М141БПЛТЛ036 8 10 7 8 8 6 9 4.0 8 8 7 6.0 7 6.0 7 8 1
М141БПЛТЛ049 6 7 6 6 8 6 8 4.0 8 5 9 6.0 8 5.0 6 8 0
М141БПЛТЛ048 8 6 8 6 9 6 4 4.0 6 4 8 4.0 6 7.0 7 8 0
М141БПЛТЛ071 6 9 7 7 9 6 8 4.0 6 7 7 6.0 5 NaN 5 7 0
М141БПЛТЛ035 5 6 7 6 8 5 5 4.0 6 6 7 5.0 8 7.0 6 7 0
М141БПЛТЛ030 7 6 6 6 7 6 6 4.0 8 5 5 5.0 8 5.0 7 9 1
М141БПЛТЛ063 5 5 6 4 8 4 4 4.0 5 4 5 4.0 7 5.0 8 8 0
М141БПЛТЛ064 7 8 6 7 6 6 8 4.0 6 4 4 4.0 6 5.0 4 7 0
130232038 6 7 6 5 8 4 8 4.0 8 4 5 5.0 6 4.0 5 6 0
М141БПЛТЛ023 7 9 6 8 9 6 9 4.0 7 7 7 6.0 4 4.0 7 5 1
М141БПЛТЛ054 7 8 6 4 8 6 4 4.0 6 4 8 4.0 4 4.0 4 8 1
М141БПЛТЛ012 6 6 7 4 10 6 5 4.0 7 5 7 4.0 5 4.0 4 8 1
М141БПЛТЛ007 6 7 7 6 7 6 7 4.0 5 5 6 5.0 4 5.0 4 7 1
М141БПЛТЛ050 8 6 6 6 8 4 5 4.0 5 5 6 4.0 5 4.0 6 6 0
М141БПЛТЛ066 7 10 7 7 9 5 8 4.0 6 5 6 4.0 6 4.0 5 6 0
М141БПЛТЛ005 5 7 5 5 7 4 7 4.0 5 4 5 5.0 4 4.0 4 8 1
М141БПЛТЛ044 4 5 7 4 6 4 4 5.0 4 4 4 4.0 6 NaN 5 5 1

Здесь наше выражение в квадратных скобках принимает значение True, когда хотя бы одно из условий верно: либо верно первое, либо второе, либо оба.

Добавление новых столбцов в таблице и удаление пропущенных значений

Давайте добавим в нашу таблицу df новый столбец, который будет представлять собой среднюю оценку по социологии (посчитаем среднее арифметическое оценок по социологии и политической социологии). Чтобы добавить новый столбец, нужно указать его название в квадратных скобках:

In [46]:
df["Avg_Soc"] = (df["Soc"] + df["Polsoc"]) / 2
In [47]:
df.head()
Out[47]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male Avg_Soc
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8.0 7 9 9 7.0 8 8.0 6 10 1 8.5
М141БПЛТЛ031 8 10 10 10 10 10 10 9.0 9 10 10 9.0 8 8.0 9 10 1 10.0
М141БПЛТЛ075 9 9 9 10 9 10 9 8.0 9 10 9 9.0 8 8.0 7 9 1 9.0
М141БПЛТЛ017 9 9 8 8 9 9 10 6.0 9 9 9 8.0 8 8.0 8 9 0 8.5
М141БПЛТЛ069 10 10 10 10 10 10 9 8.0 8 10 9 7.0 6 5.0 8 10 1 9.5

Теперь внесем изменения в уже существующий столбец в таблице. В самом начале мы заметили, что некоторые столбцы имеют тип float (числа с плавающей точкой), а не integer (целые числа). Давайте попробуем сделать столбец с политической историей целочисленным.

In [48]:
newh = [int(i) for i in df["Phist"]]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-48-209824ec1fa3> in <module>()
----> 1 newh = [int(i) for i in df["Phist"]]

<ipython-input-48-209824ec1fa3> in <listcomp>(.0)
----> 1 newh = [int(i) for i in df["Phist"]]

ValueError: cannot convert float NaN to integer

Не получается! Почему? Python пишет, что не может превратить NaN в integer. Действительно, сложно превратить объект Not a number в целое число. Тип float относится к нему толерантно, а вот тип integer уже нет. Как быть? Давайте просто удалим из датафрейма все пропущенные значения (то есть строки, содержащие пропущенные значения).

In [49]:
df = df.dropna() # удаляем и сохраняем изменения

Теперь проделаем те же операции:

In [50]:
newh = [int(i) for i in df["Phist"]]
In [51]:
df["Phist"] = newh
/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  """Entry point for launching an IPython kernel.

Получилось!

In [52]:
df.head() # Phist уже с целыми значениями
Out[52]:
Cps Mstat Soc Econ Eng Polth Mstat2 Phist Law Phil Polsoc Ptheo Preg Compp Game Wpol Male Avg_Soc
id
М141БПЛТЛ024 7 9 8 8 9 8 10 8 7 9 9 7.0 8 8.0 6 10 1 8.5
М141БПЛТЛ031 8 10 10 10 10 10 10 9 9 10 10 9.0 8 8.0 9 10 1 10.0
М141БПЛТЛ075 9 9 9 10 9 10 9 8 9 10 9 9.0 8 8.0 7 9 1 9.0
М141БПЛТЛ017 9 9 8 8 9 9 10 6 9 9 9 8.0 8 8.0 8 9 0 8.5
М141БПЛТЛ069 10 10 10 10 10 10 9 8 8 10 9 7.0 6 5.0 8 10 1 9.5

Еще немного про описательные статистики

В самом начале мы обсуждали описание базы данных с помощью метода .describe(). Помимо этого метода существует много методов, которые выводят отдельные статистики.

In [53]:
df.median() # медиана (для всех показателей)
Out[53]:
Cps        7.00
Mstat      7.50
Soc        7.00
Econ       6.00
Eng        8.50
Polth      6.00
Mstat2     7.00
Phist      6.00
Law        7.00
Phil       6.00
Polsoc     8.00
Ptheo      5.00
Preg       7.00
Compp      5.00
Game       6.00
Wpol       8.00
Male       0.00
Avg_Soc    7.25
dtype: float64

Можно запрашивать статистики по отдельным переменным (столбцам):

In [54]:
df.Phist.mean() # среднее арифметическое
Out[54]:
5.833333333333333

Или по наблюдениям (строкам):

In [55]:
df.loc["М141БПЛТЛ023"].mean() 
Out[55]:
6.25

Давайте теперь построим какие-нибудь графики. Библиотеку pandas удобно использовать в сочетании с библиотекой для построения графиков matplotlib. Давайте её импортируем (эта библиотека должна была быть установлена на ваш компьютер вместе с Anaconda).

In [56]:
import matplotlib

Теперь добавим элементы магии :) Магическую строку в Jupyter Notebook (Python magic). Эта строка позволит выводить графики прямо внутри ноутбука, файла .ipynb, а не в отдельном окне.

In [57]:
%matplotlib inline

Построим гистограмму для оценок по теории игр.

In [58]:
df["Game"].plot.hist() # histogram
Out[58]:
<matplotlib.axes._subplots.AxesSubplot at 0x11d3aa940>

Что показывает этот график? Он показывает, сколько студентов получили те или иные оценки. По гистограмме видно, что больше всего по этому курсу оценок 4 и 7.

Можно поменять цвет гистограммы:

In [59]:
df["Game"].plot.hist(color = "red")
Out[59]:
<matplotlib.axes._subplots.AxesSubplot at 0x120eb7e80>

Можно пытаться строить другие графики. Например, построить ящик с усами.

In [60]:
df["Game"].plot.box() # boxplot
Out[60]:
<matplotlib.axes._subplots.AxesSubplot at 0x120efb240>

Этот график визуализирует основные описательные статистики переменной и отображает форму её распределения. Нижняя граница яшика – это нижний квартиль, верхняя – верхний квартиль, линяя внутри ящика – медиана. Усы графика могут откладываться по-разному: если в переменной встречаются нетипичные значения (выбросы), то границы усов совпадают с границами типичных значений, если нетипичных значений нет, границы усов соответствуют минимальному и максимальному значению переменной. Подробнее про ящик с усами см. здесь.