Skip to content

Latest commit

 

History

History
388 lines (300 loc) · 21.9 KB

04-Introduction-to-Tabular-Data.md

File metadata and controls

388 lines (300 loc) · 21.9 KB

3 Введение в Табличные Данные

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

  • Ваш почтовый ящик является по сути списком сообщений. О каждом из этих сообщений ваш ящик хранит кучу информации: его отправителя, тему сообщения, тело сообщения, дату и т. д.

  • Музыкальный плейлист. Для каждой композиции плеер содержит информацию об имени артиста, о названии композиции, о названии альбома, о продолжительности композиции и многое другое: дата издания, номер по списку в альбоме, текст песни, ...

  • Папка или директория. Для каждого файла ваша файловая система записывает его имя, дату последнего изменения, размер, права на чтение/запись/исполнение для конкретных пользователей и т. д.

Что общего между всеми этими примерами? Для табличных данных характерно следующее:

  • Они состоят из строк и столбцов. Например, каждая песня, или имейл, или файл --- это строка. Каждая из их характеристик --- название композиции, тема сообщения, имя файла --- это столбец.

  • У каждой строки столько же столбцов, сколько и у любой другой, причём в том же порядке.

  • Каждый столбец обладает своим типом, но типы разных столбцов могут быть разными. Например, сообщение на электропочте содержит в себе имя отправителя, которое является строкой; тему сообщения, которая также является строкой; дату отправки, которая имеет тип даты; прочитано это сообщение или нет --- тип Boolean; и так дальше.

  • Строки обычно записаны в некотором определённом порядке. Имейлы, как правило, упорядочены по дате --- сначала идут те, которые были присланы недавно, а в конце --- те, которые пришли раньше всех остальных.

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

3.1 Создание Табличных Данных

Язык предоставляет нам несколько простых способов создания табличных данных. Самый простой --- определить данные в программе таким образом:

table: name, age
  row: "Alice", 30
  row: "Bob", 40
  row: "Carol", 25
end

То есть написать слово table, после которого записать имена столбцов в определённом порядке, после которых выписать строки таблицы. Каждая строка должна содержать ровно столько значений, сколько указано в столбцах, причём в том же порядке.

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

Заметьте, что в таблицах важен порядок столбцов: две таблицы с одинаковыми столбцами, расположенными в разном порядке, не считаются равными

check:
  table: name, age
    row: "Alice", 30
    row: "Bob", 40
    row: "Carol", 25
  end
  is-not
  table: age, name
    row: 30, "Alice"
    row: 40, "Bob"
    row: 25, "Carol"
  end
end

Конструкция с check работает так же, как и where в определении функции --- с помощью неё мы пишем тесты для нашей программы. Отличие состоит в том, что where используется для проверки какой-то одной конкретной функции, а check можно использовать для проверки всей программы целиком, написав её в конце программы.

Также здесь вместо привычного слова is используется is-not, которое, как легко догадаться, работает противоположным образом: тест считается пройденным, если значение выражения слева от is-not не равно значению выражения справа от is-not.

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

people = table: name, age
  row: "Alice", 30
  row: "Bob", 40
  row: "Carol", 25
end

Таблицы, созданные при помощи table, мы называем табличными литералами. Но есть и другие способы --- например, можно импортировать таблицу из Google Docs, указав идентификатор:

include gdrive-sheets

imported-my-table =
  load-spreadsheet("1BAexzf08Q5o8bXb_k8PwuE3tMKezxRfbKBKT-4L6UzI")

Таким образом, вы можете:

  • сами создать таблицу,

  • редактировать таблицу совместно с кем-то в Google Docs, а затем импортировать её в программе,

  • найти таблицу где-нибудь в Интернете и импортировать её

  • создать опрос, выбрать для сохранения ответов респондентов какую-нибудь таблицу, а затем импортировать ответы уже в виде таблицы в вашу программу.

3.2 Обработка строк

Давайте теперь разберёмся с тем, как мы можем обрабатывать данные внутри таблицы. Сам Pyret предоставляет нам множество операций для работы с таблицами. Однако, если этих операций нам будет недостаточно, мы имеем возможность написать свои собственные, но об этом мы поговорим позднее. А пока сфокусируемся на том, что даёт нам Pyret уже сейчас.

Давайте подумаем, какие вопросы у нас могут возникнуть к нашим данным:

  • Какие имейлы были присланы от конкретного человека?

  • Какие композиции есть у конкретного артиста?

  • У каких композиций в плейлисте больше всего прослушиваний?

  • Какие композиции слушали меньше остальных?

Видно, что некоторые из них относятся к отбору определённых строк, а другие --- к их упорядочиванию. Pyret даёт нам операции, которые позволят это сделать.

3.2.1 Отсеивание

Пусть у нас есть таблица, которая представляет собой наш электронный почтовый ящик:

email = table: sender, recipient, subject
  row: 'Саша', 'Маша', 'Очень срочно!!!!!!11'
  row: 'Amazon', 'Маша', 'Verify your new Amazon account'
  row: 'Саша', 'Маша', 'А3 распечатать'
  row: 'Zoom', 'Маша', 'Активируйте свою учётную запись Zoom'
end

и мы хотим получить таблицу сообщений от Саши.

Сделать это мы можем так:

sieve email using sender:
  sender == 'Саша'
end

--- здесь мы сообщаем о том, что хотим просеять таблицу email, используя столбец sender. Эта операция обрабатывает каждую строку таблицы. Для каждой строки переменная sender означает значение столбца sender в этой строке. Значение выражения внутри (между : и end) должно иметь тип Boolean. Если это true, то Pyret оставляет эту строку для итоговой таблицы, в противном случае он её отбрасывает. Результатом выполнения такого запроса является новая таблица с теми же самыми столбцами и лишь некоторыми --- отсеянными --- строками (в принципе, в результате строк может не оказаться вообще).

Подобным же образом можно выбрать строки из таблицы с плейлистом по имени артиста:

sieve playlist using artist:
  (artist == 'Sangam') or (artist == 'Kid Smpl')
end

Указание: напишите таблицу playlist которая будет работать с sieve выражением выше.

Указание: напишите sieve выражение с таблицей email, результатом которого будет таблица с нулём строк.

3.2.2 Упорядочивание

Мы можем использовать order выражение для упорядочивания строк. В результате исполнения этого выражения получается таблица со строками, расположенными в указанном порядке:

order playlist:
  play-count ascending
end

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

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

order playlist:
  play-count descending,
  artist ascending,
  song ascending
end

3.2.3 Совмещаем отсеивание и упорядочивание

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

  • Какое из писем от конкретного человека самое старое?

  • Какую из композиций конкретного артиста слушали реже всех остальных?

Вот первый пример:

sasha-emails = sieve email using sender:
  sender == 'Саша'
end
order sasha-emails:
  sent-date ascending
end

Заметьте, что в order выражении мы упорядочиваем не таблицу email, которая является таблицей со всеми письмами, а только sasha-emails --- таблицу с письмами от одного конкретного человека.

3.2.4 Расширение

Иногда нам может быть нужно добавить к таблице столбец, чьи значения будут основаны на значениях других, уже имеющихся в таблице, столбцов. Например, наша таблица может отражать записи о работниках какой-нибудь компании и иметь столбцы hourly-wage и hours-worked, содержащие соответственно размер почасовой оплаты и количество отработанных часов. И нам бы хотелось добавить ещё один столбец, в котором будут записаны зарплаты для каждого работника:

extend employees using hourly-wage, hours-worked:
  total-wage: hourly-wage * hours-worked
end

--- это создаёт новый столбец total-wage, чьё значение в каждой строке равно произведению значений названных выше столбцов. Pyret добавит этот столбец справа в конце. Далее мы увидим, как можно менять порядок столбцов.

Разумеется, расширения мы можем комбинировать с другими табличными операциями. Например, мы можем сначала создать столбец subject-length, который содержит в себе длины тем сообщений, а затем отсортировать сообщения по этой длине так, чтобы сначала шли сообщения с наиболее подробными темами:

ext-email = extend email using subject:
  subject-length: string-length(subject)
end
order ext-email:
  subject-length descending
end

3.2.5 Преобразование, Очистка и Нормализация

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

В обоих случаях мы хотим оставить у таблицы её исходную «форму» --- те же столбцы, те же строки, всё в том же порядке ---, но слегка изменяя значения некоторых столбцов. В Pyret это можно сделать с помощью transform. Вот пример с заказами:

transform orders using count:
  count: num-min(count, 3)
end

в котором мы устанавливаем максимально возможное количество заказов для каждого отдельного покупателя.

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

transform weather using temp, unit:
  temp:
    if unit == "F":
      fahrenheit-to-celsius(temp)
    else:
      temp
    end
  unit:
    if unit == "F":
      "C"
    else:
      unit
    end
end

--- здесь мы для изменения значений temp используем значения unit и в целом этим преобразованием конвертируем все температуры в градусы Цельсия.

3.2.6 Выборка

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

select name, total-grade from gradebook end

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

ss = select artist, song from playlist end
order ss:
  artist ascending
end

3.2.7 Итоговый обзор табличных операций

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

Операция Содержимое ячейки Порядок строк Число строк Порядок столбцов Число столбцов
Отсеивание уменьшает
Упорядочивание изменяет
Расширение не изменяет существующие, создаёт новые увеличивает
Преобразование изменяет
Выборка изменяет уменьшает

Обратите внимание на то, что слова «изменяет» и «уменьшает» следует читать как «возможно изменяет» и «возможно уменьшает».

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