Hol a szkript?

Habár írtam már pár automatikusan futó R szkriptet, mindig fejtörést okozott a munkakönyvtár beállítása. Hacsak nem eleve a szükséges mappában indult el az Rscript (márpedig a cron a $HOME-ban indítja őket), minden egyes kód a
setwd("kódot/tartalmazó/mappa")
paranccsal kezdődött. Ezzel a kénylemetlenséggel egészen addig együtt tudtam élni, amíg nem kellett megosztani a kódot; ezzel a módszerrel ugyanis amellett, hogy mások gépén nem működik, azt is megmutatja, hogy én hol tartom az adott projekt fájljait, amit nem tartok szerencsésnek.

Az Rscript programnak átadhatunk különböző paramétereket, ezek közül az egyik első a futtatandó szkript elérési útja. Az argumentumokat a commandArgs függvény visszaadja, amik közül ki lehet szedni a futtatott fájl címét - így azt már csak a dirname függvénynek kell odaadni, hogy megkapjam az eredeti kérdésre a választ. Az egészet becsomagoltam egy függvénybe:

Mostmár minden szkript első sora ugyan az:
setwd(get_script_dir())

SFTP R-ből, Ubuntun

Az Andego-nál csalásdetektálással is foglalkozunk, aminek az admin (és fejlesztői) feladatait nekem van szerencsém ellátni (ezzel el is büszkélkedtem a szeptember eleji satRday konferencián). A biztosító az adatait egy ftp-szerveren adja át nekünk, ahonnan a mi R script-ünk letölti az RCurl csomag segítségével, elmenti a megfelelő helyre, majd minden folyik a maga útján tovább. Ehhez kell egy, a hozzáférés részleteit tartalmazó link, amit a getUrl függvény fog használni:

getUrl("ftp://user:pwd@server_address:port/folder")

Ez nagyon szépen működött, egész addig, amíg egy szerverkarbantartás során nem szűnt meg az ftp kapcsolat lehetősége;

azóta csak sftp-n keresztül lehet elérni az adatokat. Én ennek nem voltam ellene, sőt, meg is nyugtatott kicsit, hogy egy fokkal biztonságosabban közlekednek az adatok a világhálón. Az ügyfélnek nem okozott gondot, így csak rajtam állt, hogy megoldom-e a problémát. E probléma pedig abban állt, hogy a korábban használt parancs módosítva sem tudott lefutni:

getUrl("sftp://user:pwd@server_address:port/folder")
Protocol sftp not supported or disabled in libcurl

Az Ubuntu farigcsálása

Így heves google-bújás kezdődött, hogy hogyan lehet a curl csomagban engedélyezni az sftp protokollt, de elsőre elég ijesztő eredménye volt ennek: Ahhoz, hogy a curl tudjon sftp-vel kapcsolódni, le kell tölteni a forrást, módosítani egy szabályokat tartalmazó fájlt, majd a forrásból felépíteni az egészet. Mivel működnie kellett, nekiláttam, hevesen reménykedve, hogy ezzel nem rontok el semmit, ami eddig működött (SPOILER: nem rontottam el semmit)
Többször nekifutottam, előbb ebből az askubuntu kérdésből kiindulva, de sajnos nem jártam sikerrel (a curl_xxx.deb fájl nem jött létre valamelyik hiba miatt a sok közül). Hozzá kell tenni a történethez, hogy a telepítéskor futó tesztek eléggé zabálják az időt, így lehet mellette mással foglalkozni, ha erre adja az ember a fejét.

Végül sok tutorial, howto és for dummies átpörgetése után egy 12.10-es ubuntuhoz adott leírás mentén vágtam újra neki a féligismertnek. Én 14.04-es ubuntuval játszottam ezt a játékot, így a verziószámok természetesen mások, illetve a libssh2-1-dev helyett elég volt a libssh2-1 csomagot letölteni.

Alább részletesen, magyarul a folyamat, amit a fenti link alapján végigjártam:

1) Legyen egy mappa amibe dolgozunk. Frissítsünk, aztán töltsük le a szükséges csomagokat:
mkdir /tmp/curl
cd /tmp/curl
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential debhelper libssh2-1
apt-get source curl
sudo apt-get build-dep curl

2) Mielőtt a következő adag kódra rátérnénk, érdemes egy pillantást vetni a curl-xxxx/debian/rules fájlra. Ha abban találunk olyat, hogy --without-libssh2, cseréljük ki --with-libssh2-re. A legfrisebb curl verzióban egyébként már semmi utalás nem volt a libssh2-re, én a biztonság kedvéért beleírtam a fájlba.

Ugyanebben a fájlban kell beállítani, hogy a gép ne végezze el az ilyenkor szokásos teszteket. Az SHLIBS_VERSION= sor elé kell beszúrni, hogy DO_TEST=no. Én ezt nem tettem meg, nem véletlen írták meg a teszteket.

3) Létrehozzuk a csomagfájlokat (ha nem így hívják, nem ez történik, e-mailben szabad panaszkodni. Erősen learning by doing alapon tanulom az Ubuntut.)
cd curl-xxxx
sudo dpkg-buildpackage

4) az összes létrejött .deb fájlt felinstalláltam (amíg nem kaptam "már telepítve" jelentésű üzenetet):
cd ..
dpkg -i curl_xxxx-1ubuntu*.deb
dpkg -i libcurl3_xxxx-1ubuntu*.deb
....

Ha most beírtam, hogy curl -V, a visszakapott protokollok között ott büszkélkedett az sftp \o/

R script farigcsálása

Nem volt vége a küzdelmeknek, mert át kellett írni a parancsokat is, amik természetesen teljesen máshogy működtek, mint az ftp-s verzióban:
A download.file "Unsupported URL scheme" hibát adott, a curlPerform-nak pedig nem sikerült megadnom a megfelelő get-es kifejezést, amivel leszedné a fájlokat. Végül az a Stackowerflow-n is megtalált megoldás nyert, hogy beolvasom a file-t R-be a getURL segítségével, majd kiírom a megfelelő helyre:

writeLines(
  getURL(paste0("sftp://user:pwd@server_address:port/folder/",
      file_to_download),
      userpwd="username:password",
      .encoding="UTF-8"),
  paste0("dest_file"))

Fontos a .encoding argumentum, enélkül nem sikerült semmiféle kódolásban megtalálnom az ékezetes betűket. Ha Windows-zal próbálkozik az ember, ISO-8859-1 a sztring amit a "latin1" helyett kell beírni.

A fájlokat természetesen vissza is kellett tenni a megfelelő helyre, ami szintén nem működött úgy, ahogy ftp esetén. Emiatt megpróbálkoztam az ftpUpload paranccsal, ami kellemes meglepetésként működött:
ftpUpload(local_file,
  paste0("sftp://user:pwd@server_address:port/folder/",
    path_to_upload))

Érdekes módon anélkül működik, hogy megadnám a jelszót és a felhasználónevet...

Ez a módszer viszont meghagyja az eredeti fájlokat, amiket így külön paranccsal kell letörölnöm. A DELETE ftp parancs nem működik, viszont a parancs amit keresek az rm.

curlPerform(url="sftp://user:pwd@server_address:port/",
  quote="rm folder/file_to_delete",
  userpwd="username:password")

Ezzel igazából minden komolyabb problémámat meg sikerült oldanom, a szkriptek pedig futnak tovább, és mindenki boldog.

Update:

Az egész folyamat után sikeresen működött minden, agészen a következő frissítésig. Ezt megakadályozandó az alábbi parancsot kell kiadni:
sudo apt-mark hold curl

useR! 1. nap

A jelenleg is napfényes Aalborgban zajlik az idei useR! konferencia.

Az első napon délelőtt és délután is négy-négy tutorial közül válogathattak a
résztvevők. Én (a többi magyar résztvevővel együtt) Csárdi Gábor Igraph-bemutatójára ültem be: a legfrissebb (1.0) verzóban már valósággal lubickolni lehet a különböző gráfmanipulálási módszerekben, szintaxisokban: a régiek (egyelőre) megmaradnak, viszont az egész bővült egy make_* függvénycsaláddal, ráadásul a make_ önmagában is megáll függvényként. Amióta utoljára elmélyültem a csomagban, jelentősen fejlődött az ábrázolás, lehet közösségeket is plottolni követlenül, nem kell a csúcsok attribútumait beállítani. Viszont lehet használni meglepően nagy mértékben: a csúcsok (vagy élek) attribútumaként is meg lehet adni a paramétereket (szín, alak, méret, bármi ami létezik). Hozzá lehet nyulni a gráf elemeihez már a "[" es "[[" operátorokkal is. Ami szintén egy nagy újítás számomra, hogy össze lehet hasonlítani a közösségeket a csomagban implementált függvényekkel, és nem kell külső csomagokból importálni a mérőeszközöket.

Délután az assertive és a testthat csomagokba kaptam egy kis bevezetőt: a két részre osztott gyakorlat első fele az előbbi, második az utóbbi csomagról szólt, hasonló felépítésben: a több mint egy órás szakaszok eleján egy bevezetőt mondott Richard J. Cotton, majd egy nagy adag házifeladaton kellett átrágni magunkat. Nem volt szó a testthat csomagnak a frissen elmentett függvényre való futtatásáról, amit Otti Levente mutatott az április BURN meetupon.

Este hétkor a helyi MüPában tartott a polgármester egy rövid beszédet, majd egy állófogadással ért véget a hivatalos program.

Szenvedés a hangokkal

Egy eszközt szeretnék készíteni, ami a felhasználóra reagálva generál hangot. Rájöttem, jogy ha a hullámformát kitalálom és megcsinálpm numpy-ban, a felhasználót a pygame-en keresztül tudom figyelni, a pygame.sndarray és a pygame.mixer modulok pedig előállítják és lejátszák a hangot. Nézzük az első felét.

Találtam egy jó példát arról, hogyan lehet szinusz-hullámot csinálni numpy-jal, ebből indultam ki. Definiáltam egy frekvenciát és egy mintaelemszámot, és megpróbáltam egy fűrészfogat készíteni, ami valahogy így néz ki:
[0, 0.25, 0.5, 0.75, 1, 0, 0.25, 0.5, 0.75, 1, ...]

A numply linspace függvényét használtam a fűrész egyetlen fogához, ami olyan hosszú, mint a minták számának egy frekvenciányi része:
fs = 44100
freq=440.
np.linspace(-1,1,fs/freq)

Ovasgatván a pygame documetációt arra is rájöttem, hogy mindennek a legelején a mixer modult kell inicializálni, és megnézni, milyen csatornát hozott létre:
import pygame
import pygame.mixer as mixer
import pygame.snarray as snd
#
mixer.init()
mixer.get_init()
# (22050, -16, 2)

Tehát két oszlopban, 16 bites számokból kell létrehoznom a huullámot.

Mivel egy fűrészhegy már megvan, kettőt kell belőle csinálnom. Erre a tile és a reshape függvény tökéletesnek tűnik: az egyetlen fogat megismétlem, majd a teljes tömböt félbevégom.
import numpy as np
fs = mixer.get_init()[0]
chs = mixer.get_init()[2]
tst_sound = np.tile(np.linspace(-1,1,fs/freq), chs).reshape(fs/freq, chs)

Már van egy nagyon rövid hangom, már csak meg kell ismételni freq-szer hogy egy másodpercig tartson. Bökkenő, hogy a tile függvény az oszlopok számát sokszorozza meg, ami most probléma, mégha érthető is. Ennek orvoslására találtam meg a transpose függvényt. Felcseréltem a sorokat és az oszlopokat, az új oszlopokat megsokszoroztam, majd visszacseréltem őket.
tst_second = np.transpose(np.tile(np.transpose(tst_sound), freq))
tst_snd = snd.make_sound(tst_second)

Ha a hang alakja megfelelő, a make_sound függvény elvileg nem ad hibát, nekem legalábbis amint megoldottam az oszlop-problémát, működött, s csak le kellett játszanom a moxer modullal:
tst_snd.play()

Fúúj. Nem ezt a hangot akartam. Nem véletlen mondja meg a mixer.get_init(), milyen típusú számokat vár. nekem ilyen típusúra kell konvertálnom a számokat:
new_type = "int"+str(abs(mixer.get_init()[1]))

A konvertáláshoz pedig kétezik egy astype metódus:
tst_snd = tst_snd.astype(new_type)

Javítás

Még mélyebbre ásva magamat a dokumentációban megkáttam, hogy a tile függvény több, mint egy dimenziót is elfogad, tehát sokkal egyszerűbben így néz ki a kód:
tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))

Ráadásul transzponálás helyett használhatom az axis argumentumát a repeat() függvénynek további egyszerűsítésként:
tst_second = np.repeat(tst_sound, frequency, 1)

Ezt midn össze tudjuk fűzni egy függvénnyé, ami a mintagyakoriságot és a csatornák számát közvetlenül a mixer modultót szerzi:
def create_snd(frequency=440., duration=.3):
    # mixer init props:
    fs = mixer.get_init()[0]
    new_type = "int"+str(abs(mixer.get_init()[1]))
    chs = mixer.get_init()[2]
    # one wave
    tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))
    # make in one second
    tst_second = np.repeat(tst_sound, frequency*duration, 1)
    # convert it to a sound and return it
    return(snd.make_sound(tst_second.astype(new_type)))

Ehhez azonban már rendesen be kell tölteni a pygame modult (nem tudom miért, mondjuk ha elolvasnám a leírását valószínűleg kiderülne :D ). A teljes folyamat az alábbi kódban található, az eredeti példából vett szinusz-hullám-gyártó függvénnyel:

def create_sin(frequency=440., duration=.3, amplitude=100):
    # mixer init props
    fs = mixer.get_init()[0]
    chs = mixer.get_init()[2]
    snd_type = "int"+str(abs(mixer.get_init()[1]))
    # time
    t = np.arange(0, duration, 1./fs)
    freq = np.array((frequency, frequency))
    sig = np.sin(2 * np.pi * freq * t.reshape(-1, 1))*amplitude
    return(snd.make_sound(sig.astype(snd_type)))
 
# program for changing notes
def int_snd_test():
    # program for changing notes
    #
    pg.init()
    pg.display.set_mode([50,50])
    mixer.pre_init(frequency=44100)
    mixer.init(frequency=44100, channels=1)
    #
    freq=440.
    done = False
    print("press w to increase, s to decrease frequency")
    #
    while not done:
        e = pg.event.wait()
        print("Waiting for user event")
        # create sound and play it:
        create_sin(frequency=freq, duration=1, amplitude=250).play()
        print("sound began to play...")
        # check user's events:
        if e.type == pg.KEYDOWN:
            if e.key == pg.K_ESCAPE:
                done = True
                pg.display.quit()
                break
            elif e.key == pg.K_w:
                freq += 100
                print("frequency up; ", freq)
            elif e.key == pg.K_s:
                freq -= 100
                print("frequency down;", freq)

Hmmm... minden szép és jó, de bizonyos frekvenciák esetén változik a kiadott hang magassága. Van bárkinek bármi ötlete? Nem találtam ilyet a Stack Overflow-n.

Ráadásul beleraktam egy időtartam és egy amplitúdó argumentumot, mert az eredeti hang halk.

Folyamatos hangot kicsikarni

A mai napom annak áldozon, hogy úgy csikarjak ki a gépől pythonnal folyamatos hangot, hogy az közben változik. A változás képességét egy for ciklussal ellenőrzöm.

Pygame.midi

A pygame midi modulját kezdtem el használni elsőre a kinecthez, ezzel azonban nem sikerült megfelelő eredményt elérnem. A baj talán ott van, hogy a midi hangok eleje más, mint a későbbi részei, így ha újra és újra elkezdem lejátszani az adott hangot, akkor remegős lesz, mint az alábbi videón is látható:

PyAudio

Ahogy a pygame-nél, itt is a probléma a változás. Találtam egy szkriptet ami szép szinusz hullámot ad ki, viszont mindig újra elő kell készítenie a hangeszközt ehhez. Ennek következtében mindig eltelik egy kis idő amíg megszólal az új hang, nem lehet folyamatosan játszani vele.

Később rájöttem, hogy ha csak a hangok összerakása előtt nyitom meg a csatornát és utánuk csukom be, akkor működik, amit szeretnék, a hangok szünet nélkül követik egymást. Ami azonban még mindig nem jó, hogy a lehető legrövidebb intervallumként "1"-et lehet megadni, viszont ez hosszú ahhoz, hogy a folyamatosan frissülő Kinect-adatokra reagáljon. Bár lehet, hogy egész egyszerűen belerakom így.

Kipróbáltam.

Minden élőlény elrohant a közelből, mert hangos és kellemetlen volt. Nagyon.

A probléma az továbbra is az időintervallummal van: a program csak az után kezdi kiszámolni a következő hangot, ha az előző végig ment. Emiatt lassan, szakadozva változott csak.

Aztán most találtam egy pygame-es megoldást, ami használja a numpy-t.

PyGame v2

Ez egy alapvetően tök szép, és érthető megoldás, teljesen szimpatikus is. Külön előny, hogy nem játsza végig a hangot, hanem ha kell, abbahagyja a régit és elkezdi az újat, így tulajdonképp vezérelhető. Azonban (legnagyobb bánatomra) ez is kattog, kikapcsol-bekapcsol. Viszont elfogad nem egész időtartamot is, de ez sajnos nem segít nekem.A trükk valahol a sndarray.make_sound() környékén lehet elrejtve, de persze az is lehet, hogy egyszerűen a lejátszandó hang generálása ilyen "eszméletlenül lassú". Mára mindenesetre feladom, nincs több ötletem.

Teremin Kinectből

Működik a tereminem. Bár dallamot játszani még nem tudok rajta, a tudat, hogy hangot tudok kiadni a kezeim mozgatásával, felettébb megnyugtató, bár nem volt egyszerű.

Beüzemelés

Ahogy utána néztem a szükséges programoknak, eszközöknek, nyílt ki a világ ezen szeglete: egyrészt Kinectből van XBOX-os és Windows-os, mindkettőből készült már egy új, korszerűbb verzió, és természetesen mindegyikhez más-más illesztőprogram kell.

Miután megtaláltam a megfelelő verziójú Kinect SDK-t, feltettem egy 64 bites Win 8.1-re. Majd nagy örömömre megtaláltam elvileg mindent, ami ahhoz kell, hogy pythonnal nyúlhassak a hardverhez. Ehhez mindössze annyit kellett tenni, hogy telepíteni kell a
- megfelelő Kinect SDK-t,
- a pythont,
- a Python Tools for Visual Studiot (ami kéri a Visual Studio-t, szóval azt is),
- a pykinect python csomagot,
- erősen ajánlott a pygame csomag.

Én ezen a folyamaton legalább 6-szor átverekedtem magamat, de nem siekrült stabilan működésre bírnom a Kinectet. Pedig találtam, és fel is telepítettem egy, a célomhoz igen közel álló programot, ami még működött is, azonban nem váltotta be a hozzá fűzött reményeket. Mivel valami sosem volt kerek, újra és újra megnéztem a telepítendő programok listáját, és akkor szúrta ki hirtelen a szememet, hogy a PVTS-t 32bites rendszerekhez ajánlják.

Miután mindezt előadtam egy 32 bites Win7 telepítéssel is, letöltöttem a KinectDemos kódot, mondván, lássuk mi van már eleve benne a rendszerben. Megörültem: a kód viszonylag érthető, a program kirajzol egy választott kameraképet, majd ha kérkük, rárajzolja a képre a megtalált csontvázakat. Mi több, tud egy egyelőre nem túl hasznosnak tartott fícsört: mozgatni is tudja a kamerát fel-le, így adott esetben valamilyen szinten a kép közepén tarthatjuk a legtöbb embert, hiába mennek közel a készülékhez. A mozgató motor azonban kifejezetten lassú, nem fogja folyamatos mozgással követni minden mozdulatunkat, főleg amiatt, hogy mozgás után előről kezdi a tér feltérképezését.

Hekkelés

Abból indultam ki, hogy a "játék" demója a képre tudta rajzolni a csontvázat: ha valahogy egyszerűen meg tudom adni, hogy hangot adjon ki a gép a kód hatására, akkor a hangerő és hangmagasság adatait már kivehetem úgy a csontvázból, ahogy a rajzoló részleg teszi.

A pygame-nek van egy midi modulja, ami úgy tud hangot generálni, hogy csak a hang erejét (0-127) és a hang magasságát (0-127) kell megadni, meg a csatornát, amin játszani kell neki. A hangszert szintén egy egész szám adja meg, amit még korábban kell deklarálni, ha nem az alapértelmezett versenyzongora (0) hangját akarja hallani az ember.

Létrehoztam egy változót, amivel be- és kikapcsolhatom a hangot, majd elkezdtem megkeresni azt a szekciót, ami a kép frissítéséért felel. Miután meglett, elkezdhettem azon ügyködni, hogy megkeressem, hogyan számolja ki az egyes hajlatok koordinátáit a program. Ez viszonylag hamar megvolt: a skeleton_to_depth_image függvény számolja ki a pozíciót, ha megadja neki a csontváz kívánt részét, és a képernyő szélességét, magasságát. Ezt például egy bal csukló esetén a csontváz[JointId.WristLeft] tartalmazza, a képernyőt én esetemben 127*127-esre állítottam, nagyobb képre úgy sincs szükségem.

Ami ennél nagyobb kihívás volt, hogy ezt nem tudtam leellenőrizni: sehogy sem akart hangot adni. nagyjából másfél óra kódban turkálás után kezdett leesni, hogy a rajzoló végigszalad az objektumon, amit megkap a Kinect-től: a megtalált összes csontvázban egyesével megrajzolja az összes ízületet. én viszont azt szerettem volna, hogy ha folyamatosan egy csontvázat követ a rendszer - ehhez előbb ki kell találni, melyik csontvázat ismeri meg, merthogy mindig másikat azonosított. Az azonosítást végül nem a TrackingState állapot vizsgálatával csináltam, egyszerűen csak annyit vártam, hogy legyen értéke a csontváz bal csuklójának.

Megzabolázni az egeret

Az utóbbi időben azon ügyködök, hogy a frissen birtokba vett laptopon használhatóra hangoljam a 14-es Ubuntut. Nem könnyű. Azon a problémán már kvázi sikeresen túllendültem, hogy elalvás után nem kapom vissza a képernyőt, csak egy teljes leállítás után. (megoldás: valamivel nem tökéletesen kompatibilis a lenovo thinkpad és az ubuntu, és ez aúgy jelenik meg, hogy az usb3.0 elérhetősége mellett nem tud felébredni. Ha kikapcsolod az usb3.0-t, akkot pedig fel tud ébredni.) Szóval jöhetett a a következő feladat: a trackpoint és a touchpad összehangolása.

A gond abból adódott alapvetően, hogy ezen a laptopon a radírhoz már nem tartoznak külön fizikai gombok, hanem a touchpad felső sávjának egyes részeit lehet akként használni. Azonban az Ubuntu erről nem tudott, így volt a gép szélénél (alul) rendesen egy jobb és egy bal egérgombom, a felső részen lévő mezők azonban mindkét oldalon balgombként működtek. Kis túrkálás után sikerült megtalálnom egy leírást az askubuntu-n, ami pontosan erről szól, annyi különbséggel, hogy ott még a touchpad által való mozgatást is meg akarta szüntetni a kérdező (amit, ha jól olvasok a sorok közt, végül nem sikerült tökéletesen). A lényeg, hogy a jobbgomb beállításához a linken megtalálható kódot kell a
/usr/share/X11/xorg.conf.d/ mappába tenni, és olyan nevet adni, ami úgy kezdődik hogy "99-" és úgy ér véget hogy ".conf", pl "99-jobbgombkeszito.conf"

A kód itt alább, ami fontos lehet, az a tabulátorok, az itt nem fog látszani, ajánlom a fenti linket tanulmányozásra.

Section "InputClass"
Identifier "tp only with clickpad buttons"
MatchDriver "synaptics"
Option "SoftButtonAreas" "60% 0 0 0 40% 60% 0 0" #Középső és jobbgomb emulálása
Option "AreaBottomEdge" "1" #mozgatás kikapcsolása, de a gombok meghagyása
EndSection

Aki pedig a touchpad mozgatásának kikapcsolását is el szeretné érni, az ugyanezt a fájlt tegye be a /etc/X11/xorg.config.d/ mappába másolnia.

Privacy's dead.

Az jutott eszembe a "Privacy's dead. What happens next?" (https://www.youtube.com/watch?v=_kBlH-DQsEg) című előadásról, hogy alapvetően nem a kereshetőség a probléma, hanem az adatok felhasználása, és hogy pár, az adatokkal visszaélő ember miatt kell lehetetlen módon védekezni. Van, aki azárt marad ki a Facebookból, hogy róla az ne gyűjtsön (annyi) adatot, más azért nem közlekedik bringával hogy ne üsse el egy autó.

Olyanok, akik "rossz" dologra használják az elérhető technológiákat, mindig lesznek. Kérdés, hogy védekezünk ellenük. Ha pedig a társadalom védekezik önmaga ellen, (https://www.youtube.com/watch?v=RIuf1V1FhpY), amikor mindenki feljelent mindenkit, akkor egy érdekes új világ alakul majd ki: a mindenkinek-megfelelés világa.

Kinect-eltem!

Ma a kezeim közé kaparintottam egy Windowshoz való Kinectet. Azonban csak XP és Lubuntu áll rendelkezésemre, szóval döntenem kellett... és kipróbáltam Linuxon. Megért.

3 leírást olvastam végig, kettőt a freenect-ről, egyet pedig az OpenNI-ről, de egyik sem működött rendesen. A negyedikhez is kellett kicsit bütykölni, de eredménnyel járt. Láttam a Kinect képét.

Pár megjegyzés az utasításokhoz: Lubuntu 12.04-et használok, működött. Érdemes mind a
Vrui-ból, mind a Kinect-ből a legfrissebb verzióval próbálkozni (most a 3.1-002 és 2.8-as verziónál járnak). A Vrui ráadásul felkínál egy .sh file-t ami elvégzi a telepítést, nekem azonban nem működött, szóval itt érdemes a tutoriál által megadott letöltöm-kibontom-lefordítom-telepítem módszert használni.

Programozó programot programozni

Arra gondoltam, jó volna, ha a sok, hasonló paraméterrel rendelkező függvényemet egyszerre elindíthatnám ugyanazon az adatsoron, például így:
eredmény függvények=c("függvény_1", "függvény_2", "függvény_n"),
közös_argumentum=változó_1,
függvény_1_argumentuma=változó_2,
függvény_v_argumentuma=változó_4)


Először is érdemes udni, hogyan hívok meg egy függvényt, amit egy változóban kapok meg, egy olyan argumentummal, amit egy másikban. Példaként vegyűk az R beépített iris adatbázisát:
# Előkészítem a függvényt és a változót:
f1 <- "mean"
v1 <- iris$Sepal.Length
# a változókban tárolt feladatot elvégeztetem:
do.call(f1, list(x=v1))
# 5.843333

A do.call függvény egy karakterként kéri a függvény nevét, és egy elnevezett listában kéri az argumentumokat. Ezek alapján, ha egyszerre több függvényt szeretnénk meghívni, használhatjuk a megszokott *apply függvénycsaládot:

# több függvényt hívok egyszerre majd meg:
f2 <- c("mean", "sd")
v1 <- iris$Sepal.Length
# sapply-jal minden függvényre megcsinálom a do.call-t
sapply(f2, do.call, args=list(x=v1))
# mean sum
# 5.843333 876.500000

Megoldható az is, hogyha az iris adatok mindegyikén (legalábbis a számokon) szeretném elvégezni a feladatot, ugyanakkor az sapply erre már nem jó:
# változók:
f2 <- c("mean", "sd")
# az iris oszlopnevei, az 5-ik kivételével. paste(), hogy eléjük tegyem a data.frame nevét:
v2 <- paste("iris", names(iris)[-5], sep="$")
# sapply
sapply(v2, function(v){sapply(f2, do.call, args=list(x=v))})

A probléma, hogy ebben az esetben az "isir$Sepal.Length" sztringre próbálja elvégezni az átlagszámítást, ami természetesen nem megy. (Egyébként próbálkoztam névvé alakítani, abban az esetben nem találja meg, és még nem tudom hogy miért). Érdemes tehát kicsit belevetni magunkat abba, hogyan íratunk meg az R-rel olyan kódokat, amiket később végre tud hajtani.

Ehhez azt kell tudnom, hogy fog kinézni a kifejezés (expression):
sapply([függvények], do.call, args=list(x=[változó]))
A [függvények] helyére a függvények neveit tartalmazó karakter vektor neve (esetemben f2), a [változó] helyére pedig az egyes oszlopok nevei kell kerüljenek. Először érdemes összerakni ezt a kifejezést:
paste0("sapply(f2, do.call, args=list(x=", [változó neve], "))")
Ebben a kódban már csak a változó nevét kell cserélgetni, amire viszont teljesen jó az sapply:
ch1 <- sapply(v2, function(v){paste0("sapply(f2, do.call, args=list(x=", v, "))")})
str(ch1)

A ch1 a kód végrehajtása után egy karakter vektor lesz, minden egyes elemének van neve, és tartalma a kód, ami egy érvényes kifejezés (lesz, most még csak egy betűhalom), és az R végre fogja tudni hajtani mindet, ha tudja, hogy értelmeznie kell őket. Az értelmezést a parse függvény, a végrehajtást pedig az eval függvény végzi. Előbbinél figyelni kell, hogy egy sztring megadása a text paraméterbe kell kerüljön, különben egy fájl tartalmát fogja keresni. Tehát a
eval(parse(text=ch1[1]))
parancs során az R megajándékoz két, névvel ellátott számmal, azt sem kell kitalálni, melyiket melyik függvény eredményeként kaptam (ez a képesség engem nagyon kellemes meglepetésként ért). Tehát nem maradt más hátra, mint előre, és az eval(parse()) kombinációt a ch1 minden tajára elvégezni:
eredmeny <- sapply(ch1, function(feladat){eval(parse(text=feladat))})
str(eredmeny)

Teljes siker: kaptunk egy függvény és egy változólistát (f2 és v2), és azt megcsináltuk azok konkrét mivoltának ismerete nélkül. Ráadásként pedig egy szépen strukturált eredményt kaptunk: egy mátrixot, annak oszlopaiban a változónevekkel, soraiban pedig a függényekkel. Ez az sapply egyik argumentumának és alapbeállításának köszönhető (simplify=TRUE), ami, ha azonos típusúak az egyes függvények eredményei, összecsomagolja őket egy vektorba/mátrixba/tömbbe, ha pedig egyikbe sem tudja, akkor listaként adja vissza az eredményt. Ennek természetesen vannak veszélyei, de ilyen egyszerű feladatnál ezt még biztos tudja kezelni az R (érdemes kipróbálni, mi történik, ha a szórás kiszámítása helyett karakterré alakítjuk a számokat - ez nem csak típusban, hanem hosszban is eltérő eredményeket fog ugyanis adni. Egyúttal itt a kérdés is, amire még nem tudom a magyarázatot: hogyan sikerül az eredményt mátrixszá összegyúrnia az sapply-nak?)

Az iris adatok tartalmazzák az egyes példányok fajtáját is, a következő(nek szánt) bejegyzésben azt részletezem majd, hogyan lehet minden fajtára elvégezni a kívánt műveleteket (lehetőleg gyorsan), illetve hogyan lehet bonyolultabb függvényeket, amik több argumentummal is rendelkeznek, ellátni a szükséges információkkal.

Oldalak