Примітки для викладача

General Notes


It’s all right not to get through the whole lesson.
This lesson is designed for people who have never programmed before, but any given class may include people with a wide range of prior experience. We have therefore included enough material to fill a full day if need be, but expect that many offerings will only get as far as the introduction to Pandas.
Don’t tell people to Google things.
One of the goals of this lesson is to help novices build a workable mental model of how programming works. Until they have that model, they will not know what to search for or how to recognize a helpful answer. Пропозиція пошукати відповідь в Google може створити враження, що ми вважаємо їх проблему тривіальною. (Однак, якщо учні раніше достатньо займались програмуванням, щоб подолати ці проблеми, підштовхування їх шукати рішення в Інтернеті може допомогти їм зміцнити своє розуміння.) It’s also worth quoting Trevor King’s comment about online search: “If you find anything, other folks were confused enough to bother with a blog or Stack Overflow post, so it’s probably not trivial.”

Запуск та завершення роботи


Змінні та присвоєння


Типи даних та їх перетворення


Вбудовані функції та довідка


Ранкова кава


Бібліотеки


Зчитування табличних даних у датафрейми


Датафрейми Pandas


Примітка для викладача

Learners often struggle here, many may not work with financial data and concepts so they find the example concepts difficult to get their head around. Однак основна проблема полягає в рядку, який обчислює wealth_score; цей крок потребує ретельного обговорення:

  • Він використовує неявне перетворення між булевим і дійсним значенням, яке ще не розглядалося в курсі.

  • Параметр axis=1 також потребує чіткого пояснення.

    :::::::::::::::::::::::::::::::::::::::::::::::::

Методи векторизації та операції групування Pandas — це функції, які надають користувачам велику гнучкість для аналізу своїх даних.

Наприклад, скажімо, ми хочемо мати більш чітке уявлення про те, як європейські країни розподілені відповідно до свого ВВП.

  1. Ми можемо поглянути на ситуацію, поділивши країни на дві групи за роки спостережень: ті, у яких ВВП вище середнього по Європі, і країни з нижчим ВВП.
  2. Далі ми оцінюємо показник заможності на основі історичних значень (з 1962 по 2007 рік), підраховуючи, скільки разів кожна країна входила до груп із вищим або нижчим ВВП

PYTHON

mask_higher = data > data.mean()
wealth_score = mask_higher.aggregate('sum', axis=1) / len(data.columns)
print(wealth_score)

ВИХІД

country
Albania                   0.000000
Austria                   1.000000
Belgium                   1.000000
Bosnia and Herzegovina    0.000000
Bulgaria                  0.000000
Croatia                   0.000000
Czech Republic            0.500000
Denmark                   1.000000
Finland                   1.000000
France                    1.000000
Germany                   1.000000
Greece                    0.333333
Hungary                   0.000000
Iceland                   1.000000
Ireland                   0.333333
Italy                     0.500000
Montenegro                0.000000
Netherlands               1.000000
Norway                    1.000000
Poland                    0.000000
Portugal                  0.000000
Romania                   0.000000
Serbia                    0.000000
Slovak Republic           0.000000
Slovenia                  0.333333
Spain                     0.333333
Sweden                    1.000000
Switzerland               1.000000
Turkey                    0.000000
United Kingdom            1.000000
dtype: float64

Нарешті, для кожної групи в таблиці wealth_score ми підсумовуємо їх (фінансовий) внесок за роки дослідження, використовуючи ланцюжок методів:

PYTHON

print(data.groupby(wealth_score).sum())

ВИХІД

          gdpPercap_1952  gdpPercap_1957  gdpPercap_1962  gdpPercap_1967  \
0.000000    36916.854200    46110.918793    56850.065437    71324.848786
0.333333    16790.046878    20942.456800    25744.935321    33567.667670
0.500000    11807.544405    14505.000150    18380.449470    21421.846200
1.000000   104317.277560   127332.008735   149989.154201   178000.350040

          gdpPercap_1972  gdpPercap_1977  gdpPercap_1982  gdpPercap_1987  \
0.000000    88569.346898   104459.358438   113553.768507   119649.599409
0.333333    45277.839976    53860.456750    59679.634020    64436.912960
0.500000    25377.727380    29056.145370    31914.712050    35517.678220
1.000000   215162.343140   241143.412730   263388.781960   296825.131210

          gdpPercap_1992  gdpPercap_1997  gdpPercap_2002  gdpPercap_2007
0.000000    92380.047256   103772.937598   118590.929863   149577.357928
0.333333    67918.093220    80876.051580   102086.795210   122803.729520
0.500000    36310.666080    40723.538700    45564.308390    51403.028210
1.000000   315238.235970   346930.926170   385109.939210   427850.333420
Вправа

Вибір індивідуальних значень

Припустімо, що Pandas було імпортовано та дані Gapminder про ВВП для Європи завантажено.

PYTHON

import pandas as pd

data_europe = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country')

Напишіть вираз для визначення ВВП Сербії на душу населення у 2007 році.

Вибір можна зробити за допомогою мітки “Сербія” для рядка, та мітки “gdpPercap_2007” для стовпця:

PYTHON

print(data_europe.loc['Serbia', 'gdpPercap_2007'])

Результат є таким

ВИХІД

9786.534714
Вправа

Межі зрізу

  1. Чи дають два наведені нижче твердження однаковий результат?
  2. На основі цього, яке правило визначає, що включено (чи ні) до числових та іменованих зрізів у Pandas?

PYTHON

print(data_europe.iloc[0:2, 0:2])
print(data_europe.loc['Albania':'Belgium', 'gdpPercap_1952':'gdpPercap_1962'])

Ні, вони не дають однакові результати! Результатом першого виразу є:

ВИХІД

        gdpPercap_1952  gdpPercap_1957
country
Albania     1601.056136     1942.284244
Austria     6137.076492     8842.598030

Другий вираз дає:

ВИХІД

        gdpPercap_1952  gdpPercap_1957  gdpPercap_1962
country
Albania     1601.056136     1942.284244     2312.888958
Austria     6137.076492     8842.598030    10750.721110
Belgium     8343.105127     9714.960623    10991.206760

Вочевидь, другий вираз повертає додатковий стовпець і додатковий рядок у порівнянні з першим.
Який висновок ми можемо зробити? Ми бачимо, що числовий зріз 0:2 не включає кінцевий індекс (тобто, індекс 2), тоді як іменований зріз ‘gdpPercap_1952’:‘gdpPercap_1962’ включає кінцевий елемент.

Вправа

Реконструювання даних

Опишіть, що робить кожен рядок наведеної короткої програми та які значення зберігаються в змінних first, second тощо?

PYTHON

first = pd.read_csv('data/gapminder_all.csv', index_col='country')
second = first[first['continent'] == 'Americas']
third = second.drop('Puerto Rico')
fourth = third.drop('continent', axis = 1)
fourth.to_csv('result.csv')

Перегляньмо цей фрагмент коду рядок за рядком.

PYTHON

first = pd.read_csv('data/gapminder_all.csv', index_col='country')

В цьому рядку дані про ВВП з усіх країн завантажуються у датафрейм first. Параметр index_col='country' вказує, який стовпець використовується як заголовки рядків у датафреймі.

PYTHON

second = first[first['continent'] == 'Americas']

Цей рядок фільтрує дані: вибираються лише ті рядки з first, у яких стовпець ‘continent’ містить значення ‘Americas’. Зверніть увагу, як логічний вираз у дужках, first['continent'] == 'Americas', використовується для вибору лише тих рядків, де вираз є істинним. Спробуйте надрукувати цей вираз! Чи можете ви також надрукувати його окремі елементи True/False? (підказка: спочатку призначте вираз певній змінній)

PYTHON

third = second.drop('Puerto Rico')

Як підказує синтаксис, цей код видаляє рядок з міткою ‘Puerto Rico’ з датафрейму second. У результаті датафрейм third містить на один рядок менше, ніж вихідний датафрейм second.

PYTHON

fourth = third.drop('continent', axis = 1)

Знову ми застосовуємо функцію drop, але в цьому випадку ми видаляємо не рядок, а цілий стовпець. Для цього нам також потрібно вказати параметр axis (ми хочемо видалити другий стовпець, який має індекс 1).

PYTHON

fourth.to_csv('result.csv')

Останнім кроком є ​​збереження даних, над якими ми працювали, у файл csv. Pandas спрощує це завдання за допомогою функції to_csv(). Єдиним обов’язковим аргументом для функції є ім’я файлу. Зверніть увагу, що файл буде записано в каталозі, з якого ви розпочали сесію Jupyter або Python.

Вправа

Вибір індексів

Поясніть простими словами, що роблять idxmin і idxmax у короткій програмі нижче. У яких випадках ви б використовували ці методи?

PYTHON

data = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country')
print(data.idxmin())
print(data.idxmax())

Для кожного стовпця в data, idxmin поверне значення індексу, що відповідає мінімуму кожного стовпця; idxmax зробить те саме для максимального значення кожного стовпця.

Ви можете використовувати ці функції щоразу, коли потрібно отримати індекс рядка з мінімальним або максимальним значенням, а не саме значення.

Вправа

Потренуйтеся з вибором

Припустімо, що Pandas було імпортовано та дані Gapminder про ВВП для Європи завантажено. Напишіть вираз, щоб вибрати кожне з наступного:

  1. ВВП на душу населення для всіх країн у 1982 році.
  2. ВВП на душу населення для Данії за всі роки.
  3. ВВП на душу населення для всіх країн за роки після 1985 року.
  4. ВВП на душу населення у 2007 році для кожної країни, виражене як кратне до ВВП на душу населення цієї ж країни у 1952 році.

1:

PYTHON

data['gdpPercap_1982']

2:

PYTHON

data.loc['Denmark',:]

3:

PYTHON

data.loc[:,'gdpPercap_1985':]

Пакет pandas досить розумний, щоб розпізнати число в кінці заголовку стовпця й не видати помилки, навіть якщо стовпця з назвою gdpPercap_1985 не існує. Це зручно, якщо нові стовпці додаються до файлу CSV пізніше.

4:

PYTHON

data['gdpPercap_2007']/data['gdpPercap_1952']
Вправа

Багато способів доступу

Отримати значення або зріз з датафрейму можна щонайменше двома способами: за назвою або за індексом. Однак існує багато інших варіантів. Наприклад, можна отримати окремий стовпець або рядок як DataFrame або Series об’єкт.

Запропонуйте різні способи виконання наступних операцій з датафреймами:

  1. Доступ до одного стовпця
  2. Доступ до одного рядку
  3. Доступ до окремого елемента датафрейму
  4. Доступ до декількох стовпців
  5. Доступ до декількох рядків
  6. Доступ до підмножини, заданої визначеними рядками та стовпцями
  7. Доступ до підмножини, заданої діапазонами рядків та стовпців

1. Доступ до одного стовпця:

PYTHON

# by name
data["col_name"]   # as a Series
data[["col_name"]] # as a DataFrame

# by name using .loc
data.T.loc["col_name"]  # as a Series
data.T.loc[["col_name"]].T  # as a DataFrame

# Dot notation (Series)
data.col_name

# by index (iloc)
data.iloc[:, col_index]   # as a Series
data.iloc[:, [col_index]] # as a DataFrame

# using a mask
data.T[data.T.index == "col_name"].T

2. Доступ до одного рядку:

PYTHON

# by name using .loc
data.loc["row_name"] # as a Series
data.loc[["row_name"]] # as a DataFrame

# by name
data.T["row_name"] # as a Series
data.T[["row_name"]].T # as a DataFrame

# by index
data.iloc[row_index]   # as a Series
data.iloc[[row_index]]   # as a DataFrame

# using mask
data[data.index == "row_name"]

3. Доступ до окремого елемента датафрейму:

PYTHON

# by column/row names
data["column_name"]["row_name"]         # as a Series

data[["col_name"]].loc["row_name"]  # as a Series
data[["col_name"]].loc[["row_name"]]  # as a DataFrame

data.loc["row_name"]["col_name"]  # as a value
data.loc[["row_name"]]["col_name"]  # as a Series
data.loc[["row_name"]][["col_name"]]  # as a DataFrame

data.loc["row_name", "col_name"]  # as a value
data.loc[["row_name"], "col_name"]  # as a Series. Preserves index. Column name is moved to `.name`.
data.loc["row_name", ["col_name"]]  # as a Series. Index is moved to `.name.` Sets index to column name.
data.loc[["row_name"], ["col_name"]]  # as a DataFrame (preserves original index and column name)

# by column/row names: Dot notation
data.col_name.row_name

# by column/row indices
data.iloc[row_index, col_index] # as a value
data.iloc[[row_index], col_index] # as a Series. Preserves index. Column name is moved to `.name`
data.iloc[row_index, [col_index]] # as a Series. Index is moved to `.name.` Sets index to column name.
data.iloc[[row_index], [col_index]] # as a DataFrame (preserves original index and column name)

# column name + row index
data["col_name"][row_index]
data.col_name[row_index]
data["col_name"].iloc[row_index]

# column index + row name
data.iloc[:, [col_index]].loc["row_name"]  # as a Series
data.iloc[:, [col_index]].loc[["row_name"]]  # as a DataFrame

# using masks
data[data.index == "row_name"].T[data.T.index == "col_name"].T

4. Доступ до декількох стовпців:

PYTHON

# by name
data[["col1", "col2", "col3"]]
data.loc[:, ["col1", "col2", "col3"]]

# by index
data.iloc[:, [col1_index, col2_index, col3_index]]

5. Доступ до декількох рядків

PYTHON

# by name
data.loc[["row1", "row2", "row3"]]

# by index
data.iloc[[row1_index, row2_index, row3_index]]

6. Доступ до підмножини, заданої визначеними рядками та стовпцями

PYTHON

# by names
data.loc[["row1", "row2", "row3"], ["col1", "col2", "col3"]]

# by indices
data.iloc[[row1_index, row2_index, row3_index], [col1_index, col2_index, col3_index]]

# column names + row indices
data[["col1", "col2", "col3"]].iloc[[row1_index, row2_index, row3_index]]

# column indices + row names
data.iloc[:, [col1_index, col2_index, col3_index]].loc[["row1", "row2", "row3"]]

7. Доступ до підмножини, заданої діапазонами рядків та стовпців

PYTHON

# by name
data.loc["row1":"row2", "col1":"col2"]

# by index
data.iloc[row1_index:row2_index, col1_index:col2_index]

# column names + row indices
data.loc[:, "col1_name":"col2_name"].iloc[row1_index:row2_index]

# column indices + row names
data.iloc[:, col1_index:col2_index].loc["row1":"row2"]
Вправа

Пошук доступних методів використовуючи функцію dir()

У Python є функція dir(), яка дозволяє переглянути всі доступні методи (функції), які вбудовані в об’єкт даних. В епізоді 4 ми використали деякі методи для рядка. Але за допомогою dir() можна побачити ще більше доступних методів:

PYTHON

my_string = 'Hello world!'   # creation of a string object 
dir(my_string)

Ця команда повертає:

PYTHON

['__add__',
...
'__subclasshook__',
'capitalize',
'casefold',
'center',
...
'upper',
'zfill']

Ви можете використати help() або Shift+Tab, щоб дізнатися більше про призначення цих методів.

Припустимо, що Pandas вже імпортовано, а дані Gapminder про ВВП Європи завантажено в змінну data. Тоді скористайтеся dir(), щоб знайти функцію, яка друкує середній ВВП на душу населення для всіх європейських країн за кожен доступний рік.

Серед багатьох варіантів dir() пропонує функцію median(). Таким чином,

PYTHON

data.median()
Обговорення

Інтерпретація даних

Кордони Польщі стабільні з 1945 року, але до цього кілька разів змінювалися. Як би ви врахували це при створенні таблиці ВВП на душу населення для Польщі за все ХХ століття?

Ключові моменти
  • Використовуйте DataFrame.iloc[..., ...] для вибору значень за їх позицією
  • Використовуйте лише : замість міток для позначення всіх стовпців або всіх рядків.
  • Вибирайте кілька стовпців або рядків за допомогою DataFrame.loc та зрізу, заданого іменами стовпців або рядків.
  • Результат застосування операції зрізу може бути використаний у подальших операціях.
  • Використовуйте порівняння для вибору даних на основі їх значень.
  • Вибирайте значення або NaN за допомогою булевої маски.




Вибір можна зробити за допомогою мітки “Сербія” для рядка, та мітки “gdpPercap_2007” для стовпця:

PYTHON

print(data_europe.loc['Serbia', 'gdpPercap_2007'])

Результат є таким

ВИХІД

9786.534714




Ні, вони не дають однакові результати! Результатом першого виразу є:

ВИХІД

        gdpPercap_1952  gdpPercap_1957
country
Albania     1601.056136     1942.284244
Austria     6137.076492     8842.598030

Другий вираз дає:

ВИХІД

        gdpPercap_1952  gdpPercap_1957  gdpPercap_1962
country
Albania     1601.056136     1942.284244     2312.888958
Austria     6137.076492     8842.598030    10750.721110
Belgium     8343.105127     9714.960623    10991.206760

Вочевидь, другий вираз повертає додатковий стовпець і додатковий рядок у порівнянні з першим.
Який висновок ми можемо зробити? Ми бачимо, що числовий зріз 0:2 не включає кінцевий індекс (тобто, індекс 2), тоді як іменований зріз ‘gdpPercap_1952’:‘gdpPercap_1962’ включає кінцевий елемент.





Перегляньмо цей фрагмент коду рядок за рядком.

PYTHON

first = pd.read_csv('data/gapminder_all.csv', index_col='country')

В цьому рядку дані про ВВП з усіх країн завантажуються у датафрейм first. Параметр index_col='country' вказує, який стовпець використовується як заголовки рядків у датафреймі.

PYTHON

second = first[first['continent'] == 'Americas']

Цей рядок фільтрує дані: вибираються лише ті рядки з first, у яких стовпець ‘continent’ містить значення ‘Americas’. Зверніть увагу, як логічний вираз у дужках, first['continent'] == 'Americas', використовується для вибору лише тих рядків, де вираз є істинним. Спробуйте надрукувати цей вираз! Чи можете ви також надрукувати його окремі елементи True/False? (підказка: спочатку призначте вираз певній змінній)

PYTHON

third = second.drop('Puerto Rico')

Як підказує синтаксис, цей код видаляє рядок з міткою ‘Puerto Rico’ з датафрейму second. У результаті датафрейм third містить на один рядок менше, ніж вихідний датафрейм second.

PYTHON

fourth = third.drop('continent', axis = 1)

Знову ми застосовуємо функцію drop, але в цьому випадку ми видаляємо не рядок, а цілий стовпець. Для цього нам також потрібно вказати параметр axis (ми хочемо видалити другий стовпець, який має індекс 1).

PYTHON

fourth.to_csv('result.csv')

Останнім кроком є ​​збереження даних, над якими ми працювали, у файл csv. Pandas спрощує це завдання за допомогою функції to_csv(). Єдиним обов’язковим аргументом для функції є ім’я файлу. Зверніть увагу, що файл буде записано в каталозі, з якого ви розпочали сесію Jupyter або Python.





Для кожного стовпця в data, idxmin поверне значення індексу, що відповідає мінімуму кожного стовпця; idxmax зробить те саме для максимального значення кожного стовпця.

Ви можете використовувати ці функції щоразу, коли потрібно отримати індекс рядка з мінімальним або максимальним значенням, а не саме значення.





1:

PYTHON

data['gdpPercap_1982']

2:

PYTHON

data.loc['Denmark',:]

3:

PYTHON

data.loc[:,'gdpPercap_1985':]

Пакет pandas досить розумний, щоб розпізнати число в кінці заголовку стовпця й не видати помилки, навіть якщо стовпця з назвою gdpPercap_1985 не існує. Це зручно, якщо нові стовпці додаються до файлу CSV пізніше.

4:

PYTHON

data['gdpPercap_2007']/data['gdpPercap_1952']




1. Доступ до одного стовпця:

PYTHON

# by name
data["col_name"]   # as a Series
data[["col_name"]] # as a DataFrame

# by name using .loc
data.T.loc["col_name"]  # as a Series
data.T.loc[["col_name"]].T  # as a DataFrame

# Dot notation (Series)
data.col_name

# by index (iloc)
data.iloc[:, col_index]   # as a Series
data.iloc[:, [col_index]] # as a DataFrame

# using a mask
data.T[data.T.index == "col_name"].T

2. Доступ до одного рядку:

PYTHON

# by name using .loc
data.loc["row_name"] # as a Series
data.loc[["row_name"]] # as a DataFrame

# by name
data.T["row_name"] # as a Series
data.T[["row_name"]].T # as a DataFrame

# by index
data.iloc[row_index]   # as a Series
data.iloc[[row_index]]   # as a DataFrame

# using mask
data[data.index == "row_name"]

3. Доступ до окремого елемента датафрейму:

PYTHON

# by column/row names
data["column_name"]["row_name"]         # as a Series

data[["col_name"]].loc["row_name"]  # as a Series
data[["col_name"]].loc[["row_name"]]  # as a DataFrame

data.loc["row_name"]["col_name"]  # as a value
data.loc[["row_name"]]["col_name"]  # as a Series
data.loc[["row_name"]][["col_name"]]  # as a DataFrame

data.loc["row_name", "col_name"]  # as a value
data.loc[["row_name"], "col_name"]  # as a Series. Preserves index. Column name is moved to `.name`.
data.loc["row_name", ["col_name"]]  # as a Series. Index is moved to `.name.` Sets index to column name.
data.loc[["row_name"], ["col_name"]]  # as a DataFrame (preserves original index and column name)

# by column/row names: Dot notation
data.col_name.row_name

# by column/row indices
data.iloc[row_index, col_index] # as a value
data.iloc[[row_index], col_index] # as a Series. Preserves index. Column name is moved to `.name`
data.iloc[row_index, [col_index]] # as a Series. Index is moved to `.name.` Sets index to column name.
data.iloc[[row_index], [col_index]] # as a DataFrame (preserves original index and column name)

# column name + row index
data["col_name"][row_index]
data.col_name[row_index]
data["col_name"].iloc[row_index]

# column index + row name
data.iloc[:, [col_index]].loc["row_name"]  # as a Series
data.iloc[:, [col_index]].loc[["row_name"]]  # as a DataFrame

# using masks
data[data.index == "row_name"].T[data.T.index == "col_name"].T

4. Доступ до декількох стовпців:

PYTHON

# by name
data[["col1", "col2", "col3"]]
data.loc[:, ["col1", "col2", "col3"]]

# by index
data.iloc[:, [col1_index, col2_index, col3_index]]

5. Доступ до декількох рядків

PYTHON

# by name
data.loc[["row1", "row2", "row3"]]

# by index
data.iloc[[row1_index, row2_index, row3_index]]

6. Доступ до підмножини, заданої визначеними рядками та стовпцями

PYTHON

# by names
data.loc[["row1", "row2", "row3"], ["col1", "col2", "col3"]]

# by indices
data.iloc[[row1_index, row2_index, row3_index], [col1_index, col2_index, col3_index]]

# column names + row indices
data[["col1", "col2", "col3"]].iloc[[row1_index, row2_index, row3_index]]

# column indices + row names
data.iloc[:, [col1_index, col2_index, col3_index]].loc[["row1", "row2", "row3"]]

7. Доступ до підмножини, заданої діапазонами рядків та стовпців

PYTHON

# by name
data.loc["row1":"row2", "col1":"col2"]

# by index
data.iloc[row1_index:row2_index, col1_index:col2_index]

# column names + row indices
data.loc[:, "col1_name":"col2_name"].iloc[row1_index:row2_index]

# column indices + row names
data.iloc[:, col1_index:col2_index].loc["row1":"row2"]




Серед багатьох варіантів dir() пропонує функцію median(). Таким чином,

PYTHON

data.median()


Побудова графіків


Ланч


Списки


Цикли for


Умовні оператори


Looping Over Data Sets


Післяобідня кава


Writing Functions


Область видимості змінної


Стиль програмування


Wrap-Up


Зворотний зв'язок