Pentru a descărca fișierele necesare acestui laborator deschidem un terminal (folosim combinația de taste Alt+Ctrl+t
) și clonăm repository-ului oficial uso. Folosim comanda:
student@midgard:~$ git clone https://github.com/systems-cs-pub-ro/uso
În directorul /home/student/uso/lab09
găsim fișierele necesare pentru laboratorul 9.
Pe parcursul acestui laborator, vom urmări moduri în care putem stoca, analiza și prelucra datele. Termenul în limba engleză pentru prelucrarea datelor este data processing sau data analysis.
În continuare, împreună cu asistentul veți parcurge câteva exerciții de tip demo/tutorial pentru a vă acomoda cu noțiunile, metodele și utilitarele pentru prelucrarea datelor.
Un alt mod de stocare a informațiilor este sub formă tabelară. Fiecare o să ocupe propria linie în cadrul tabelului, iar fiecare atribut o să aibă propria coloană.
Nr. | Producator | Model | Frecventa | Cache |
1. | Intel | i7-5960X | 3.00GHz | 20MB Cache |
2. | Intel | i7-5930K | 3.50GHz | 15MB Cache |
3. | Intel | i7-4790K | 4.00GHz | 8MB Cache |
4. | AMD | FX X8 9370 | 4.70GHz | 16MB Cache |
5. | AMD | FX X8 9370 | 4.40GHz | 16MB Cache |
Există o mulțime de formate de fișiere, care se bazează pe stocarea informațiilor sub forma unor tabele. Cele mai importante sunt CSV, ODT, ODS etc. Datele pot fi stocate fie în forma binară, fie în text clar.
Aplicații care sunt specializate pe lucrul cu astfel de formate sunt: Microsoft Excel, LibreOffice Calc, Google Sheets.
O bază de date, reprezintă un mod de stocare a informațiilor pe un suport extern.
Ca şi utilitate, bazele de date ne permit:
Pornind de la același model de structurare a datelor (sub forma tabelară) s-au dezvoltat bazele de date relaționale. Acestea sunt un tip de baze de date în care datele, văzute ca şi atribute ale entităţilor reale, sunt stocate în tabele şi sunt legate între ele prin relaţii.
Acest mod de structurare a datelor, bazat pe legături între date, permite eliminarea redundanţei, astfel încât stocarea şi, mai ales, modificarea unei informaţii se face într-un singur loc, iar, din punct de vedere funcţional, această structură permite regăsirea, filtrarea, ordonarea şi agregarea datelor, în mod natural.
Pentru manipularea și accesul la bazele de date relaționale, se folosește limbajul SQL.
Pentru a putea rula comenzi SQL, avem nevoie de o bază de date pe care să lucrăm:
student@midgard:~/uso/lab09$ sqlite3 lab09 < db/lab09.dump
Cea mai simplă comandă, care listează toate intrările dintr-o tabelă este următoarea:
student@midgard:~/uso/lab09$ sqlite3 lab09 "select * from students"; 1|Ionescu|Ion|7.0 2|Gheorghescu|Gheorghe|3.0 3|Vasilescu|Vasile|8.0 4|Marinescu|Marian|10.0
Dacă vrem să limităm afișarea doar la coloana cu nume (filtrare pe coloane), folosim comanda:
student@midgard:~/uso/lab09$ sqlite3 lab09 "select nume from students"; Ionescu Gheorghescu Vasilescu Marinescu
Dacă se dorește afișarea unui număr mai mic de intrări (filtrare pe linii), se folosește cuvântul cheie WHERE:
student@midgard:~/uso/lab09$ sqlite3 lab09 "select * from students WHERE nota > 5"; 1|Ionescu|Ion|7.0 3|Vasilescu|Vasile|8.0 4|Marinescu|Marian|10.0
sed este un stream editor. Sintaxa pentru sed
este:
sed /pattern/ action input_files
Pattern este o expresie regulată (vom învăța mai multe despre expresii regulate în laboratorul următor). Caracterele / din jurul expresiei regulate sunt folosite ca delimitatori.
Action este acțiunea ce trebuie executată când este identificat un pattern. Acțiunea este executată asupra liniei pe care a fost găsit patternul. Acțiuni uzuale sunt:
p
afișează linia d
șterge linia s/pattern1/pattern2/
substituie pattern1 cu pattern2
Când este invocat, sed
urmează un set de pași:
Să luăm un exemplu:
student@ubuntu:~/uso/lab09/sed_examples$ cat db_select.out 1|Ionescu|Ion|7.0 2|Gheorghescu|Gheorghe|3.0 3|Vasilescu|Vasile|8.0 4|Marinescu|Marian|10.0 student@ubuntu:~/uso/lab09/sed_examples$ sed 'p' db_select.out 1|Ionescu|Ion|7.0 1|Ionescu|Ion|7.0 2|Gheorghescu|Gheorghe|3.0 2|Gheorghescu|Gheorghe|3.0 3|Vasilescu|Vasile|8.0 3|Vasilescu|Vasile|8.0 4|Marinescu|Marian|10.0 4|Marinescu|Marian|10.0 student@ubuntu:~/uso/lab09/sed_examples$ sed -n 'p' db_select.out 1|Ionescu|Ion|7.0 2|Gheorghescu|Gheorghe|3.0 3|Vasilescu|Vasile|8.0 4|Marinescu|Marian|10.0
Acțiunea p
afișează fiecare linie din input. Observăm că pentru prima comandă dată fiecare linie din input apare de două ori. Asta se întâmplă deoarce sed afișează implicit fiecare linie pe care o procesează. Acțiunea p
afișează încă o data fiecare linie la stdout. Pentru a anula acest comportament, vom folosi opțiunea -n
care dezactivează afișarea implicită a fiecărei linii.
student@ubuntu:~/uso/lab09/sed_examples$ sed -n '/Marinescu/p' db_select.out 4|Marinescu|Marian|10.0
Putem folosi sed
pentru a avea același comportament ca grep
. Comanda de mai sus caută pe fiecare linie șirul 'Marinescu' și afișează liniile pe care șirul este găsit.
student@ubuntu:~/uso/lab09/sed_examples$ sed -n 's/Vasil/Gigel/p' db_select.out 3|Gigelescu|Vasile|8.0
Cel mai adesea vom folosi sed
pentru a substitui diverse patternuri. În exemplul de mai sus este căutat pe fiecare linie șirul 'Vasil' și este înlocuit cu 'Gigel'. Observăm că deși există mai multe potriviri pe aceeași linie, comportamentul default sed
este să înlocuiască doar prima apariție. La ieșire sunt afișate liniile pe care patternul a fost găsit.
Pentru a înlocui toate aparițiile din linie, folosim opțiunea g
(global).
student@ubuntu:~/uso/lab09/sed_examples$ sed -n 's/Vasil/Gigel/gp' db_select.out 3|Gigelescu|Gigele|8.0
sed
o include pe cea a lui tr.
De multe ori, una dintre cele mai bune forme de prezentare a datelor e în forma unor grafice. Există mai multe tipuri de grafice (cu puncte, cu linii, pie chart, bar chart etc.) decizia de a folosi un tip sau altul aparținând observatorului uman.
Pentru trasarea de grafice, putem folosi utilitarul gnuplot. Gnuplot primește comenzi care pot fi trecute într-un script pentru a trasa grafice.
Pentru un exemplu de utilizare a gnuplot, accesăm subdirectorul draw-plots/
din directorul laboratorului:
student@mjolnir:~/uso.git/lab11/process-table-data$ cd ../draw-plots/ student@mjolnir:~/uso.git/lab11/draw-plots$ ls draw-overhead-for-kpps.gnu transcode-overhead-for-kpps.csv memwalk-overhead-for-kpps.csv
În acest director sunt două fișiere format CSV care conțin rezultatele unor experimente ce folosesc trafic de rețea. Fișierele memwalk-overhead-for-kpps.csv
și transcode-overhead-for-kpps.csv
conțin overhead-ul în secunde pentru diverse viteze de trafic de rețea, măsurate în kilopackets per second (kpps). Prima coloană din fiecare fișier reprezintă viteza în kpps
, iar a doua coloană overhead-ul în secunde. Cele două fișiere indică overhead-ul indus în momentul rulării unor aplicații numite respectiv memwalk
și transcode
.
Tot aici se găsește și un script gnuplot, draw-overhead-for-kpps.gnu
care trasează graficul dependenței overhead-ului față de viteza de trafic pentru aplicația memwalk
.
Pentru a rula scriptul folosim comanda
student@mjolnir:~/uso/lab09/draw-plots$ gnuplot draw-overhead-for-kpps.gnu
În urma rulării rezultă fișierul overhead-for-kpps.pdf
:
student@mjolnir:~/uso/lab09/draw-plots$ ls draw-overhead-for-kpps.gnu overhead-for-kpps.pdf memwalk-overhead-for-kpps.csv transcode-overhead-for-kpps.csv
Putem vizualiza fișierul cu ajutorul utilitarului evince
folosind comanda
student@mjolnir:~/uso/lab09/draw-plots$ evince overhead-for-kpps.pdf
Întrucât dorim să plasăm pe același grafic și datele pentru aplicația transcode
, vom adăuga în script și fișierul de intrare transcode-overhead-for-kpps.csv
. Pentru aceasta vom schimba ultima linie din script în două linii care vor avea conținutul
plot 'memwalk-overhead-for-kpps.csv' with linespoints, \ 'transcode-overhead-for-kpps.csv' with linespoints
După ce schimbăm ultima linie din fișier conform indicațiilor de mai sus și după ce salvăm fișierul, rulăm din nou scriptul și deschidem fișierul rezultat overhead-for-kpps.pdf
:
student@mjolnir:~/uso/lab09/draw-plots$ gnuplot draw-overhead-for-kpps.gnu student@mjolnir:~/uso/lab09/draw-plots$ evince overhead-for-kpps.pdf
Observăm că nu avem legendă și nu ne putem da ușor seama care grafic reprezintă care aplicație. În scriptul draw-overhead-for-kpps.gnu
am dezactivat legenda prin folosirea liniei
unset key
Pentru a activa legenda comentăm acea linie în cadrul scriptului prefixând-o cu simbolul diez (#
, hash):
#unset key
După ce am comentat linia care dezactiva legenda, rulăm din nou scriptul și deschidem fișierul rezultat overhead-for-kpps
:
student@mjolnir:~/uso/lab09/draw-plots$ gnuplot draw-overhead-for-kpps.gnu student@mjolnir:~/uso/lab09/draw-plots$ evince overhead-for-kpps.pdf
Acum legenda apare pe grafic, dar este plasată neconvenabil în partea de sus a ecranului. Cel mai bine este ca legenda să fie plasată în partea dreapta centru. Pentru aceasta scriem în fișier linia
set key right center
Putem scrie linia oriunde înainte de linia plot
. O putem scrie imediat după linia comentată mai sus. După ce am adăugat linia care poziționează legenda în partea dreapta centru, rulăm din nou scriptul și deschidem fișierul rezultat overhead-for-kpps
:
student@mjolnir:~/uso/lab09/draw-plots$ gnuplot draw-overhead-for-kpps.gnu student@mjolnir:~/uso/lab09/draw-plots$ evince overhead-for-kpps.pdf
Ca ultimă schimbare, putem configura graficul să folosească pe post de limite pentru ordonată (axa Oy
) reprezentând overhead-ul, valorile 0
și 150
. Pentru aceasta adăugăm în script linia
set yrange [0:150]
Putem scrie linia oriunde înainte de linia plot
.
Pentru estetică, putem să precizăm etichetelor din legendă. Pentru aceasta înlocuim liniile care defineau graficul (ultimele două linii din script) cu liniile de mai jos, care folosesc cuvântul cheie title
pentru a preciza etichetele în legendă:
plot 'memwalk-overhead-for-kpps.csv' with linespoints title 'memwalk', \ 'transcode-overhead-for-kpps.csv' with linespoints title 'transcode'
Apoi rulăm din nou scriptul și deschidem fișierul rezultat. Rezultatul final trebuie să fie similar imaginii de mai jos.
În acest moment avem un grafic care indică dependența overhead-ului de viteza traficului pentru două aplicații. Datele au fost prelucrate din două fișiere de intrare în format CSV, conținând două coloane: prima cu viteza traficulului (în kilopackets per second) și a doua cu overhead-ul cauzat de aplicație (în secunde). Am trasat două grafice de tipul linespoints (puncte și linii între puncte), am plasat legenda în partea din centru dreapta a graficului, am creat etichete pentru legendă și am configurat pentru axa Oy
limitele 0
și 150
.
Pentru a descărca fișierele necesare acestui laborator deschidem un terminal (folosim combinația de taste Alt+Ctrl+t
) și clonăm repository-ului oficial uso. Folosim comanda:
student@midgard:~$ git clone https://github.com/systems-cs-pub-ro/uso
În directorul /home/student/uso/lab09
găsim fișierele necesare pentru laboratorul 9.
Trebuie să ne asigurăm că avem atât clientul cât și serverul de MySQL instalate pe stația pe care lucrăm:
student@uso:~$ sudo apt-get update; sudo apt-get install sqlite student@uso:~$ sudo apt-get install mysql-server-5.5 mysql-client-core-5.5
Dacă nu puteți instala aceste pachete, încercați cu versiunea 5.7:
student@uso:~$ sudo apt-get install mysql-server-5.7 mysql-client-core-5.7
student@uso:~$ sudo apt-get update
root
pentru utilizatorul root
.
Pentru a descărca fișierul, folosiți un browser în mod grafic și (Firefox sau Chrome) și apoi folosiți intrarea File > Download As > Comma Separated Value
. Asigurați-vă că separatorul este virgula (,)
În exercițiile precedente, am modificat câteva fișiere de tip CSV. Am văzut că putem să și filtrăm textul în linie de comandă.
Vrem acum să stocăm toate aceste informații într-o bază de date relațională ce poate fi interogată ulterior de mai multe aplicații.
După instalare pornim mysql
:
student@uso:~/uso$ mysql -u root -proot Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 42 Server version: 5.5.44-0ubuntu0.14.04.1 (Ubuntu) Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
Creăm o bază de date goală, fără tabele:
mysql> create database uso; Query OK, 1 row affected (0.00 sec)
Pentru a valida comanda de mai sus folosim:
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | uso | +--------------------+ 4 rows in set (0.00 sec)
Creăm o tabelă care imită CSV-ul descărcat. Trebuie să alegem baza de date cu care lucrăm. Aceasta va fi uso
:
mysql> use uso Database changed mysql> create table studenti( nume varchar(100) not null, grupa varchar(10) not null, nota1 float, nota2 float, nota3 float);
Pentru a valida corectitudinea comenzii folosim comanda:
mysql> describe studenti;
Pentru a importa un fișier CSV versiunea actuală de MySQL nu ne lasă în mod implicit. Trebuie să oprim consola folosind combinația de taste CTRL+d
și să pornim mysql
astfel:
student@uso:~/uso$ mysql -u root -proot --local-infile=1 uso
Pentru importarea CSV-ului se folosește în mysql
următoarea comandă:
mysql> load data local infile '/path/to/file/students.csv' into table studenti columns terminated by ',' lines terminated by '\n';
În loc de /path/to/file/students.csv
dați calea către locul în care ați salvat fișierul Excel descărcat ca CSV din Google Spreadsheets. În caz de succes observați output-ul dat de mysql
:
Query OK, 91 rows affected (0.01 sec) Records: 91 Deleted: 0 Skipped: 0 Warnings: 0
Aceasta ne spune că au fost adăugate 91 de intrări. Puteți verifica faptul că CSV-ul avea 91 de intrări folosind wc:
student@uso:~/uso$ cat students.csv | wc -l
Bazei noastre de date îi lipsește o cheie primară. Aceasta se poate adăuga folosind comanda (asigurați-vă că folosiți baza de date uso
când lucrați):
mysql> alter table studenti add id int unsigned not null auto_increment,add primary key (id); Query OK, 92 rows affected (0.06 sec) Records: 92 Duplicates: 0 Warnings: 0
Putem verifica acest lucru afișând primele 10 intrări din tabela studenti
:
mysql> select * from studenti limit 10; +-----------------------------------+-------+-------+-------+-------+----+ | nume | grupa | nota1 | nota2 | nota3 | id | +-----------------------------------+-------+-------+-------+-------+----+ | VLĂDUȚU I. Liviu-Alexandru | 311CC | 6 | 3.5 | 5.22 | 1 | | GEORGIU V. Alexandra-Maria | 311CC | 10 | 10 | 9.67 | 2 | | PĂUNOIU N. Gabriel | 311CC | 7 | 6.5 | 3.5 | 3 | | BĂCÎRCEA A. Andrei | 311CC | 7 | 5.5 | 4.44 | 4 | | BOU V. Paul | 311CC | 7 | 5.75 | 3.6 | 5 | | PETRIA O.T. Cristina | 311CC | 4 | 2.25 | 4 | 6 | | DRĂGAN Al. Andrei-Teodor | 311CC | 6 | 2.25 | 4.9 | 7 | | BURGHELEA C. Stelian | 311CC | 9 | 8.75 | 8 | 8 | | POPESCU A. Rodica | 311CC | 7 | 7.5 | 5.5 | 9 | | NIȚĂ‚ I. Diana-Maria | 311CC | 5 | 5.5 | 2.67 | 10 | +-----------------------------------+-------+-------+-------+-------+----+
Ne propunem să efectuăm următoarele sarcini simple în MySQL:
nume
, grupa
și nota1
- așadar, dorim afișarea notei 1 pentru toți studenții.nota1
și afișarea tuturor coloanelor. Puteți porni de la exemplele de SELECT oferite în această pagină.
JSON (JavaScript Object Notation) este un format de stocare a datelor ca text human-readable sub formă de perechi cheie-valoare. Este adesea folosit în limbaje de programare precum Javascript pentru a agrega și transmite date între diverse componente. Un exemplu clasic este transmiterea datelor de la un sever la o pagină web.
{ "title": "Example Schema", "type": "object", "properties": { "firstName": { "type": "string" }, "lastName": { "type": "string" }, "age": { "description": "Age in years", "type": "integer", "minimum": 0 } }, "required": ["firstName", "lastName"] }
Mai multe detalii despre structura acestui fișier puteți găsi aici.
Pentru acest exercițiu vom trimite un request către acest site folosind curl pentru a obține lista de utilizatori înrolați. Răspunsul de la server va fi în formatul JSON
.
resources
.
Salvați răspunsul în fișierul user_list.json
.
Folosind sed
, afișați intrarea dinjson
care corespunde utilizatorului cu id-ul 2
. Va trebui să căutați după linia ce conține id-ul și să afișați următoarele linii din intrare.
Man sed
si căutați după lines following
.
Folosind sed
, modificați user_list.json
astfel încât atributul “phone” din toate intrările să fie înlocuit cu “mobile_phone”.
Este deseori util să știm să prelucrăm informațiile pe care le obținem de la utilitarele Linux. În acest sens dorim că datele să fie salvate într-un format bine precizat.
Când dorim să aflat informații despre sistemul de fișiere, 2 utilitare des folosite sunt find și stat. Cu find am mai lucrat în laboratorul 6.
Sarcina voastră este să listați primele 5 fișiere obișnuite (regular files) din ierarhia /etc (adică din toate directoarele, subdirectoarele și subdirectoarele acestora etc.) sortate crescător în ordinea dimesiunii ocupate de fiecare fișier. Outputul trebuie salvat în fișierul 'uuuuge_files.txt
, fără a include erorile.
stat -c ... $(find ...)
Apoi filtrați output-ul comenzii de mai sus cu ajutorul comenzii sort pentru a sorta numeric în funcție de dimensiunea fișierului. Adică o construcție de forma:
stat -c ... $(find ...) | sort ...
Apoi filtrați output-ul pentru a extrage doar primele 5 fișiere, în ordinea dimensiunii. Folosiți tail
.
Obțineți fișierul index.html
al paginii web http://elf.cs.pub.ro. wget
Dorim să eliminăm toate tag-urile HTML din index.html
folosind sed
. Sed va primi ca pattern o expresie regulată. Fișierul ar trebui să aibă următorul conținut.
<html> <head> <meta name="google-site-verification" content="gTsIxyV43HSJraRPl6X1A5jzGFgQ3N__hKAcuL2QsO8" /> </head> <body> <h1>It works!</h1> </body> </html>
<
și apoi face match pe oricâte caractere până când este întâlnit carcaterul >
. Output-ul așteptat este următorul:
It works!
După ce am eliminat tag-urile, observăm că textul nu apare la început de rând întrucât este precedat de o suita de whitespaces. Dorim să eliminăm și toate whitespace-urile (s
) aflate la început de linie.
It works!
Întrucât am eliminat toate spațiile de la început de linie, liniile care conțineau numai spații au rămas goale. Observăm că în output apar multe astfel de linii. Dorim să le eliminăm și pe acestea folosind sed.
delete
a lui sed pentru a o șterge.
It works!
Folosind datele din fișierul grades.txt
ne propunem să creăm o histogramă a notelor obținute de studenți; adică un grafic care are pe axa Ox
notele studenților de la 1
la 10
iar pe axa Oy
prezintă câți studenți au luat nota respectivă. Astfel, pentru fiecare notă va exista o bară care va indica câți studenți au obținut acea notă.
Fișierul grades.txt
are formatul:
[...] 15 8 15 9 13 10
Ultima linie înseamnă că au fost obținute 13 note de 10, penultima linie înseamnă ca au fost obținute 15 note de 9, iar antepenultima linie înseamnă că au fost obținute 15 note de 8
Pentru aceasta, accesați subdirectorul draw-plots/
și creați o copie a scriptului draw-overhead-for-kpps.gnu
numită draw-student-grades-histogram.gnu
. Acest script va folosi la intrare fișierul grades.txt
din directorul părinte și va genera la ieșire un fișier PDF numit student-grades.pdf
cuprinzând histograma notelor studenților.
Ox
și prima coloană pentru axa Oy
folosiți construcția using 2:1
la sfârșitul liniei cu plot
. Nu mai e nevoie de construcția with linespoints
întrucât nu dorim grafic cu linii între puncte.
Detalii despre folosirea construcției using
găsiți aici. Căutați după șirul Using using în cadrul paginii.
Folosiți comanda gnuplot
cu argument scriptul draw-student-grades-histogram.gnu
pentru a genera la ieșire graficul cu puncte în fișierul student-grades.pdf
. Deschideți fișierul folosind comanda evince
. Rezultatul final trebuie să fie similar imaginii de mai jos.
set style data histogram set style fill solid border
și modificați linia de plot pentru a specifica ce coloană este folosită pentru axa 0x
și ce coloană pentru axa Oy
. Sintaxa pentru histogramă este de forma using 1:xticlabels(2)
pentru a folosi a doua coloană pentru axa Ox
și a prima coloană pentru axa Oy
.
Detalii despre crearea de histograme în gnuplot găsiți aici și aici.
Folosiți comanda gnuplot
cu argument scriptul draw-student-grades-histogram.gnu
pentru a genera la ieșire graficul cu histogramă în fișierul student-grades.pdf
. Deschideți fișierul folosind comanda evince
. Rezultatul final trebuie să fie similar imaginii de mai jos.
Pe parcursul acestui laborator am acoperit câteva aspecte practice legate de moduri în care putem stoca, analiza și prelucra datele. Datele sunt preluate dintr-un suport și format specific de stocare și apoi sunt prelucrate și prezentate într-o formă utilă observatorului uman: rapoarte, tabele, grafice, rezultate numerice.
Un mod uzual de stocare a informațiilor este sub formă tabelară, unde fiecare intrare o să ocupe propria linie în cadrul tabelului, iar fiecare atribut o să aibă propria coloană.
Pornind de la același model de structurare a datelor (sub forma tabelară) s-au dezvoltat bazele de date relaționale. Acestea sunt un tip de baze de date în care datele, văzute ca şi atribute ale entităţilor reale, sunt stocate în tabele şi sunt legate între ele prin relaţii. Pentru manipularea și accesul la bazele de date relaționale, se folosește limbajul SQL.
Sed este un stream editor util pentru manipularea datelor. Sed poate fi folosit pentru afișa, șterge sau substitui patternuri intr-un text primit la intrare. Cel mai adesea vom folosi sed pentru a substitui diverse patternuri.
De multe ori, una dintre cele mai bune forme de prezentare a datelor e în forma unor grafice. Pentru trasarea de grafice, putem folosi utilitarul gnuplot. Gnuplot primește comenzi care pot fi trecute într-un script pentru a trasa grafice.