<![CDATA[BoonWeb - Blog]]> http://www.boonweb.de/feed/rss/blog Sat, 19 May 2012 16:06:38 +0200 (BoonWeb) BoonWeb Zend_Feed DE-de http://blogs.law.harvard.edu/tech/rss <![CDATA[3-jähriges Bestehen - das muss gefeiert werden!]]> http://www.boonweb.de/blog/article/4/3-j-hriges-bestehen-das-muss-gefeiert-werden- 4 danke zu sagen. Und wie könnten wir das wohl besser als mit folgender Aktion betonen?

Wir erlassen 30% auf die Endrechnung bei Projekten, deren Netto-Auftragswert 1500€ übersteigen und welche im März 2012 abgeschlossen werden.

Das ist also DIE Gelegenheit JETZT eine WebAPP, iOS-Applikation oder Website entwickeln zu lassen

]]>
Wed, 15 Feb 2012 23:38:49 +0100
<![CDATA[BWStream - laut.fm mal anders genießen]]> http://www.boonweb.de/blog/article/3/bwstream-laut-fm-mal-anders-geniessen 3
Die erste Version der App ist seit 13. Dezember 2011 im AppStore verfügbar.

Informationen zu verschiedenen Versionen und zusätzliche Screenshots gibt es auf BWStream.

Durch schütteln des iPhones werden zufällige Stationen vorgeschlagen. Favoriten lassen sich verwalten. Das Key-Feature ist die Eventim-Ticketsuche. Mit dieser lassen sich für den aktuell abgespielten Interpreten nach Konzerten suchen. Auch die iTunes-Suche ist integriert. So lässt sich der derzeit ablaufende Song direkt kaufen.
]]>
Sun, 18 Dec 2011 14:12:08 +0100
<![CDATA[TF-IDF - inverse Dokumenthäufigkeit]]> http://www.boonweb.de/blog/article/2/tf-idf-inverse-dokumenth-ufigkeit 2 Welchen Vorteil haben wir durch TF-IDF?

Benötigen wir zum Beispiel eine Bewertung von Suchbegriffen in einem Formular, so können wir hier eine Vorauswahl anhand der Wichtung des einzelnen Wortes treffen. Dies bietet die Möglichkeit, die Suchergebnisse von relevant bis unrelevant zu sortieren.

Wie könnte ein solches Ergebnis aussehen?

Nachfolgend unser Ergebnis nach der Formelanwendung. Die Key's entsprechen hier den Wörtern, welche gewichtet werden sollen. 1 und 2 sind die Dokumente, in denen die Gewichtung statt findet. Der zugewiesene Float-Wert entspricht letzten Endes unserem w (dem Gewicht).

array
  'Lorem' =>
    array
      1 => float 0.69314718055995

      2 => float 0.17328679513999
  'sit' =>
    array
      1 => float 0.69314718055995

      2 => float 0.17328679513999
  'ipsum' =>
    array
      1 => float 0.69314718055995

      2 => float 0.17328679513999
  'gubergren' =>
    array
      1 => float 0.69314718055995

  'Platzhalter' =>
    array
      2 => float 0.69314718055995

Die Texte 1 und 2 hierzu sind:

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

und:

„Lorem ipsum dolor sit amet, consectetur, adipisci velit …“ (vollständiger Text →Weblinks) ist ein Blindtext, der nichts bedeuten soll, sondern als Platzhalter im Layout verwendet wird. Die Verteilung der Buchstaben und der Wortlängen des pseudo-lateinischen Textes entspricht in etwa der natürlichen (lateinischen) Sprache. Der Text ist (absichtlich) unverständlich, damit der Betrachter nicht durch den Inhalt abgelenkt wird.

Wie lauten die Formeln zur Berechnung der Wichtung?

Die Vorkommenshäufigkeit tfi,j gibt an, wie häufig der Term i im Dokument j vorkommt. Wäre das Dokument 5 der aufgeführte Satz:

  • Das rote Auto hält an der roten Ampel.

dann würde folgendes gelten:

tfrot,5 = 2.

Können wir nun die inverse Dokumenthäufigkeit berechnen?

Ja und zwar mit der Formel:
idf_i = \log \frac{N}{n_i}
und der Formel:
w_{i,j} = \frac{tf_{i,j} \cdot idf_i}{max_l( freq_{l,j})} = \frac{tf_{i,j}}{max_l( freq_{l,j})} \cdot \log \frac{N}{n_i}

(siehe http://de.wikipedia.org/wiki/TF-IDF)

Kommen wir nun zur Umsetzung.

Zunächst benötigen wir ein gewisses Setup.

$wordsToCheckWith = array_unique(array('Lorem', 'sit', 'ipsum', 'gubergren', 'Platzhalter'));
$documents        = array('Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.',
                          '„Lorem ipsum dolor sit amet, consectetur, adipisci velit …“ (vollständiger Text →Weblinks) ist ein Blindtext, der nichts bedeuten soll, sondern als Platzhalter im Layout verwendet wird. Die Verteilung der Buchstaben und der Wortlängen des pseudo-lateinischen Textes entspricht in etwa der natürlichen (lateinischen) Sprache. Der Text ist (absichtlich) unverständlich, damit der Betrachter nicht durch den Inhalt abgelenkt wird.');
$w_max  = 0;
$N      = count($documents);
$result = $result_max = array();

Wir haben also Wörter und Dokumente um die Wertigkeit der Wörter innerhalb der Dokumente auszurechnen. $N ist hier die Anzahl der Dokumente, also 2. Wenn wir nun die Dokumente mit einer Schleife durchlaufen und innerhalb dieser Dokumentschleife die Wörter zählen und abgleichen wollen (Frequenz), erhalten wir die nächsten Codezeilen:

for ($j=0; $j<$N; $j++)
{
    foreach ($wordsToCheckWith as $word) {
        $frequency = substr_count(strtolower($documents[$j]), strtolower($word));

        if ($frequency > 0) {
            $result[$j][$word] = (! isset($result[$j][$word]))
                               ? $frequency
                               : ($result[$j][$word] + $frequency);

            if (!isset($result_max[$word])) {
                $result_max[$word] = $result[$j][$word];
            } elseif ($result[$j][$word] > $result_max[$word]) {
                $result_max[$word] = $result[$j][$word];
            }
            
            $result_words[$word][$j] = true;
        }
    }
}

Nun haben wir also die Dokumente, in denen die durchlaufenen Wörter am häufigsten vor kommen.

Jetzt müssen wir leider die Schleifendurchläufe erneut durchführen um die Gewichtung der Wörter zu berechnen. Das machen wir, indem wir die beiden Formeln kombinieren und wie folgt umsetzen:

$w_count = array();

for ($j=0; $j<$N; $j++)        
{
    $temp = array();
    foreach ($wordsToCheckWith as $word)
    {
        $w_cache = 0;
        
        if (isset($result[$j][$word])) {
            $w_cache = ($result[$j][$word] / $result_max[$word]) * log(($N / $result_words[$word][$j]));
            $w[$word][$j + 1] = $w_cache;

            if (!isset($w_max) || $w_max < $w_cache) {
                $w_max = $w_cache;
                $valence[$word][$j] = $w_cache;
                $temp[] = $j;
            } elseif (($w_max == $w_cache) && (!in_array($j, $temp))) {
                $temp[] = $j;
                $valence[$word][$j] = $w_cache;
            }
        }
        
        if (isset($w_count[$j])) {
            $w_count[$j] = $w_count[$j] + $w_cache;
        } else {
            $w_count[$j] = $w_cache;
        }
    }
}

Nun wären wir eigentlich fertig. Lassen wir uns nun $w ausgeben, erhalten wie die obig aufgeführte Auflistung der Wörter und deren Wertigkeit im Dokumentkorpus. Nun soll das ganze Beispiel auch noch in einem gewissen Kontext aufgezeigt werden. Hierzu habe ich folgendes Klassenbeispiel und dessen Anwendung für Sie:

<?php
class App_TFIDF
{
    /**
     * Words to get the valence for.
     *
     * @var array
     */
    protected $_words = array();

    /**
     * The documents to handle with.
     *
     * @var array
     */
    protected $_documents = array();

    /**
     * Temporary array
     *
     * @var array
     */
    private $_temp = array();

    /**
     * Number of documents.
     *
     * @var integer
     */
    private $_N = 0;

    /**
     * Valence for provided $_words.
     *
     * @var array
     */
    private $_w = array();

    /**
     * Sets documents and words if provided.
     *
     * First parameter should be a document to add.
     * The second parameter should be an array of words.
     *
     * @param mixed $options
     *
     * @return void
     */
    public function __construct($options = array())
    {
        if (func_num_args() > 1) {
            $args      = func_get_args();
            $documents = array_shift($args);

            if (is_array($documents)) {
                $this->setDocuments($documents);
            } else {
                $this->addDocument($documents);
            }

            if (!empty($args)) {
                $words = array_shift($args);

                if (is_array($words)) {
                    $this->addWords($words);
                } else {
                    $this->addWord($words);
                }
            }
        } elseif (is_string($options)) {
            $this->addDocument($options);
        } elseif (is_array($options)) {
            if (isset($options['documents'])) {
                $this->setDocuments($options['documents']);
            }

            if (isset($options['words'])) {
                $this->addWords($options['words']);
            }
        }
    }

    /**
     * Gets $_documents.
     *
     * @return array
     */
    public function getDocuments()
    {
        return $this->_documents;
    }

    /**
     * Set multiple documents at once.
     *
     * @param array $documents
     *
     * @return App_TFIDF
     */
    public function setDocuments(array $documents)
    {
        $this->_documents = array();

        foreach ($documents as $document)
        {
            $this->addDocument($document);
        }

        return $this;
    }

    /**
     * Add a document to $_documents.
     *
     * @param string $document
     *
     * @return App_TFIDF
     */
    public function addDocument($document)
    {
        if (is_string($document)) {
            $this->_documents[] = $document;
        }

        $this->_N = count($this->_documents);

        return $this;
    }

    /**
     * Reset $_documents.
     *
     * @return App_TFIDF
     */
    public function clearDocuments()
    {
        $this->_documents = array();
        $this->_N = 0;

        return $this;
    }

    /**
     * Gets $_words.
     *
     * @return array
     */
    public function getWords()
    {
        return $this->_words;
    }

    /**
     * Add multiple words at once to $_words.
     *
     * @param array $words
     *
     * @return App_TFIDF
     */
    public function addWords(array $words)
    {
        foreach ($words as $word)
        {
            $this->addWord($word);
        }

        return $this;
    }

    /**
     * Add $word to $_words if not already done.
     *
     * @param string $word
     *
     * @return App_TFIDF
     */
    public function addWord($word)
    {
        if (is_string($word) || !in_array($word, $this->_words)) {
            $this->_words[] = $word;
        }

        return $this;
    }

    /**
     * Reset $_words.
     *
     * @return App_TFIDF
     */
    public function clearWords()
    {
        $this->_words = array();

        return $this;
    }

    /**
     * Removes $word from $_words.
     *
     * @param string $word
     *
     * @return App_TFIDF
     */
    public function removeWord($word)
    {
        if (in_array($word, $this->_words)) {
            unset($this->_words[$word]);
        }

        return $this;
    }

    /**
     * Gets $_w.
     *
     * Processes formula and returns an array with valence for each word.
     *
     * @return array
     */
    public function process()
    {
        $w_max = 0;
        $result_max = array();

        for ($j=0; $j<$this->_N; $j++)
        {
            // for each word  from users question
            foreach ($this->_words as $word)
            {
                // frequency ij
                $frequency = substr_count(strtolower($this->_documents[$j]),
                                          strtolower($word));

                if ($frequency > 0) {
                    $result[$j][$word] = (!isset($result[$j][$word]))
                                       ? $frequency
                                       : $result[$j][$word] + $frequency;

                    // max(frequency ij)
                    if (!isset($result_max[$word])) {
                        $result_max[$word] = $result[$j][$word];
                    } elseif ($result[$j][$word] > $result_max[$word]) {
                        $result_max[$word] = $result[$j][$word];
                    }

                    // count($result_words[$word]) == n
                    $result_words[$word][$j] = true;
                }
            }
        }

        if (!isset($result_words)) {
            return $this->_w;
        }

        $w_count = array();

        for ($j=0; $j<$this->_N; $j++)
        {
            $temp = array();

            foreach ($this->_words as $word)
            {
                $w_cache = 0;
                if (isset($result[$j][$word])) {
                    // Now lets calculate the valence.
                    $w_cache = ($result[$j][$word] / $result_max[$word])
                               * log(($this->_N / $result_words[$word][$j]));
                    // Count +1 because the first document should not be 0 :)
                    $this->_w[$word][$j + 1] = $w_cache;

                    if (!isset($w_max) || $w_max < $w_cache) {
                        $w_max = $w_cache;
                        // Reset $max_entry to this value.
                        $valence[$word][$j] = $w_cache;
                        $temp[] = $j;
                    } elseif (($w_max == $w_cache) && (!in_array($j, $temp))) {
                        // Check if already stored in $max_entry.
                        $temp[] = $j;
                        $valence[$word][$j] = $w_cache;
                    }
                }

                if (isset($w_count[$j])) {
                    $w_count[$j] = $w_count[$j] + $w_cache;
                } else {
                    $w_count[$j] = $w_cache;
                }
            }
        }

        return $this->_w;
    }
}

Benutzt werden könnte die Klasse nun so:

        $options = array('documents' => array(
                             'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.',
                             '„Lorem ipsum dolor sit amet, consectetur, adipisci velit …“ (vollständiger Text →Weblinks) ist ein Blindtext, der nichts bedeuten soll, sondern als Platzhalter im Layout verwendet wird. Die Verteilung der Buchstaben und der Wortlängen des pseudo-lateinischen Textes entspricht in etwa der natürlichen (lateinischen) Sprache. Der Text ist (absichtlich) unverständlich, damit der Betrachter nicht durch den Inhalt abgelenkt wird.',
                         ),
                         'words'     => array(
                             'Lorem',
                             'sit',
                             'ipsum',
                             'gubergren',
                             'Platzhalter',
        ));
        $tfidf = new App_TFIDF($options);
        $w     = $tfidf->process();
        var_dump($w);
]]>
Mon, 07 Nov 2011 07:58:33 +0100
<![CDATA[[ZF] Flash Messenger Implementierung]]> http://www.boonweb.de/blog/article/1/-zf-flash-messenger-implementierung 1 Soll zum Beispiel aus einer Datenbank ein Wert gelöscht, für diese zugehörige Action jedoch keine extra View angelegt werden, so bietet es sich an dem Endbenutzer über Flash Messenger eine Nachricht anzuzeigen. Dies ist optimal durch den Action-Helfer: FlashMessenger lösbar. Hierzu speichern wir uns als erstes in der preDispatch-Schleife den zugehörigen Actionhelfer ab:

$this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
Als nächstes fügen wir dem Nachrichtenstack einen Text hinzu und übergeben das Objekt an die View/das Layout:
$this->view->messenger = $this->_flashMessenger
                              ->addMessage(array('message' => 'This is a simple test message!',
                                                 'status'  => 'info'));

Nun wäre es fast geschafft, doch fehlt noch das Rendering der Nachrichten. In unserem Layout können wir also:

echo $this->flashMessages();

eintragen und nun einen zugehörigen View-Helfer programmieren. Dieser könnte wie folgt aussehen:

/**
 * @category App
 * @package App_View_Helper
 */
class App_View_Helper_FlashMessages
    extends Zend_View_Helper_Abstract
{
    /**
     * The html string to return.
     *
     * @var string
     */
    private $_html = '';

    /**
     * @var string
     */
    private $_format = '<div class="%s"><ul>%s</ul></div>';

    /**
     * Format the flash messenger messages and return a string representation of it
     * with grouped messages for each status.
     *
     * @param Zend_Translate $translator [optional]
     *
     * @return string
     */
    public function flashMessages($translator = null)
    {
        $messages = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger')
                                                       ->getMessages();
        $statMessages = array();

        if (count($messages) > 0) {
            foreach ($messages as $message) {
                if (!array_key_exists($message['status'], $statMessages)) {
                    $statMessages[$message['status']] = array();
                }

                if ($translator != NULL && $translator instanceof Zend_Translate) {
                    array_push($statMessages[$message['status']], $translator->translate($message['message']));
                } else {
                    array_push($statMessages[$message['status']], $message['message']);
                }
            }

            foreach ($statMessages as $status => $messages) {
                $stack = '';
                foreach ($messages as $message) {
                    $stack .= '<li>' . $message . '</li>';
                }

                $this->_html .= sprintf($this->_format, $status, $stack);
            }
        }

        return $this->_html;
    }
}

Um den Quellcode so nutzen zu können, müsste natürlich vorher über die Applikationskonfiguration (Bsp.: _initView()) der Pfad zu den View-Helfern gesetzt werden.

]]>
Mon, 07 Nov 2011 07:39:44 +0100