Spis treści 1 TI:WTBD/Kolokwium Poprawkowe 1.1 zadanie 1 1.1.1 1.2 zadanie 2 1.2.1 1.3 zadanie 3 1.3.1 TI:WTBD/Kolokwium Poprawkowe zadanie 1 Medianę sekwencji liczb można zdefiniować za pomocą następującego kodu: def median(x): xx = sorted(x) if not xx: return None if len(xx) == 1: return xx[] if len(xx) == 2: return sum(xx)/2. else: return median(xx[1:-1]) (kod ten służy wprowadzeniu definicji mediany, nie jest to optymalna ani nawet szczególnie dobra implementacja obliczania mediany w praktyce!) Niech T będzie tabelą, zawierającą (między innymi) kolumnę "X float not null". Napisać zapytanie SQL zwracające medianę wartości z kolumny X. Napisać program do testowania tego zapytania, tworzący tabelę z 10 000 liczb losowych (w pamięci) i porównujący medianę obliczoną za pomocą zapytania SQL z wynikiem rachunku w Pythonie na tych samych liczbach (ulepszając algorytm z powyższej definicji). def py_median(x): xx = sorted(x)
if not xx: return None limit, offset = 2 - len(xx) % 2, (len(xx) - 1) / 2 middle = xx[offset:offset+limit] return sum(middle) / float(limit) def sql_median(x): from sqlite3 import connect conn = connect(':memory:') conn.execute('create table T (X float not null)') conn.executemany('insert into T values (?)', zip(x)) #((X,) for X in x)) ((res,), ) = conn.execute(''' select avg(x) from ( select X from T order by X limit 2 - (select count(*) from T)%2 offset ((select count(*) from T) - 1)/2 )''' ) conn.close() return res def test_median(n=10000): from numpy.random import rand x = rand(n) m1 = py_median(x) m2 = sql_median(x) delta = m1 - m2 return delta N = 10000 if len(argv) == 2: N = int(argv[1]) print 'py_median - sql_median = {}'.format(test_median(n)) zadanie 2 Posługując się gotową bazą SQLite zawartą w pliku pl_lud.db napisać program, który: w odpowiedzi na argument 'q1' wypisuje listę nazw województw, po jednym na linijkę, i w tej samej linijce dla każdego z nich podaje liczbę powiatów, należących do danego województwa; w odpowiedzi na argument 'q2' wypisuje listę nazw 10 powiatów o największym zaludnieniu w
kraju, obok jego nazwy wypisując liczbę ludności i nazwę województwa, do którego należy; w odpowiedzi na argument 'q3' wypisuje średnią i odchylenie standardowe liczby ludności obliczone dla wszystkich powiatów w kraju. QUERIES = { 'q1' : ''' select w.nazwa as WOJEWODZTWO, count(*) as ILE_POWIATOW from WOJEWODZTWA w join POWIATY p on w.id=p.w_id group by w.nazwa order by w.nazwa ''', 'q2' : ''' select p.nazwa as POWIAT, p.ludnosc, w.nazwa as WOJEWODZTWO from POWIATY p join WOJEWODZTWA w on w.id=p.w_id order by p.ludnosc desc limit 10 ''', 'q3' : ''' select sum(ludnosc)/count(*), sum(ludnosc*ludnosc)/count(*) from POWIATY; ''' } def main(baza, arg): from sqlite3 import connect conn = connect(baza) cursor = conn.execute(queries[arg]) if arg in ('q1', 'q2'): rows = list() rows.append(tuple(r[] for r in cursor.description)) for r in cursor: rows.append(tuple(unicode(x) for x in r)) for r in rows: print u' '.join(r).encode('utf-8') #print r elif arg == 'q3': avg, avg2 = cursor.fetchall()[] stddev = (avg2 - avg**2)**0.5 print u'średnia: {}; sigma: {}'.format(avg,
round(stddev)).encode('utf-8') baza = 'pl_lud.db' try: arg = argv[1] except IndexError: print u'{}: wymaga dokładnie jednego argumentu.'.format( file ).encode('utf-8') exit(1) if arg in ('q1', 'q2', 'q3'): main(baza, arg) else: print u'{0}: {1}: nieznany argument.'.format( file, arg).encode('utf-8') zadanie 3 Napisać program, który na podstawie pliku tekstowego kodowanego w UTF-8 stworzy indeks słów występujących w tym pliku, w postaci tabeli SQLite zawierającej kolumny slowo (tekst słowa), wiersz (nr wiersza w którym wystąpiło) i kolumna (pozycja w wierszu). Sprawdzić działanie tego programu na podstawie pliku z tekstem 1. księgi Pana Tadeusza (Plik:PanTadeusz1.txt). W sformułowaniu tego zadania kryje się niestety pewna niejednoznaczność, której z początku nie zauważyłem:,,pozycja w wierszu" może oznaczać numer kolejny słowa w wierszu, lub pozycję w wierszu początku tego słowa jako numer kolejny znaku w wierszu -- ta druga interpretacja była zamierzona, ale oceniając traktowałem obie jako poprawne. Również jako równie poprawne traktowałem zaczynanie numeracji od zera lub od jedynki. import re import sqlite3 def words(plik): wordrx = re.compile(r'\b\w+\b', re.unicode) f = open(plik)
for num, line in enumerate(f): line = line.decode('utf-8') ## to jeśli kolumnę rozumieć jako nr słowa w linii #for pos, word in enumerate(wordrx.findall(line)): #yield word, num, pos ## a teraz wersja gdy kolumna oznacza pozycję początku słowa w znakach m = wordrx.search(line) while m: yield m.group(), num, m.start() m = wordrx.search(line, m.end()) f.close() def create(dbplik, plik): db = sqlite3.connect(dbplik) db.execute(''' drop table if exists indeks_slow db.execute(''' create table indeks_slow (slowo text, wiersz int, kolumna int, primary key (wiersz, kolumna)) db.executemany(''' insert into indeks_slow (slowo, wiersz, kolumna) values (?,?,?) ''', words(plik)) db.commit() ((n,),) = db.execute(''' select count(*) from indeks_slow return n try: plik = argv[1] except IndexError: print (u'{}: wymaga argumentu - nazwy pliku tekstowego w UTF-8.'. format( file ).encode('utf-8') ) exit(1) dbplik = plik + '.sqlite3' print u'tworzę tabelę indeks_slow w pliku {}...'.format(dbplik).encode('utf-8') nrows = create(dbplik, plik) print u'wstawiono {} wierszy.'.format(nrows).encode('utf-8')