Следующая тема: ИАД. Взаимосвязь данных

Вернуться в раздел: Исследовательский анализ данных

Вернуться в оглавление: Я.Практикум

2.Срез по данным из внешнего словаря

3.Срез по данным из внешнего словаря (продолжение)

4.Добавляем столбец

5.Добавляем столбец (продолжение)

6.Объединяем данные из двух таблиц

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

8.Объединение столбцов методами merge() и join()

9.Заключение

10.Проверочные задания. Работа с несколькими источниками данных

Введение

Вы всегда работали с одним датафреймом — все данные содержались в таблице data. Так бывает не всегда. Часто приходится объединять данные из разных источников.

Чему вы научитесь

  • Делать срез по данным из внешнего словаря.
  • Создавать новый столбец по данным из другого датафрейма, списка и Series.
  • Присваивать столбцы по порядку строк или по совпадению индексов.
  • Объединять данные из двух таблиц.
  • Применять методы join() и merge() для слияния столбцов.

Вам предстоит:

  • Избавиться от АЗС с долей «аномально быстрых» заездов больше 50%.
  • Очистить данные от заправок дольше 1000 секунд.
  • Рассчитать медианное время заправки по очищенным данным.

Сколько времени это займёт

7 уроков по 25 минут, плюс 20 минут на проверочные задания.

Постановка задачи

Избавьтесь от сверхбыстрых и аномально медленных заправок в наборе данных. Составьте рейтинг АЗС по времени заправки.

Срез по данным из внешнего словаря

В прошлой теме вы обнаружили заправки короче 60 секунд и узнали, как они распределены по АЗС. Выяснили, что на некоторых «плохих» АЗС сверхбыстрых заездов очень много. Оставив станции с такими заездами в наборе данных, вы рискуете некорректно оценить продолжительность заправки. Лучше избавиться от них.

Нужно очистить таблицу data от АЗС с аномально быстрыми заправками.

Выглядели данные так:

import pandas as pd

data = pd.read_csv('/datasets/visits.csv', sep='\t')

data['too_fast'] = data['time_spent'] < 60
data['too_slow'] = data['time_spent'] > 1000
too_fast_stat = data.pivot_table(index='id', values='too_fast')

print(too_fast_stat.head())
too_fast_stat.hist(bins=30)
too_fast
id                
00ca1b70  0.250000
011f7462  0.637489
015eaddd  0.726190
0178ce70  0.211538
018a83ef  0.510269
 
   a  b  c
0  2  5  X
1  3  4  Y
 
   a  b  c
0  0  5  X
1  1  4  Y
 
Когда в переменной сохранён объект Series, конструкция a in @our_series проверит вхождение в список значений, а не индексов:
our_series=pd.Series([10,11,12])
df = pd.DataFrame({
    'a': [0, 1, 10, 11, 12],
    'b': [5, 4, 3, 2, 1],
    'c': ['X', 'Y', 'Y', 'Y', 'Z']
})
# строим срез, в котором значения столбца a равны значениям Series, но не их индексам
print(df.query('a in @our_series')) 
    a  b  c
2  10  3  Y
3  11  2  Y
4  12  1  Z
 
Если нужно проверить вхождение в индекс, это указывают явно, дописывая index через точку: 'a in @our_series.index'.
import pandas as pd
our_series = pd.Series([10,11,12])
df = pd.DataFrame({
    'a': [0, 1, 10, 11, 12],
    'b': [5, 4, 3, 2, 1],
    'c': ['X', 'Y', 'Y', 'Y', 'Z']
})
# строим срез, в котором значения столбца a равны индексам Series, т. е. 0, 1 или 2
print(df.query('a in @our_series.index'))
   a  b  c
0  0  5  X
1  1  4  Y
Когда имеют дело с объектом DataFrame, вхождение в индекс проверяют так же, как в Series, — приписав index через точку к имени датафрейма: 
 
df = pd.DataFrame({
    'a': [0, 1, 10, 11, 12],
    'b': [5, 4, 3, 2, 1],
    'c': ['X', 'Y', 'Y', 'Y', 'Z']
})
our_df = pd.DataFrame ({
    'a1': [2, 4, 6],
    'b1': [3, 2, 2],
    'c1': ['A', 'B', 'C']
})
# строим срез, в котором значения столбца a равны индексам датафрейма our_df, т. е. 0, 1 или 2
print(df.query('a in @our_df.index')) 
   a  b  c
0  0  5  X
1  1  4  Y
 
Для проверки вхождения в какой-либо столбец, передают его имя. Построим срез из значений столбца b первого датафрейма, равных элементам столбца b1 второго датафрейма: 
df = pd.DataFrame({
    'a': [0, 1, 10, 11, 12],
    'b': [5, 4, 3, 2, 1],
    'c': ['X', 'Y', 'Y', 'Y', 'Z']
})
our_df = pd.DataFrame ({
    'a1': [2, 4, 6],
    'b1': [3, 2, 2],
    'c1': ['A', 'B', 'C']
})
# строим срез, в котором значения столбца b равны значениям столбца b1 датафрейма our_df
print(df.query('b in @our_df.b1')) 
    a  b  c
2  10  3  Y
3  11  2  Y
 
 

Результат

317104
229095

После применения правила осталось 72% (229095/317104) от первоначальных данных. Хоть 28% потерялось, качество оставшихся данных улучшилось. А значит, показатели будут более достоверными.

Результат

154601

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

Результат
             time_spent
name
Белоцветник       154.0
Бальзамин         158.0
Незабудка         161.0
Колокольчик       161.0
Обриета           167.0
Функия            171.5
Гацания           178.5
Аммобиум          180.5
Пион              180.5
Астильба          423.0
Вот он, долгожданный рейтинг! Но могут ли в нём быть ещё какие-то проблемы?
 

Добавляем столбец

В прошлом уроке вы смело выбросили неправдоподобные наблюдения.

Сравним с тем временем, что было раньше. Построим две гистограммы: по исходным данным raw (пер. «необработанный, сырой») и новым filtered (пер. «отфильтрованный»).

median_station_stat = data.pivot_table(
    index='id', values='time_spent', aggfunc='median'
)
good_stations_stat = good_data.pivot_table(
    index='id', values='time_spent', aggfunc='median'
)

ax = median_station_stat.plot(
    kind='hist',
    y='time_spent',
    histtype='step',
    range=(0, 500),
    bins=25,
    linewidth=5,
    alpha=0.7,
    label='raw',
)
good_stations_stat.plot(
    kind='hist',
    y='time_spent',
    histtype='step',
    range=(0, 500),
    bins=25,
    linewidth=5,
    alpha=0.7,
    label='filtered',
    ax=ax,
    grid=True,
    legend=True,
)
import pandas as pd

df1 = pd.DataFrame({'a': [1, 2, 3, 3, 3], 
                    'b': ['Q', 'R', 'S', 'T', 'U']})
df2 = pd.DataFrame({'c': [3, 4, 5, 6, 7], 
                    'd': ['V', 'W', 'X', 'Y', 'Z'], 
                    'e': [3, 3, 3, 3, 3]})
print(df1)
print()
print(df2)
 

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

   c  d  e
0  3  V  3
1  4  W  3
2  5  X  3
3  6  Y  3
4  7  Z  3

Чтобы добавить в df1 столбец из df2, создадим новый столбец в df1 и присвоим ему значения столбца df2
df1 = pd.DataFrame({'a': [1, 2, 3, 3, 3], 
                    'b': ['Q', 'R', 'S', 'T', 'U']})
df2 = pd.DataFrame({'c': [3, 4, 5, 6, 7], 
                    'd': ['V', 'W', 'X', 'Y', 'Z'], 
                    'e': [3, 3, 3, 3, 3]})
print(df1)
print()
print(df2)
df1['new'] = df2['d']
print()
print(df1)

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

   c  d  e
0  3  V  3
1  4  W  3
2  5  X  3
3  6  Y  3
4  7  Z  3

   a  b new
0  1  Q   V
1  2  R   W
2  3  S   X
3  3  T   Y
4  3  U   Z

Если бы столбец new уже был в df1, то все его элементы были бы удалены, а вместо них записаны новые: 

df1 = pd.DataFrame({'a': [1, 2, 3, 3, 3], 
                    'new': ['Q', 'R', 'S', 'T', 'U']}) # столбец new уже есть
df2 = pd.DataFrame({'c': [3, 4, 5, 6, 7], 
                    'd': ['V', 'W', 'X', 'Y', 'Z'], 
                    'e': [3, 3, 3, 3, 3]})
print(df1)
print()
print(df2)
df1['new'] = df2['d']
print()
print(df1)

   a new
0  1   Q
1  2   R
2  3   S
3  3   T
4  3   U

   c  d  e
0  3  V  3
1  4  W  3
2  5  X  3
3  6  Y  3
4  7  Z  3

   a new
0  1   V
1  2   W
2  3   X
3  3   Y
4  3   Z

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

   d  e
c      
3  V  3
4  W  3
5  X  3
6  Y  3
7  Z  3

   a  b  new
0  1  Q  NaN
1  2  R  NaN
2  3  S  NaN
3  3  T    V
4  3  U    W

   b
a   
1  Q
2  R
3  S
3  T
3  U

   c  d  e
0  3  V  3
1  4  W  3
2  5  X  3
3  6  Y  3
4  7  Z  3

   b new
a       
1  Q   W
2  R   X
3  S   Y
3  T   Y
3  U   Y

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

   c  d
e      
3  3  V
3  4  W
3  5  X
3  6  Y
3  7  Z

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-13-72467f8997a5> in <module>
     
ValueError: cannot reindex from a duplicate axis

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

0    1
1    2
2    3
3    4
4    5
dtype: int64

   b  new
a        
1  Q    2
2  R    3
3  S    4
3  T    4
3  U    4

Если передавать столбцу список значений, сохранённых не как Series, а, например, как list, присвоение будет идти по порядку строк:

df1 = pd.DataFrame({'a': [1, 2, 3, 3, 3], 
                    'b': ['Q', 'R', 'S', 'T', 'U']})
print(df1)
df1.set_index('a', inplace=True)
list_values = [1, 2, 3, 4, 5]
df1['new'] = list_values
print()
print(df1)

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

   b  new
a        
1  Q    1
2  R    2
3  S    3
3  T    4
3  U    5

 
Результат
             time_spent  good_time_spent
name
Агератум     337.802721            309.0
Амарант      132.760012            187.5
Аммобиум     256.708042            180.5
Арктотис      73.879984            185.0
Астильба     376.143149            423.0

Функия       302.494737            171.5
Хризантема   195.738710            188.0

Есть такая поговорка, что аналитики данных могут заставить числа доказать всё что угодно. Это как раз пример её справедливости. Обратите внимание, что в таблице good_stat отсутствуют некоторые сети АЗС, поэтому и появились значения NaN. Если вместо этого в good_stat добавить столбец со средними значениями из таблицы stat, то «Фасоль», «Годеция», «Лобулярия», «Нарцисс» и «Настурция» совсем не войдут в таблицу. А вы могли бы этого не заметить.

gas_check_ins = pd.read_csv('gas_check_ins.csv', sep=';')
gas_check_ins.head(10)
IDGas StationTime
0 4826 GasOne 60
1 1186 GasSeven 180
2 2643 GasOne 60
3 2891 GasOne 60
4 4662 GasOne 60
5 3499 GasOne 60
6 661 GasFour 180
7 3850 GasOne 60
8 1142 GasOne 60
9 852 GasOne 60
 
ID, название АЗС и продолжительность заправки в секундах. Пока всё понятно. Узнаем, сколько зафиксировано заездов на каждую заправку:
gas_check_ins['Gas station'].value_counts()
GasOne      10000
GasSeven      125
GasTen        120
GasEight      116
GasFive       113
GasThree      110
GasNine       109
GasFour       105
GasSix        102
GasTwo        100
 
Среднее арифметическое время заправки: 70.9090909090909
Медиана времени заправки: 60.0
 
 
Посчитаем среднее и медиану:
print('Среднее арифметическое время заправки:', gas_stations['Time'].mean())
print('Медиана времени заправки:', gas_stations['Time'].median())
Среднее арифметическое время заправки: 168.0
Медиана времени заправки: 180.0
 
              time_spent
    id                  
    00ca1b70       166.0
    0178ce70       234.5
    01abf4e9       181.5
    030a9067       135.5
    03740f2d       289.0
 
Непонятно, какой сети принадлежат id. Это решаемо. Вспомните, как выглядели исходные данные в data:              time_spent
 
print(data.head())
         date_time        id  time_spent     name          local_time  \
0  20180406T165358  76144fb2        98.0  Василёк 2018-04-06 19:53:58   
1  20180404T173913  76144fb2        15.0  Василёк 2018-04-04 20:39:13   
2  20180403T172824  76144fb2       220.0  Василёк 2018-04-03 20:28:24   
3  20180407T070441  76144fb2        19.0  Василёк 2018-04-07 10:04:41   
4  20180404T132049  76144fb2        14.0  Василёк 2018-04-04 16:20:49
 
     завтрак ингредиенты
0      омлет        яйца
1      омлет      молоко
2      омлет        соль
3  бутерброд        хлеб
4  бутерброд     ветчина
5  бутерброд         сыр
 
Применим pivot_table, где индексом будет завтрак, а значениями — ингредиенты. В aggfunc передадим first:
df.pivot_table(index='завтрак', values='ингредиенты', aggfunc='first')
Name    ингредиенты
завтрак    
бутерброд    сыр
омлет    соль
 

Результат
                first count
                 name  name
id
00ca1b70     Вероника   131
0178ce70      Василёк   164
01abf4e9      Гацания    30
030a9067  Колокольчик   228
03740f2d      Василёк   157

Двухэтажные названия столбцов — это оригинально. У вас не кружится голова? Нет? Сейчас закружится!

id_name = good_data.pivot_table(index='id', values='name', aggfunc=['first', 'count'])

                first count
                 name  name
id                         
00ca1b70     Вероника   131
0178ce70      Василёк   164
01abf4e9      Гацания    30
030a9067  Колокольчик   228
03740f2d      Василёк   157

Список, который передаётся в columns, должен иметь столько же элементов, сколько столбцов в датафрейме, и содержать как новые названия столбцов, так и старые в той последовательности, в которой столбцы расположены в таблице. Поэтому составлять такой список нужно осторожно — легко перепутать порядок или количество названий.

import pandas as pd

data = pd.read_csv('/datasets/visits.csv', sep='\t')

# фильтруем слишком быстрые и медленные заезды и АЗС
data['too_fast'] = data['time_spent'] < 60
data['too_slow'] = data['time_spent'] > 1000
too_fast_stat = data.pivot_table(index='id', values='too_fast')
good_ids = too_fast_stat.query('too_fast < 0.5')
good_data = data.query('id in @good_ids.index and 60 <= time_spent <= 1000')

# считаем данные по отдельным АЗС и по сетям
station_stat = data.pivot_table(index='id', values='time_spent', aggfunc='median')
good_stations_stat = good_data.pivot_table(index='id', values='time_spent', aggfunc='median')

stat = data.pivot_table(index='name', values='time_spent')
good_stat = good_data.pivot_table(index='name', values='time_spent', aggfunc='median')
stat['good_time_spent'] = good_stat['time_spent']

id_name = good_data.pivot_table(index='id', values='name', aggfunc=['first', 'count'])
id_name.columns = ['name', 'count'] 
print(id_name.head(5))

Результат

                 name  count
id
00ca1b70     Вероника    131
0178ce70      Василёк    164
01abf4e9      Гацания     30
030a9067  Колокольчик    228
03740f2d      Василёк    157

Всё простое обычно неправильно, а правильное — по большей части непросто. А у вас всё просто и правильно. Как в японском саду камней.

Объединение столбцов методами merge() и join()

                 name  count
id                          
00ca1b70     Вероника    131
0178ce70      Василёк    164
01abf4e9      Гацания     30
030a9067  Колокольчик    228
03740f2d      Василёк    157

У вас есть таблица id_name: с идентификатором каждой АЗС, названием её сети и числом заездов. Для полного счастья не хватает добавить в id_name типичную длительность заправки из good_stations_stat. Напомним, какие данные вы там сохранили: 

print(good_stations_stat.head())

У вас есть таблица id_name: с идентификатором каждой АЗС, названием её сети и числом заездов. Для полного счастья не хватает добавить в id_name типичную длительность заправки из good_stations_stat. Напомним, какие данные вы там сохранили: 

print(good_stations_stat.head())

          time_spent
id                  
00ca1b70       166.0
0178ce70       234.5
01abf4e9       181.5
030a9067       135.5
03740f2d       289.0

Объединяя id_name и good_stations_stat, можно по очереди присваивать значения столбца одной таблицы столбцу другой: 

good_stations_stat['name'] = id_name['name']
good_stations_stat['count'] = id_name['count']

      author          literary_work
0   Фонвизин              Недоросль
1  Грибоедов            Горе от ума
2     Пушкин      Капитанская дочка
3     Гоголь                Ревизор
4  Лермонтов                  Мцыри

       author         literary_work
0      Пушкин        Евгений Онегин
1      Гоголь          Мёртвые души
2   Лермонтов  Герой нашего времени
3  Островский                 Гроза
4    Тургенев           Отцы и дети

Методом merge() объединим строки датафреймов учеников по совпадающим значениям столбца author

authorliterary_work_xliterary_work_y
0 Пушкин Капитанская дочка Евгений Онегин
1 Гоголь Ревизор Мёртвые души
2 Лермонтов Мцыри Герой нашего времени
 
ФонвизинНедоросльNaN
1 Грибоедов Горе от ума NaN
2 Пуш author literary_work_x literary_work_y
0кин Капитанская дочка Евгений Онегин
3 Гоголь Ревизор Мёртвые души
4 Лермонтов Мцыри Герой нашего времени
5 Островский NaN Гроза
6 Тургенев NaN Отцы и дети
 
authorliterary_work_xliterary_work_y
0 Фонвизин Недоросль NaN
1 Грибоедов Горе от ума NaN
2 Пушкин Капитанская дочка Евгений Онегин
3 Гоголь Ревизор Мёртвые души
4 Лермонтов Мцыри Герой нашего времени
 
authorliterary_work_записал первыйliterary_work_записал второй
0 Фонвизин Недоросль NaN
1 Грибоедов Горе от ума NaN
2 Пушкин Капитанская дочка Евгений Онегин
3 Гоголь Ревизор Мёртвые души
4 Лермонтов Мцыри Герой нашего времени
 

     a  b
0  1  A
1  2  B
2  3  C
3  4  D

     a  c
0  2  E
1  2  F
2  2  G
3  2  H

0      F
1      G
2      H
3    NaN

Каждому значению в столбце 'a' первого датафрейма метод ищет соответствие в индексах второго датафрейма. И находит. В индексах второго датафрейма есть 1, 2 и 3. На экран выводят соответствующие им значения столбца 'c': F, G и H. В индексах второго датафрейма нет 4: join() не находит её и возвращает в итоговый столбец NaN. Разобравшись с работой merge() и join(), примените их к таблицам id_name и good_stations_stat. Напомним их устройство:

print(id_name.head())
print()
print(good_stations_stat.head())
 

                                 name  count
id                          
00ca1b70     Вероника    131
0178ce70      Василек    164
01abf4e9      Гацания     30
030a9067  Колокольчик    228
03740f2d      Василек    157

                    time_spent
id                  
00ca1b70       166.0
0178ce70       234.5
01abf4e9       181.5
030a9067       135.5
03740f2d       289.0

Результат

                 name  count  time_spent
id
00ca1b70     Вероника    131       166.0
0178ce70      Василёк    164       234.5
01abf4e9      Гацания     30       181.5
030a9067  Колокольчик    228       135.5
03740f2d      Василёк    157       289.0

Протянул мне робко ты 
Датафрейм, а в нём — цветы.

Результат
          median_time  stations
name
Агератум       308.50         3
Амарант        169.00         5
Аммобиум       178.75         4
Арктотис       190.50         3
Астильба       435.50         4

Говорят, что единственный человек, с кем нужно себя сравнивать — это вы в прошлом. Чем таблицы с информацией про АЗС хуже? В следующей задаче узнаем, как изменилась good_stat2 по сравнению с таблицей stat из прошлого.

import pandas as pd

data = pd.read_csv('/datasets/visits.csv', sep='\t')

# фильтруем слишком быстрые и медленные заезды и АЗС
data['too_fast'] = data['time_spent'] < 60
data['too_slow'] = data['time_spent'] > 1000
too_fast_stat = data.pivot_table(index='id', values='too_fast')
good_ids = too_fast_stat.query('too_fast < 0.5')
good_data = data.query('id in @good_ids.index')
good_data = good_data.query('60 <= time_spent <= 1000')

# считаем данные по отдельным АЗС и по сетям
station_stat = data.pivot_table(index='id', values='time_spent', aggfunc='median')
good_stations_stat = good_data.pivot_table(index='id', values='time_spent', aggfunc='median')

stat = data.pivot_table(index='name', values='time_spent')
good_stat = good_data.pivot_table(index='name', values='time_spent', aggfunc='median')
stat['good_time_spent'] = good_stat['time_spent']

id_name = good_data.pivot_table(index='id', values='name', aggfunc=['first', 'count'])
id_name.columns = ['name', 'count']
station_stat_full = id_name.join(good_stations_stat)

# считаем показатели сетей из показателей АЗС,
# а не усреднённые заезды на все АЗС сети
good_stat2 = (
    station_stat_full
    .query('count > 30')
    .pivot_table(index='name', values='time_spent', aggfunc=['median', 'count'])
)
good_stat2.columns = ['median_time', 'stations']

final_stat = stat.join(good_stat2)
print(final_stat)

Результат

             time_spent  good_time_spent  median_time  stations
name
Агератум     337.802721            309.0       308.50       3.0
Амарант      132.760012            187.5       169.00       5.0
Аммобиум     256.708042            180.5       178.75       4.0
Арктотис      73.879984            185.0       190.50       3.0

Функия       302.494737            171.5       171.50       1.0
Хризантема   195.738710            188.0       188.25       2.0

Среднее время заправки в сети «Роза» теперь оценивается как 350 секунд, а не 315. Ещё пропали данные по сети «Обриета» — значит, там были АЗС со слишком малым числом заездов. 
Как убедиться, что преобразованный набор данных лучше исходного? На графиках совместного распределения. Туда нам и дорога.

Заключение

Отлично! Вы сделали это — составили рейтинг АЗС по времени заправки. Да, его можно улучшить, чем вы и займётесь в следующих уроках, а пока подведём итоги.

Вы умеете:

  • фильтровать данные, применяя query();
  • соединять два датафрейма методом merge();
  • строить срезы с более сложными структурами: Series, словарями и датафреймами;
  • объединять несколько таблиц методом join(): их набор передают списком вместо второго датафрейма.

Чтобы найти взаимосвязи между разными значениями, перейдём к графикам совместного распределения.

Заберите с собой

Чтобы ничего не забыть, скачайте шпаргалку и конспект темы.

Где ещё почитать про объединение данных

Аналитикам: большая шпаргалка по Pandas.
Объединение датафреймов.

Проверочные задания. Работа с несколькими источниками данных

Чтобы пройти тест нужно правильно ответить на 5 вопросов из 10.
Время на прохождение: 20 минут

Задание 1 из 10
Сколько строк датафрейма выведет такой код?
our_dict = {0: 10, 1: 11, 2: 'X'}
df = pd.DataFrame({
    'a': [2, 3, 10, 11, 12],
    'b': [5, 4, 3, 2, 1],
    'c': ['X', 'Y', 'Y', 'Y', 'Z'] 
})
print(df.query('a in @our_dict')) 


0

Правильный ответ
1

2

3

Нужные данные для среза могут находиться в другом источнике, например в словаре. Отобрать строки датафрейма можно методом query(). Конструкция 'a in @our_dict' проверит, входят ли значения столбца a в ключи словаря: 0, 1, 2. В такой срез войдёт только одна строка, в которой значение a равно 2.

Задание 2 из 10
Выберите код, который выведет первую строку датафрейма. 
our_series = pd.Series([2, 11, 12])
df = pd.DataFrame({
    'a': [2, 3, 10, 11, 12],
    'b': [5, 4, 3, 2, 1],
    'c': ['X', 'Y', 'Y', 'Y', 'Z']
}) 
   a  b  c
0  2  5  X 


Правильный ответ
print(df.query('a in @our_series.index'))

print(df.query('a in @our_series'))

print(df.query('a in @our_series.values()'))

Вывести одну строку можно, проверив, совпадают ли значения a с индексами our_series: 0, 1, 2. Совпадает только строка, в которой значение a равно 2.

Задание 3 из 10
Как будут выглядеть названия столбцов и первая строка в датафрейме df_new, если применить код df_new['new'] = df_old['a']? 
df_old:

   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

df_new:

   c  d  e
0  3  V  3
1  4  W  3
2  5  X  3
3  6  Y  3
4  7  Z  3
 


Правильный ответ
   c  d  e new
0  3  V  3   1 

   c  d  e  a
0  3  V  3  1 

   c  d  e 
0  3  V  3  

   c  d  e new
0  3  V  3   0 

Индексы двух датафреймов совпадают, значит, столбец a добавится в датафрейм df_new без изменений. Только называться он будет по-другому: теперь это столбец new.

Задание 4 из 10
Как будут выглядеть названия столбцов и первая строка в датафрейме df_new, если применить код df_new['new'] = df_old['a']? 

df_old:
   a  b
0  1  Q
1  2  R
2  3  S
3  3  T
4  3  U

df_new:
   c  d  e
a  3  V  3
b  4  W  3
c  5  X  3
d  6  Y  3
e  7  Z  3
 
   c  d  e new
0  3  V  3   1 

   c  d  e 
0  3  V  3  

   c  d  e new
0  3  V  3  a 

Правильный ответ
   c  d  e new
a  3  V  3 NaN 

Добавляя новый столбец, вы не просто копируете и вставляете его в датафрейм. В pandas для каждой строки первого датафрейма будет подобрана строка с таким же индексом во втором датафрейме. Присвоение значений происходит только в случае, если индексы совпадают. У датафреймов df_old и df_new общих индексов нет, поэтому в столбец new войдут только NaN.

Задание 5 из 10
Выберите код, с помощью которого можно переименовать столбцы датафрейма.
   a  b
0  1  Q
1  2  R
2  3  S 


Правильный ответ
df.columns = ['first', 'second']

df.columns() = ['first', 'second']

df.columns == ['first', 'second']

df.column = ['first', 'second']

Изменить названия датафрейма можно с помощью атрибута columns. Новые названия присваивают атрибуту в виде списка. Обратите внимание, что длина списка должна соответствовать количеству столбцов в датафрейме.

Задание 6 из 10
Соотнесите свойство с методом.
Может соединить больше двух таблиц за один вызов.
join()
По умолчанию установлен тип слияния how='left'.
join()
Окончаниями названий столбцов управляет параметр suffixes.
merge()
По умолчанию объединяет датафреймы по типу how='inner'.
merge()
Оба метода используют для объединения таблиц, но результат может оказаться совсем разным. Зная особенности этих методов, можно выбрать подходящий для конкретной задачи.

Задание 7 из 10
Выберите команду, которая объединит датафреймы. Результатом должна стать таблица из четырёх строк с четырьмя колонками: name, age, height, salary.

import pandas as pd

df1 = pd.DataFrame({
    'name': ['Mike', 'Sam', 'Bill'], 
    'age': [23, 25, 29],
    'height': [176, 192, 182]
})

df2 = pd.DataFrame({
    'name': ['Mike', 'John', 'Bill'], 
    'salary': [100, 120, 150]
}) 


df1.merge(df2, on='name')

df1.join(df2, on='name', how='inner')

df1.join(df2, on='name', how='outer')

Правильный ответ
df1.merge(df2, on='name', how='outer')

Методы merge() и join() нельзя назвать взаимозаменяемыми. Объединить датафреймы по столбцу с общим названием можно методом merge(), а join() для такой задачи не подойдёт. Методу join() понадобятся общие индексы, которых в датафрейме df1 и df2 нет. Важно не забыть правильный тип объединения — если в общем столбце не все данные совпадают, лучше использовать «внешний» тип слияния outer.

Задание 8 из 10
Выберите код, который объединит датафреймы в такую таблицу.

import pandas as pd

df1 = pd.DataFrame({
    'name': ['Mike', 'Sam', 'Bill'], 
    'age': [23, 25, 29],
    'height': [176, 192, 182]
})

df2 = pd.DataFrame({
    'name': ['Mike', 'John', 'Bill'], 
    'salary': [100, 120, 150],
    'tax': [0, 13, 0]
}) 
   name   age  height  salary  tax
0  Mike  23.0   176.0     100    0
1  John   NaN     NaN     120   13
2  Bill  29.0   182.0     150    0 


df1.join(df2, on='name')

df1.merge(df2, on='name', how='left')

Правильный ответ
df1.merge(df2, on='name', how='right')

df1.join(df2, on='name', how='right')

Обратите внимание, что в таблицу вошли все строки датафрейма df2. Режим такого объединения — right (от англ. «правый»). Этот режим предполагает, что в таблицу обязательно войдут все строки правого датафрейма. К ним будут добавлены данные из левого датафрейма, если значения в колонке name совпадают.

Задание 9 из 10
Выберите код, который объединит датафреймы в такую таблицу. 
import pandas as pd

df1 = pd.DataFrame({
    'name': ['Mike', 'Sam', 'Bill'], 
    'age': [23, 25, 29],
    'height': [176, 192, 182]
})

df2 = pd.DataFrame({
    'name': ['Mike', 'John', 'Bill'], 
    'salary': [100, 120, 150],
    'tax': [0, 13, 0]
}) 
   name  age  height  salary  tax
0  Mike   23     176   100.0  0.0
1   Sam   25     192     NaN  NaN
2  Bill   29     182   150.0  0.0 

df1.join(df2, on='name')

Правильный ответ
df1.merge(df2, on='name', how='left')

df1.merge(df2, on='name', how='right')

df1.join(df2, on='name', how='right')

Здесь обратная ситуация: в таблицу вошли все строки датафрейма df1, а строка с именем John пропала. Такое слияние задают режимом left (от англ. «левый»). Этот режим предполагает, что в результат слияния обязательно войдут все строки из левого датафрейма.

Задание 10 из 10
Выберите код, который объединит датафреймы в такую таблицу. 
import pandas as pd

df1 = pd.DataFrame({
    'name': ['Mike', 'Sam', 'Bill'], 
    'age': [23, 25, 29],
    'height': [176, 192, 182]
})

df2 = pd.DataFrame({
    'name': ['Mike', 'John', 'Bill'], 
    'salary': [100, 120, 150],
    'tax': [0, 13, 0]
}) 
   name  age  height  salary  tax
0  Mike   23     176     100    0
2  Bill   29     182     150    0 


df1.join(df2, on='name', how='outer')

Правильный ответ
df1.merge(df2, on='name')

df1.merge(df2, on='name', how='outer')

df1.join(df2, on='name', how='inner')

В итоговую таблицу вошли только те строки, которые совпадают в двух датафреймах. Такой тип объединения называется inner (от англ. «внутренний»). В методе merge() тип inner установлен по умолчанию, поэтому его можно не указывать.

 

Следующая тема: ИАД. Взаимосвязь данных

Вернуться в раздел: Исследовательский анализ данных

Вернуться в оглавление: Я.Практикум