вторник, 25 декабря 2007 г.

Мимоходом

Почитал отрывки из книги о применении СОК в анализе финансовых данных. Понравилась четырнадцатая глава с конкретными рецептами - советами. Первая глава даёт хорошее представление о том, что есть СОК. Оцифровка любительская.

Уважаю авторов сайта http://www.omegahat.org и пакетов, выложенных на нём. В основном там разнообразные привязки, как, например, Rlibstree к http://www.icir.org/christian/libstree/ или Rstem.

среда, 19 декабря 2007 г.

День пятый (пакет XML)

ЗАДАЧА
Выполнить синтаксический анализ HTML документа, выяснить возможности выполнения XPath запросов.

РЕШЕНИЕ
Некоторое время я потратил на "ложный след" - решил проводить вычистку (xml-изацию) документа с помощью JTidy. В связи с чем поставил rJava. К сожалению, это пакет не очень продуман в плане удобства пользования - пользователь обязывается использовать соглашения JNI для именования типов, например,

> .jcall(f, "Ljava/awt/Component;", "add", .jcast(b, "java/awt/Component"))
и делается это непоследовательно - есть разница в объявлении типа для .jcall и.jcast. Также эти коварные ';'. От пользователя требуется помнить про зоопарк (или иметь под рукой)
I integer D double (numeric) J long (*)  F float (*)
V void Z boolean   C char (integer)  B byte (raw)
и соглашения для массивов: [L/java/lang/String. Видимо из - за разных моделей типизации в R и Java, не реализовано отслеживание наследования Java - классов, поэтому код
...
> in <- .jnew("java/io/ByteArrayInputStream")
> .jcall(tidy, "Lorg/w3c/tidy/Node;", "parse", in, out)
не работает, поскольку требует явного приведения:
> .jcall(tidy, "Lorg/w3c/tidy/Node;", "parse", 
        .jcast(in,"java/io/InputStream"),  
        .jcast(out,"java/io/OutputStream"))
Так что, промежуточный вывод такой - при работе с rJava необходимо как можно больше кода писать на Java (Groovy? - интересный опыт, но что - то есть сомнения в успешности), оставляя на стороне R самый минимум. Что, в общем, очевидно.

Спустя некоторое время обнаружил, что в доках на пакеты в разделе Usage могут перечисляться не только синонимы имён функций, но и различные функции, а пакет XML реализует всю необходимую функциональность для работы с HTML.
htmlTreeParse(file, ignoreBlanks = TRUE, handlers = NULL,
              replaceEntities = FALSE, asText = FALSE, 
              trim = TRUE, isURL = FALSE, asTree = FALSE,
              useInternalNodes = FALSE, encoding = character(),
              useDotNames = length(grep("^\.", names(handlers))) > 0,
              xinclude = FALSE, addFinalizer = TRUE) 
парсит документ, представленный в виде дискового файла (возможно сжатого), URL, строки текста.

Задачу решает следующий код:
...
> doc <- htmlTreeParse(file, useInternalNodes=T)
> scnd_table <- getNodeSet(doc, "/html/body/table[2]")
> length(scnd_table)
1
> names(scnd_table[[1]])
ВЫВОД
Пакет XML- превосходная работа! Есть возможность обращения к удалённому документу, выполнения XPath запросов к содержимому, DOM и SAX механизмы обработки. Определённо, если я возьмусь делать свой "Большой Проект", он будет на R.

четверг, 6 декабря 2007 г.

День четвёртый (web harvesting)

ЗАДАЧА
Для работы с данными из веба надо сначало извлечь их. Затем сконвертировать в объекты, понимаемые R. Поскольку содержание страниц может меняться, надо как - то автоматизировать сбор данных, по возможности сделав его устойчивым к изменению страницы или, по крайней мере, легко модифицируемым.

РЕШЕНИЕ
Полностью автоматизировать сбор, основываясь, например, на регулярности элементов DOM не получится - слишком часто встречаются тучи без смысла вложенных TABLE. Поэтому я сделал ставку на визуальное выделение "интересных" элементов страницы с последующим назначением инструкций по их обработке. Для работы плагина нужен прокси, чтобы добавить в тело страницы строчки:

script src="scripts/jquery.js" type="text/javascript"
script src="scripts/jqDnR.js" type="text/javascript"
script src="scripts/selector.rule.js" type="text/javascript"
а также загрузить сами сценарии из scripts/. Можно обойтись и без jqDnR.js - функциональность небольшая, а мороки много. Кое - что я сам поправил - убрал прозрачность (были из - за неё проблемы). С остальным разбираться - нет знаний и желания. Плагин работает неторопливо, поэтому, в частности, два режима работы. В одном клик на навигационной панели приводит к прыжку (откату выделения), в другом - к предпросмотру содержимого элемента.
/*
* SelectorRule - jQuery plugin for a quick navigation over 
* html tree
*
* Author: dimiii
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
* Version: r1
*
*/

(function($) {
 var style = {
   tasks_panel : {
     "position" : "absolute", "zIndex" : "500", "width" : "75%",
     "padding" : "5px", "margin" : "0px",
     "background-color" : "#eff",  "border" : "1px solid #999",
     "textAlign" : "left"
   },
   hint_area : {
     "padding" : "5px", "margin" : "5px", "width" : "95%", 
     "height" : "200px",
     "background-color" : "#eff", "border" : "1px dotted"
   },
   cmd_area : {
     "padding" : "5px", "margin" : "5px", 
     "width" : "50%", "height" : "80px", 
     "border" : "1px dotted"
   },
   mouseover : {
     "border" : "2px solid lime"
   },
   mouseclick : {
     "border" : "3px solid green"
   }
 };

 /*
  Node selector contains
 */
 $.fn.nodeSelector = function() {
   var click_trace = [];
   var tasks_panel = null;   

   if (!tasks_panel) {
     tasks_panel = $() // It seems blogger doesn't understand pre tag.
// So here is a commented tasks_panel's skeleton.
     .appendTo("BODY")
     .hide()
     .bind("click", function(e) {
       e.stopPropagation();
     });
        
     $(this).bind("contextmenu", function(e) {
       display(tasks_panel, e, click_trace);
       activate_actions(tasks_panel, click_trace);
       return false;
     });
   }

   mk_clickable(document.body, click_trace);
  
   return this;
 };

 function display(tasks_panel, e, click_trace) {
  
   $(".jqHandle").css("background-color", "lightgrey");

   $("#panel_stuff")
   .empty()
   .append(get_mode_slctr())
   .append(get_jump_nav(click_trace, true))
   .append(get_hint_text(click_trace))
   .append(get_command_panel())
   .append(get_btns());
  
   $("A")
   .css({"font-weight" : "bold", 
         "text-decoration" : "none", "color" : "black"})
   .bind("mouseover", function() { $(this).css("color", "red"); })
   .bind("mouseout", 
     function() { $(this).css("color", "black"); });
   
   tasks_panel
   .css(style.tasks_panel)
   .css({"left":0.15* document.width,"top":e.pageY})
   .show();
 };

 function activate_actions(tasks_panel, click_trace)
 {
   $(".jump_nav")
   .bind("click",
     function() {
       if ("selected" == $("#jumpModeBtn").attr("class")) {
      
         var  clcked = click_trace[$(this).attr("id")],
              trace_pos = click_trace.pop(); 
      
         while(trace_pos != clcked) {
           $(trace_pos.childNodes).each(
             function() { brk_clickable(this); });
           $(trace_pos).css({"border" : trace_pos.cssMemo || ""});
           trace_pos = click_trace.pop();
         }
     
         click_trace.push(clcked);//back
      
         $(clcked.childNodes).each(
           function() { mk_clickable(this, click_trace); });
        
         tasks_panel.hide();
       } else {
         $("#hint_area")
         .empty()
         .append($(click_trace[$(this).attr("id")]).text());
       }
     });

   $("#closeBtn")
   .bind("click", function(){ tasks_panel.hide(); });

   $("#commitBtn")
   .bind("click",  function(){ 
                     alert(get_jump_nav(click_trace, false));
                     tasks_panel.hide(); });
  
   $("#insPathBtn")
   .bind("click", 
     function(){ $("#cmd_area")
                 .append(get_jump_nav(click_trace, false)); });
  
   $("#jumpModeBtn")
   .bind("click",
     function() {
       if("selected" != $(this).attr("class")) {
         $(this).empty().append("[jump]").addClass("selected");
         $("#viewModeBtn")
         .empty().append("view").removeClass("selected");
       }
     });
  
   $("#viewModeBtn")
   .bind("click",
     function() {
       if("selected" != $(this).attr("class")) {
         $(this)
         .empty().append("[view]").addClass("selected");
         $("#jumpModeBtn")
         .empty().append("jump").removeClass("selected");
       }
     });
 
   tasks_panel.jqDrag(".jqDrag");
 };

 function mk_clickable(docnode, click_trace) {
   if ($(docnode).attr("id") == "tasks_panel") return;

   switch (docnode.nodeName.toString().toLowerCase()) {
     // we can't make clickable tbody and tr, 
     // so let's make clickable their childs
     case "tbody":
     case "tr":
       $(docnode.childNodes).each(
         function() { mk_clickable(this, click_trace); });
     break;

     case "body":
     case "div":
     case "p":
     case "span":
     case "a":
     case "table":
     case "td":
       try {
         docnode.cssMemo = $(docnode).css("border");
       }catch(exc){}

       $(docnode)
       .bind("mouseover", 
         function() { $(this).css(style.mouseover); })
       .bind("mouseout",  
         function() { $(this).css({"border" : docnode.cssMemo  
                                              || ""}); })
       .bind("click", 
         function() {
           click_trace.push(this);
           $(this).css(style.mouseclick);

           switch(docnode.parentNode.nodeName.toString()
                 .toLowerCase()) {
             case "tbody":
             case "tr":
               $(docnode.parentNode.parentNode.childNodes).each(
                 function() { brk_clickable(this); });
             break;

             default:
               $(docnode.parentNode.childNodes).each(
                 function() { brk_clickable(this); });
             break;
           }

           $(docnode.childNodes).each(
             function() { mk_clickable(this, click_trace); });
         });
     break;
   }
 };

 function brk_clickable(docnode) {
   switch(docnode.nodeName.toString().toLowerCase()) {
     case "tbody":
     case "tr": // reasons like for mk_clickable
       $(docnode.childNodes).each(
         function() { brk_clickable(this); });
     break;
        
     default:
       $(docnode)
       .unbind("mouseover").unbind("click").unbind("mouseout");
     break;
   }
 };

 /*
   Implementations of other functions are skipped.
 */

})(jQuery);
$(function() {
 $("BODY A").attr("href", "#");// defence
 $("BODY A").unbind("click", function() { mk_clickable(this);} );

 $("BODY").nodeSelector();
});
Инструкции предполагается записывать на DSL или непосредственно на R. Окончательный выбор сделаю после более подробного знакомства с библиотекой XML.

ВЫВОД
Работает и каши не просит.

БОНУС
Сегодня, через весьма насыщенный http://twit88.com узнал о существовании http://web-harvest.sourceforge.net. Основательный подход, но пока я никаких преимуществ перед моим "тынц-тынц" методом не вижу. Всё же, следует знать.

пятница, 2 ноября 2007 г.

А мне книгу дали

Читать времени нет, но отдавать пора. Я отсканировал из неё интересные места. Книга называется что - то вроде: "Анализ финансовых данных и самоорганизующиеся карты Кохонена." Прикладной направленности книга. Но к моим данным, как можно применять SOM, пока не вижу. Хотя для первичного анализа очень симпатично смотрятся картинки, да. Выложу сканы с соответствующей меткой. Забавно - в процессе поиска способа выгрузить файл нашлось готовое решение - http://r.lab.works.googlepages.com/home С готовым акаунтом. Чёрт! Всё таки Google is Evil. И это, определённо, обернётся чем - то плохим. А пока 100 бесплатных мб и идиотский интерфейс персонализации. В качестве теста выложил "Дядя Петрос и проблема Гольдбаха".

понедельник, 22 октября 2007 г.

День третий (an investigation)

TASK
An Investigation. R and tail recursion. Lazy evaluation in R.

SOLUTION
With a Google's help i found this message: http://finzi.psych.upenn.edu/R/Rhelp02a/archive/73651.html
> Some functional languages have a feature called tail recursion that
> can provide performance improvements if you write your recursions
> to take advantage of it:
>
> http://en.wikipedia.org/wiki/Tail_recursion
>
> but I don't think R supports it. Is this likely to become available
> in R?
No. For a start, this is usually done by compilers, which we don't got.
In addition, it would be very difficult to do tail recursion optimization and not break most of the sys.* functions, which give explicit access to all the frames that would be optimized away.
-thomas


Alas, R doesn't have tail recursion optimization. That explains last error messages about stack fault. Now, what do they mean, when they write: "R is a functional language, with lazy evaluation"?
Here is the answer:

http://tolstoy.newcastle.edu.au/R/help/03b/0731.html
"...R is a functional language, with lazy evaluation and weak dynamic typing (a variable can change type at will: a <- 1 ; a <- "a" is allowed). Semantically, everything is copy-on-modify although some optimization tricks are used in the implementation to avoid the worst inefficiencies. Parameter passing is according to the "pass-by-value illusion", i.e. what is really getting passed down to a function is a "promise", which embodies the expression used in the call. This is at the core of the lazy evaluation mechanism: The result of the expression is not computed until needed. It also allows a function to get hold of the the expression itself: This is useful for labeling plots but it also allows some variants of "pass-by-name"-like semantics via evaluation in the environment of the caller..."


But... Extra!Extra! I found more sensational materials...
"Besides lazy evaluation in R is not really lazy evaluation!" http://www.postech.ac.kr/~gla/paper/R-ism-dec-8.ppt

CONCLUSIONS
So, my first impression, when i started this blog, was: "Incredibly! Such perfect program for data analysis, visualization, uses functional language." Now i see, R is not perfect.

суббота, 20 октября 2007 г.

День второй (lazy list)

ЗАДАЧА
Реализация ленивых списков и ленивых вычислений над ними.

РЕШЕНИЕ
Первоначально я сконструировал список так:

> nums_from <-function(n)
     c(function() n, 
       function() 
          c(function() n + 1, 
            function() nums_from(n + 2)))
Сразу же выяснил, что писать функции, работающие с ним не очень удобно:
> ints <- nums_from(1) 
> ints[[1]]()
[1] 1
> ints[[2]]()[[1]]()
[1] 2
> ints[[2]]()[[2]]()[[1]]()
[1] 3
Следующий вариант стал более удачным:
> nums_from <-function(n)               
     pairlist(head = function() n,                        
              tail = function() 
                 pairlist(head = function() n + 1,
                          tail = function() nums_from(n + 2)))
> ints <- nums_from(1) # ленивый список натуральных числел
> ints$head()
[1] 1
> ints$tail()$head()
[1] 2
> ints$tail()$tail()$head()
[1] 3
В этот момент, я вспомнил, как где - то мне встречалось утверждение о том, что в R модель вычислений ленивая. Последовала незамедлительная проверка, не переливаю ли я из пустого в порожнее:
> nums_from <-function(n) c(n, c(n+1, nums_from(n + 2))) #1
> ints <- nums_from(1) #2
Ошибка: исполнение расположено слишком глубоко: неопределенная рекурсия / options(expressions=)?
> ?options
....
expressions:    sets a limit on the number of nested expressions that 
will be  evaluated. Valid values are 25...500000 with default 5000. 
If you increase it, you  may also want to start R with a larger protection 
stack; see --max-ppsize in  Memory. Note too that you may cause a segfault 
from overflow of the C stack, and on  OSes where it is possible you may 
want to increase that
.....
Так что #1 - если вычисления аргументов и ленивые, то непонятно, почему #2 - стек сносит. Т.е. стек сносит понятно почему - вычисления всё - таки производятся, при этом хвостовая реурсия, можно предположить, не оптимизируется. Тут определённо какие - то тонкости. С ними разберусь потом.
> take <- function(count, nums) {             
     if(count == 1) nums$head()             
     else  c( nums$head(), take(count - 1, nums$tail()) )
}
> take(6, ints)
[1] 1 2 3 4 5 6
> is.even <- function(num) num %% 2 == 0
> sieve <- function(cond, nums) {      
     if (cond(nums$head())) 
        pairlist(head = nums$head,                    
                 tail = function() sieve(cond, nums$tail()))
     else              
        sieve(cond, nums$tail())
}
> take(3, sieve( is.even, ints ))
[1] 2 4 6
В последнем вызове - sieve( is.even, ints )) уже лениво фильтруются чётные числа. Далее вычисляются 7 первых простых числел:
> nums_down_from <- function(n)       
     pairlist(head = function() n,              
              tail=function() pairlist(head = function() n - 1,   
                                       tail = function()
                                          nums_down_from(n - 2)))
> has.divider <- function(nums, dvding) {     
     if (nums$head() == 1) F     
     else if (dvding %% nums$head() == 0) T         
     else has.divider(nums$tail(), dvding) 
}
> is.prime <- function(num) {     
     if (num == 1) F      
     else {            
        nums <- nums_down_from(floor(sqrt(num)))           
        !has.divider(nums, num)      
     }
 }
> take(7, sieve( is.prime, ints ))
[1] 2 3 5 7 11 13 17
ВЫВОД
Второй предок R - Scheme. Вполне понятно моё желание поиграться с ленивыми списками и вычислениями. Я это сделал. Но результаты не очень - то и радуют. Похоже, хвостовая рекурсия съедает стек, а вместе с ним и все преимущества ленивости. Да, кстати, и memoization не реализована. Сегодняшний день породил больше вопросов чем ответов.

среда, 17 октября 2007 г.

День первый (an arithmetic)

ЗАДАЧА
При некотором натуральном n числа 4n+5 и 9n+4 - точные квадраты. Доказать, что число 5n+4 делится на 29.

РЕШЕНИЕ
В действительности - это задача на вычеты, и не важно даже - точные квадраты первые два числа или нет. И вот почему:

9n + 4 - (4n + 5) = 5n - 1, 5n + 4 = 0 mod 29 
<=> 5n - 1 = 24 mod 29
Используя R проверим:
> ns <- 1:29 # а дальше повторы 
> ns[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
> rst1 <- (4*ns +5) %% 29 
> rst1 # где - то здесь вычет первого квадрата
[1] 9 13 17 21 25 0 4 8 12 16 20 24 28 3 7 11 15 19 23 27 2 6 10 14 18 22 26 1 5
> rst2 <- (9*ns +4) %% 29 
> rst2 # где - то здесь вычет второго квадрата
[1] 13 22 2 11 20 0 9 18 27 7 16 25 5 14 23 3 12 21 1 10 19 28 8 17 26 6 15 24 4
> (rst2 - rst1) %% 29 # а это вычеты 5n - 1, ищем 24
[1] 4 9 14 19 24 0 5 10 15 20 25 1 6 11 16 21 26 2 7 12 17 22 27 3 8 13 18 23 28
> # Ага, есть такой остаток!
ВЫВОД
В незаконченном "The R Language Definition" пишут, что R происходит от APL и Scheme. Значит по линии APL он в родственных отношениях с J, K и прочими Q. Пусть возможности R по работе с многомерными данными и не столь изощрённы, но эта задачка - была моим первым опытом после установки, и "векторизация" очень понравилась. Кажется, R можно использовать для арифметических экспериментов.

БОНУС
Задача производит впечатление чего - то вырожденного, давалась школьникам на маткружке. В самом деле, не счёт же от них требовали. Очевидно, здесь есть решение "с вывертом". Вот оно:
(4*n+5) = a^2(9*n + 4) = b^2
9*a^2 - 4*b^2 = 29
(3*a - 2*b)*(3*a + 2*b) = 29
Из простоты 29 получаем n = 5

воскресенье, 14 октября 2007 г.

Запись вводная

Основные темы этих лабораторных записей:

  • анализ данных
  • программирование на языке R (и не только)
  • R, как программа
За образец взяты записки inferno программиста. Мотивы те же. Формат записей следующий:
Задача - тема лабораторной
Решение - решение, как я его вижу на текущий момент
Вывод - оставлю как отдушину для графоманства )