This is an old revision of the document!
Pe parcursul laboratoarelor, vom dori sa agregam si sa sumarizam rezultatele rularii scripturilor de ns-3 sub forma unor grafice, urmand ca apoi sa interpretam aceste grafice.
In general, workflow-ul unui laborator va arata astfel:
Rularea succesiva cu argumente variabile/dinamice va putea fi automatizata prin intermediul unui script Bash
. In mod ideal, acest script ar trebui sa salveze datele de interes intr-un fisier (.CSV preferabil) pentru a putea fi folosit ulterior pentru a obtine grafice.
Pentru realizarea graficelor, ne vom folosi de limbajul Python, de modulul de Python
matplotlib si de pachetul Jupyter Notebook pentru o vizualizare mai usoara a graficelor. Jupyter Notebook
este o aplicatie care porneste un server web local si care permite rularea de cod (Python
, Java
, etc) in cadrul unei pagini web locale care poarta numele de notebook
.
Pentru a lansa aplicatia de Jupyter Notebook, rulati urmatoarea comanda:
student@isrm-vm-2020:~$ jupyter-notebook
Aceasta comanda va porni serverul web care sta in spatele aplicatiei. Pentru a putea accesa pagina de baza a serverului web in browser, exista doua variante:
To access the notebook, open this file in a browser: file:///home/student/.local/share/jupyter/runtime/nbserver-5314-open.html Or copy and paste one of these URLs: http://localhost:8888/?token=ca54fe5b73bc1eb1f51f2f209e99eabb69117fd73664b721
Odata intrati pe pagina web, putem trece la crearea unui notebook (butonul New → Python3)
In cadrul laboratoarelor de ISRM, vom folosi Github classroom pentru a urca rezolvarile exercitiilor, pentru a primi feedback pe rezolvari/interpretari si pentru a primi nota pe laborator.
2022_lab08-10_<numePrenume>_<orice></note>
Inainte de a porni cu laboratorul, urmati pasii mentionati aici.
Workflow-ul cu git va fi similar cu cel de aici.
Fisierele de interes care vor trebui urcate intr-un pull request de Git incepand cu al treilea laborator sunt urmatoarele:
* scripturi bash sau alte fisiere prin care automatizati rularea
* fisierele de output in care salvati rezultatele obtinute in urma rularii scripturilor
* graficele (fisiere .png) / notebook-ul Jupyter aferent laboratorului
* un fisier README in care interpretati/analizati rezultatele obtinute
===== Plotarea datelor simple =====
La început, vom analiza un fișier de date. Acesta poate fi un fișier text care conține datele ca coloane. Descarcati fișierul de date numit plotting_data1.csv ce conține:
<file csv plotting_data1.csv>
# comentarii
# X Y
1 2
2 3
3 2
4 1
</file>
Pe baza acestor date, graficul poate fi construit folosind urmatorul template (pe care il puteti folosi si la restul laboratoarelor):
<code python>
import copy
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# Calea absoluta catre fisierul de date din care citim
# TODO - trebuie inlocuita cu calea corecta
DATA_FILE = '/home/student/plotting_data1.csv'
columns = ['x', 'y']
# Citim datele din fisier
# Argumentul delimiter precizeaza care este delimitatorul dintre coloane
# Argumentul skip_header precizeaza cate linii nu vor fi citite pornind cu inceputul fisierului
# Argumentul names este unul foarte util deoarece permite asocierea de nume pentru coloanele din fisier si
# de asemenea duce la un acces foarte usor al datelor in script. In acest exemplu, names va fi egal cu ['x', 'y']
# ceea ce inseamna ca putem accesa valorile din prima coloana prin sim_data['x'].
# Argumentul dtype setat specifica modul in care vor fi interpretate coloanele (string-urile ca string-uri, float-urile ca float-uri).
# In absenta acestui argument, valorile din coloane vor fi interpretate ca float.
sim_data = np.genfromtxt(DATA_FILE, delimiter=' ', skip_header=2, names=columns, dtype=None)
def plot_data(sim_data):
data = copy.deepcopy(sim_data)
# Apelul subplots poate fi folosit pentru a crea mai multe subgrafice in cadrul aceluiasi grafic sau in cadrul unor grafice diferite
# Prin figsize se specifica dimensiunea graficului
fig, ax = plt.subplots(figsize=(12,12))
# Valori stilistice pentru grafic
ax.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
# Denumirile axelor Ox si Oy, precum si titlul graficului
plt.xlabel('X values')
plt.ylabel('Y values')
plt.title('A very nice looking plot')
# Aici este construit efectiv graficul. Campul label va fi folosit in cadrul legendei graficului
ax.plot(data['x'], data['y'], label='My plot')
ax.legend()
plt.show()
if name == 'main':
plot_data(sim_data)
</code>
Introduceti acest snippet code in notebook cu mentiunea de a corecta calea data de variabila DATA_FILE. In urma rularii codului in notebook (butonul Run), imaginea rezultată este:
Pentru a include mai multe subgrafice in cadrul aceluiasi grafic, este suficient sa apelam
ax.plot pentru fiecare subgrafic nou:
<code python>
import copy
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# Calea absoluta catre fisierul de date din care citim
# TODO - trebuie inlocuita cu calea corecta
DATA_FILE = '/home/student/plotting_data1.csv'
columns = ['x', 'y']
# Citim datele din fisier
# Argumentul delimiter precizeaza care este delimitatorul dintre coloane
# Argumentul skip_header precizeaza cate linii nu vor fi citite pornind cu inceputul fisierului
# Argumentul names este unul foarte util deoarece permite asocierea de nume pentru coloanele din fisier si
# de asemenea duce la un acces foarte usor al datelor in script. In acest exemplu, names va fi egal cu ['x', 'y']
# ceea ce inseamna ca putem accesa valorile din prima coloana prin sim_data['x'].
# Argumentul dtype setat specifica modul in care vor fi interpretate coloanele (string-urile ca string-uri, float-urile ca float-uri).
# In absenta acestui argument, valorile din coloane vor fi interpretate ca float.
sim_data = np.genfromtxt(DATA_FILE, delimiter=' ', skip_header=2, names=columns, dtype=None)
def plot_data(sim_data):
data = copy.deepcopy(sim_data)
# Apelul subplots poate fi folosit pentru a crea mai multe subgrafice in cadrul aceluiasi grafic sau in cadrul unor grafice diferite
# Prin figsize se specifica dimensiunea graficului
fig, ax = plt.subplots(figsize=(12,12))
# Valori stilistice pentru grafic
ax.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
# Denumirile axelor Ox si Oy, precum si titlul graficului
plt.xlabel('X values')
plt.ylabel('Y values')
plt.title('A very nice looking plot')
# Aici este construit efectiv graficul. Campul label va fi folosit in cadrul legendei graficului
ax.plot(data['x'], data['y'], label='My plot')
ax.plot(data['y'], data['x'], label='My secondary plot')
ax.legend()
plt.show()
if name == 'main':
plot_data(sim_data)
</code>
==== Date cu erori ====
O necesitate frecventă este reprezentarea datelor cu bare de erori
pentru a indica de exemplu comportarea funcției într-un punct. În
următorul exemplu avem măsurători ale puterii pentru o rezistență
dată, stocate în formatul: r, P, Perror care poate semnifica eroarea
de măsurare a puterii:
<file csv battery.csv>
# X Y stddev
50.000000 0.036990 0.007039
47.000000 0.036990 0.007039
44.000000 0.038360 0.007053
41.000000 0.042160 0.007050
38.000000 0.043200 0.007018
35.000000 0.046900 0.007021
32.000000 0.048840 0.006963
29.000000 0.052000 0.006929
26.000000 0.055470 0.006947
23.000000 0.060000 0.006882
20.000000 0.064660 0.006879
17.000000 0.069600 0.006936
14.000000 0.079800 0.007080
11.000000 0.086920 0.007232
8.000000 0.085500 0.007262
5.000000 0.101260 0.008415
2.000000 0.091000 0.011203
0.000000 0.081480 0.011828
</file>
Vom plota astfel:
<code python>
import copy
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# Calea absoluta catre fisierul de date din care citim
# TODO - trebuie inlocuita cu calea corecta
DATA_FILE = '/home/student/battery.dat'
columns = ['resistance', 'power', 'power_error']
# Citim datele din fisier
sim_data = np.genfromtxt(DATA_FILE, delimiter=' ', skip_header=2, names=columns, dtype=None)
def plot_data(sim_data):
data = copy.deepcopy(sim_data)
fig, ax = plt.subplots(figsize=(12,12))
ax.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
plt.xlabel('X values')
plt.ylabel('Y values')
plt.title('A very nice looking plot')
ax.errorbar(data['resistance'], [1000 * x for x in data['power']], yerr=[1000 * x for x in data['power_error']], fmt='-o', label='My plot')
ax.legend()
plt.show()
if name == 'main':
plot_data(sim_data)
</code>
Valorile puterii sunt stocate în Watt în fișierul de date, dar au
valori mai mici decât 1. De aceea dorim să folosim mW ca unitate de
măsură.
===== Prelucrarea datelor în linie de comandă =====
==== Resurse ====
* Bash Hackers Wiki
* Learning Bash Scripting for Beginners - Here are a list of tutorials and helpful resources to help you learn bash scripting and bash shell itself.
* TLDP - Bash Guide for Beginners
* TLDP - Advanced Bash-Scripting Guide
==== Filtrarea datelor ====
Pentru a extrage primele sau ultimele linii dintr-un output mare folosim
head și
tail:
<code bash>
student@isrm-vm:~$ dmesg | head -n 20
student@isrm-vm:~$ dmesg | tail -n 50
</code>
Putem extrage folosind
sed explicit anumite linii. De exemplu din fișierul students.csv vrem liniile 10-16:
<code bash>
student@isrm-vm:~$ cat students.csv | sed -n '10,16p;17q'
</code>
Putem extrage informații structurate pe linii și coloane folosind utilitarul cut:
<code Bash>
student@isrm-vm:~$ cat /etc/passwd | cut -d':' -f1,6 | head -3
root:/root
daemon:/usr/sbin
bin:/bin
</code>
Utilitarul awk permite aceleași acțiuni ca și cut,
dar funcționalitatea sa este mai extinsă. Spre exemplu,
awk poate folosi o expresie regulată ca delimitator, pe când cut acceptă un singur caracter:
<code bash>
student@isrm-vm:~$ netstat -i
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 0 1955 0 0 0 521 0 0 0 BMRU
lo 65536 0 359 0 0 0 359 0 0 0 LRU
student@isrm-vm:~$ netstat -i | cut -d' ' -f1,2
Kernel Interface
Iface
eth0
lo
student@isrm-vm:~$ netstat -i | awk -F' *' '{print $1,$2,$4}'
Kernel Interface
Iface MTU RX-OK
eth0 1500 2029
lo 65536 359
</code>
Awk este considerat un fel de limbaj de programare ce vizează procesarea text.
Există script-uri awk complexe ce se aseamănă programelor C.
Printre altele, awk permite implementarea și apelarea de funcții. AWK (K vine de la Kernighan) este mic, simplu, și rapid, spre deosebire de perl sau python. Nu poți face tot ce faci în perl/python, dar poți face foarte ușor multe taskuri de procesare de text. Are o sintaxă apropiată de C, dar preferă datele organizate pe coloane, ca foarte multe date în rețelistică: trace-uri de simulare, tcpdump, loguri, etc. Un mare avantaj este ca poate fi rulat direct de pe linia de comandă,
fără a mai folosi un script separat - de multe ori apare într-un pipeline cu
cat, sed, tr.
* În cazul cel mai des întâlnit, se specifică un program care este rulat succesiv pentru fiecare linie de intrare: <code awk>cat trace.out | awk '{print $2}'</code> afișează coloana a doua a fiecărei linii. De exemplu, pentru acest fișier: <file txt trace.out>
10 2 0.2
11 3 0.3
12 2 0.2
13 3 0.1
14 4 0.05 </file>
* <code awk>cat trace.out | awk '{print $1+$2, $2 $3, i++;}'</code> produce
<code>
12 20.2 0
14 30.3 1
14 20.2 2
0 3
16 30.1 4
18 40.05 5
</code>
* Din acest exemplu se observă că:
* caracterul
= toată linia
* se pot rula mai multe programe per linie, dacă sunt activate de condiții logice/regex. Pentru o linie se execută TOATE programele care se pot activa.
* există secțiunea BEGIN{} care se rulează o singură dată înainte de input, și END{} la sfârșit
* sunt disponibile multe funcții de bibliotecă: printf, sqrt, substr, xor - vedeți man awk
==== Structuri condiționale în shell scripting ====
<spoiler Click pentru informații despre variabila scripting basics>
Un script shell este un fișier text care conține comenzi și construcții specifice shell-ului.
Un script shell începe cu construcția
#!/bin/bash, denumită shebang care indică interpretorul scriptului;
în cazul de față interpretorul este chiar shell-ul Bash. Dacă nu este specificat nici un shell
prin shebang atunci implicit va fi luat shell-ul implicit (setat în
/etc/passwd) asignat utilizatorului logat.
</spoiler>
<code bash>
#!/bin/bash
T1=“foo”
T2=“bar”
if [ “$T1” = “$T2” ]; then
echo expression evaluated as true
else
echo expression evaluated as false
fi
</code>
Script pentru a afișa doar studenții care au media > 5 din acest fisier students.csv
<code Bash>
#!/bin/bash
IFS=','
while read name group final_grade test_grade practical_grade; do
if test “$final_grade” -gt 5; then
echo “$name,$group,$final_grade”
fi
done < students.csv
</code>
Pașii de mai sus puteau fi realizați și cu ajutorul comenzii
cut. Dar, în cazul parsării folosind construcția
while read avem două avantaje:
* putem afișa coloanele în ce ordine dorim;
cut permitea afișarea de coloane doar în ordinea din fișierul de intrare;
* putem prelucra în continuare, în cadrul construcției
while read informația parsată. Spre exemplu, afișarea poate avea forma "Studentul ... face parte din grupa ...".
<spoiler Click pentru informații despre variabila
IFS>
Variabila internă shell-ului
IFS (Internal Field Separator) definește
caracterul sau caracterele care vor fi folosite pentru împărțirea unei linii în câmpuri (splitting).
Variabila internă este folosită ori de câte ori în shell este nevoie de împărțirea unei linii.
Un caz uzual de folosire a variabilei
IFS este în conjuncție cu construcția
while read
pentru citirea de linii de la intrarea standard sau dintr-un fișier și împărțirea acestora în câmpuri.
Informații și pe Wikipedia.
</spoiler>
În bash există și construcții de tipul
for. Un exemplu comun este acela de a itera prin conținutul unui
director și de a face prelucrări asupra fișierelor.
Spre exemplu, dorim să facem backup tuturor fișierelor dintr-un director trimis ca parametru în script:
<code Bash>
#!/bin/bash
for file in $1/*
do
if test -f $file; then
stat –print=”%a %F %n\n” $file
cp $file $file.bkp
fi
done
</code>
Se poate itera și pe output-ul unei comenzi:
<code Bash>
#!/bin/bash
cd ~
unzip media.zip
cd media
for file in $(find . -iname '*.jpg')
do
echo $file
done
</code>
==== Aritmetica în shell scripting ====
Bash face toate calculele pe integer, deci nu poate fi folosit pentru a calcula medii (mean, median, standard deviation).
<code bash>
$ x=5; echo $1)
bc
cu pipe pentru calcule în floating point:
$ echo '6.5 / 2.7' | bc 2 $ echo 'scale=3; 6.5/2.7' | bc 2.407
$ awc() { awk "BEGIN{print $*}"; } $ for i in `seq 1 1 4`; do awc "$i + sqrt($i)"; done 2 3.41421 4.73205 6===== Procesare multicore ===== Majoritatea graficelor pe care dorim sa le plotăm se folosesc de același script de simulare pe care îl rulăm cu parametri diferiți, iar la final recoltăm din outputul lui una sau mai multe valori. Dacă simularea nu folosește resurse temporare care pot duce la race conditions, se poate rula în paralel pentru a putea folosi core-urile existente. Utilitarul
GNU parallel
este potrivit pentru acest job, întrucât detectează automat numărul de core-uri, și are multe opțiuni pentru scriptare (nu toate naturale).
Exemple simple:
$ parallel echo "{1} a{2}" ::: $(seq 1 1 3) ::: $(seq 100 102) 1 a100 1 a101 1 a102 2 a100 2 a101 2 a102 3 a100 3 a101 3 a102Un gotcha este rularea mai multor comenzi shell care este posibilă doar prin înglobarea lor într-o funcție sau script separat:
$ cat > batch sleep $1 echo $2 $ parallel ./batch ::: $(seq 3 -1 1) ::: $(seq 100 102) 100 100 101 102 100 101 101 102 102Un altul este afișarea rezultatelor taskurilor în ordine secvențială, nu atunci când se termină fiecare:
$ parallel -k bash ./batch ::: $(seq 3 -1 1) ::: $(seq 100 102) 100 101 102 100 101 102 100 101 102Exemplu care poate fi adesea refolosit în acest semestru:
$ function run_fixed(){ echo -n "$1 $2 " ./waf --run "lab6-7-cw --payloadSize=212 --ns=$1 --nd=$1 --minCw=$2 --maxCw=$2 --pcap=false" | tail -n1 } $ export -f run_fixed $ parallel -k run_fixed {1} {2} ::: 4 6 7 20 40 ::: 3 7 15 31 63 127 255 511 1023 2047 4095Acest exemplu rulează scriptul ns3 pentru toate combinațiile de parametri minCw si maxCw listate în secventele separate de ::: Opțiunea -k asigură afișarea rezultatelor în ordine, chiar dacă în realitate se rulează în paralel pe mai multe core-uri, si unele taskuri se termină mai repede. ==== [OPȚIONAL] Trasare de grafice cu gnuplot ==== Vom folosi fișierele de aici: https://github.com/systems-cs-pub-ro/uso/tree/master/lab09/draw-plots 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 comandastudent@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.csvPutem vizualiza fișierul cu ajutorul utilitarului
evince
folosind comandastudent@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ținutulplot 'memwalk-overhead-for-kpps.csv' with linespoints, \ 'transcode-overhead-for-kpps.csv' with linespointsDupă 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.pdfObservă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 linieiunset keyPentru 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.pdfAcum 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 centerPutem 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.pdfCa 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 liniaset 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.
Oy
limitele 0
și 150
.
Un mic ghid de gnuplot găsiți aici: http://www.gnuplotting.org/plotting-data/
====== Task-uri ======
===== [01] Prelucrare fișiere =====
Folosind fișierul students.txt dorim afișarea doar a numelui studenților pentru acei studenți care au nota finală 10. Adică a treia coloana are valoarea 10.
Apoi ne propunem să realizăm un script numit extract-sort-grades
pentru a afișa numele studenților, grupa și nota finală pentru acei studenți care au nota finală cuprină între 6 și 9 (inclusiv, adică valorile 6, 7, 8, 9). Adică a treia coloana să aibă valoarea cuprinsă între 6 și 9. Apoi vom sorta intrările în ordinea notei și apoi în ordinea grupei (adică dacă au aceeași notă să fie sortați în ordinea grupei).
Ca bonus, actualizați scriptul extract-name-tab
astfel încât să primească argumente în linia de comandă notele: extract-name-tab 6 9
.
Folosind awk
(sau altă soluție) - extrageți toate notele din fișier și calculați media generală.
===== [02] Prelucrare fișiere =====
Ne propunem să afișăm grupele sortate în funcție de câți studenți din acea grupă au obținut nota 10.
Creați un script numit sort-groups-by-grade
care afișează fiecare grupă și numărul de note de 10 obținute de studenții din acea grupă, separate prin virgulă (,, comma), sortate după numărul de note de 10. Sortarea să fie inversă, adică grupele cu cele mai multe note de 10 să fie primele.
314CC,4 311CC,3 315CC,3 313CC,2 312CC,1Grupa 314CC este prima grupă afișată întrucât are cel mai mare număr de studenți care au obținut nota 10: 4 studenți. Grupa 312CC este ultima grupă afișată întrucât are cel mai mic număr de studenți care au obținut nota 10: 1 student. ===== [03] Basic plot ===== Având datele de mai jos:
Date,Open,High,Low,Close 10-03-16,774.25,776.065002,769.5,772.559998 10-04-16,776.030029,778.710022,772.890015,776.429993 10-05-16,779.309998,782.070007,775.650024,776.469971 10-06-16,779,780.47998,775.539978,776.859985 10-07-16,779.659973,779.659973,770.75,775.080017Se cere graficul de mai jos:
/proc/net/dev
conține toate interfețele de rețea care sunt în sistem. Iată exemplu de cum arată:
mihai@wormhole:~$ cat /proc/net/dev Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed ens33: 401898492 1164147 0 15 0 0 0 0 387589434 843816 0 0 0 0 0 0 lo: 404014153 1015835 0 0 0 0 0 0 404014153 1015835 0 0 0 0 0 0Fișierul
/proc/net/snmp
conține informații despre pachete recepționate de tip TCP/UDP pe sockeții din sistem. Iată un exemplu:
mihai@wormhole:~$ cat /proc/net/snmp Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout Re asmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates Ip: 2 64 1932581 0 4 0 0 0 1925281 1673627 20 8 0 0 0 0 0 0 0 Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddr MaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutA ddrMaskReps Icmp: 46 0 0 45 0 0 0 0 0 1 0 0 0 0 41 0 40 0 0 0 0 1 0 0 0 0 0 IcmpMsg: InType0 InType3 OutType3 OutType8 IcmpMsg: 1 45 40 1 Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors Tcp: 1 200 120000 -1 4228 2576 2818 50 19 1698398 1812313 1563 5 3074 0 Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti Udp: 52986 40 0 21899 0 0 0 198401 UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti UdpLite: 0 0 0 0 0 0 0 0Iterați pe conținutul acestor fișiere și afișați datele într-un format cu coloane. Datele vor fi separate printr-un singur spațiu:
iface_name packets_receive packets_transmit drops_receive drops_transmit
InUdpDatagrams InUdpErrors TcpInSegs TcpRetransSegs