Funzioni definite dall’utente

In pTabs è possibile definire delle proprie funzioni [1] che si comportano in modo analogo ai comandi predefiniti. Una nuova funzione può essere utilizzata per eseguire una serie di istruzioni di pTabs o per memorizzare valori e dati da utilizzare nello script.

Una funzione corrisponde a quello che in altri software viene definito come macro.

Una funzione può ricevere dei parametri che possono condizionare la sua esecuzione o possono essere elaborati dalla funzione.

Una funzione che restituisce uno o più valori può essere utilizzata come argomento di un’altra istruzione pTabs.

Avvertimento

Attenzione a non utilizzare come nomi delle proprie funzioni e variabili nomi di istruzioni di pTabs. In tal caso la nuova funzione o variabile sovrascriverà quella di pTabs, che non sarà più visibile.

Funzioni e variabili interne al report

Per rendere più fluida la scrittura di uno script ed evitare ripetizioni, può essere utile definire delle variabili o delle proprie funzioni all’interno di un report.

Le funzioni vengono definite con l’istruzione def.

Una variabile viene creata semplicemente assegnando un valore a un identificatore.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 report = ptabs.new do
   ...

   pesi = [0.25, 0.37, 0.75, 1.5, 2.5, 3, 3.5, 4, 5]

   def my_tab(n, root)
     ord "#{root}_#{n}"
       w :legend, pesi
     end
   end

   (1..10).rep {|i| my_tab i, :q45}
   (1..15).rep {|i| my_tab i, :q47}
   (1..18).rep {|i| my_tab i, :q52}

   ...
 end

La variabile pesi e la funzione my_tab saranno visibili solo all’interno del blocco ptabs.new ... end.

Nota

Questo tipo di sintassi è utilizzabile solo se la definizione e l’utilizzo della funzione o della variabile avviene all’interno dello stesso report.

Funzioni e variabili globali

Un modo più proficuo di scrivere le proprie funzioni, comporta la possibilità di riutilizzarle in differenti report dello stesso lavoro o tra diversi lavori.

Per definire tali tipi di funzioni e variabili è necessario utilizzare una sintassi specifica. [2]

Definizione

Le nuove funzioni possono essere definite con l’istruzione defpfun per le funzioni da usare al di fuori del report e deffun per le funzioni da usare all’interno del blocco del report (ptabs.new ... end). Il nome della funzione deve essere un simbolo.

1
2
3
4
5
6
7
 defpfun :my_function_1 do |argomenti|
   # corpo della funzione
 end

 deffun :my_function_2 do |argomenti|
   # corpo della funzione
 end

Le variabili possono essere definite con l’istruzione defpvar per le variabili da usare al di fuori del report e defvar per le funzioni da usare all’interno del blocco del report (ptabs.new ... end). Il nome della variabile deve essere un simbolo.

1
2
3
4
5
 defpvar :my_var_1 => valore

 defvar :my_var_2 => valore
 defvar :my_var_3 => valore, :my_var_4 => valore, :my_var_5 => valore
 defvar my_var_6: valore, my_var_7: valore

nome: valore è una variante sintattica equivalente a :nome => valore.

Un’unica istruzione defpvar o defvar permette di creare più variabili contemporaneamente.

Collocazione

Questie funzioni e variabili possono essere definite ovunque: nello stesso script o in file esterni.

I file esterni devono essere file Ruby con estensione .rb e possono essere caricati nello script con la sintassi:

1
 require 'my_filename' # senza estensione

Se il file risiede nella stessa cartella dello script, verrà trovato senza la necessità di specificare il percorso.

user_functions.rb

La cartella user [3] contiene lo stub file user_function.rb in cui è possibile aggiungere delle proprie funzioni di uso ricorrente. Questo file viene automaticamente caricato in ogni scipt di pTabs.

E’ anche possibile inserire nella cartella user ulteriori file Ruby. Questi file dovranno essere caricati esplicitamente nello script con l’istruzione require, ma non è necessario specificarne il percorso.

Per fare in modo che vengano caricati automaticamente, inserire l’istruzione require direttamente nel file user_functions.rb.

Nel caso si voglia caricare un file presente in un percorso differente, magari di rete, aggiungere il percorso nell’istruzione require, ricordandosi di utilizzare il carattere / e non \:

1
 require 'x:/my/path/my_filename'

defpfun e defpvar

defpfun e defpvar devono essere usati per generare funzioni e variabili che lavorano esternamente alla definizione del report e che hanno bisogno di far riferimento all’oggetto ptabs. Le nuove funzioni e variabili saranno disponibili con la sintassi: ptabs.my_function_name.

Esempio: esecuzione parametrizzata di un report

Un report viene definito e poi eseguito due volte con parametri differenti.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 defpfun :myrep do |rep_name, caption, data_file|

   report = ptabs.new(filename) do
     title  caption
     spss.open data_file
     ...
   end
   report.render :html

 end

 ptabs.myrep "rep001", "Report XXX", "dat001"
 ptabs.myrep "rep002", "Report YYY", "dat002"

Esempio: generazione output

La funzione che esegue i metodi render desiderati del report, permettendo di scegliere se produrre tutti gli output (:test) o solo l’html (:all) .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 defpfun :print do |report, mode=:test|
   report.render :html
   if mode == :all
     report.render :pdf
     report.render :xls, :single_panel => true,
                   :sheets => { "VA %" => [:count, :rowpct, :colpct, :mresp, :base, :mean, :sd, :se, :mtest],
                                "%"    => [        :rowpct, :colpct, :mresp, :base, :mean                  ],
                                "VA"   => [:count,                   :mresp, :base, :mean                  ] }
     report.render :db
   end
 end

 report = ptabs.new do
   ...
 end
 ptabs.print report

deffun e defvar

deffun e defvar devono essere usati per generare funzioni e variabili che devono essere eseguite all’inteno di un report (all’interno del blocco ptabs.new ... end) per esempio funzioni che definiscono tavole. Le nuove funzioni e variabili saranno direttamente disponibili all’interno di un report con la sintassi: my_function_name.

Esempio: tabella personalizzata

Si definisce una nuova istruzione che genera un tipo specifico di tavola da usarsi per variabili con scala a 10 punti più non risponde.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 deffun :ord_net do |varname, caption|

   ord varname, caption do
     net "NEGATIVO" => 1..5, "POSITIVO" => 6..10, "NON RISPONDE" => 11
     w :legend, "( # ) *", 1,2,3,4,5,6,7,8,9,10,:na
     hide 14
   end

 end

 report = ptabs.new do
   ...
   ord_net :v25, "Tabella D25 - ..."
   ...
 end

Esempio: pesi di uso ricorrente

Una variabile può servire semplicemente per memorizzare e poi restituire dei valori (dati o strutture di dati) da utilizzare nello script.

Nell’esempio, vengono definiti dei pesi da utilizzare per una specifica tipologia di domanda.

1
2
3
4
5
6
 defvar :my_weights => [0.25, 0.37, 0.75, 1.5, 2.5, 3, 3.5, 4, 5]

 # -- nel report
   ord ... do
     w :legend, my_weights
   end

Il valore memorizzato nella variabile non è un semplice numero, ma è un vettore contenete più elementi. La variabile viene usata, nell’esempio, come argomento del sottocomando w.

Esempio: template del titolo della tabella

Si definisce un template personalizzato da usare nei titoli delle tabelle.

1
2
3
4
5
6
 deffun :my_title(n, title, subtitle=nil)
   "Dom.#{n} - #{title}" + (subtitle.empty? ? "" : "\n -*- #{subtitle} -*-")
 end

 # -- nel report
   cat :d1, my_title(1, "Titolo", "Sottotitolo")

Questa funzione non esegue nessuna istruzione pTabs, ma restituisce [4] semplicemente una stringa di testo costruita a partire dai parametri di input.

La funzione viene usata, nell’esempio, come argomento dell’istruzione cat al posto del titolo.

Esempio: libreria di ponderazioni

L’esempio seguente illustra un semplice metodo per creare una libreria contenente i marginali e le numerosità degli universi da utilizzare nelle ponderazioni.

 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
 # -- file: users/universi.rb
 # INDIVIDUI >= 18 ANNI
 defvar :uni_18_n =>  47_431_000,
        :uni_18 => {
           :sesso  => [47.8, 52.2],
           :ampc   => [18.6, 29.2, 17.4,  1.0, 23.8],
           :istr   => [28.0, 36.3, 28.4,  7.3],
           :area   => [10.7, 16.1, 11.8,  7.3, 13.4,  9.0, 18.9, 12.7],
        }

 # FAMIGLIE
 defvar :uni_fam_n => 21_810_000,
        :uni_fam => {
           :area   => [11.8, 16.8, 11.8,  7.6, 13.0,  9.1, 17.5, 12.4],
           :ncomp  => [24.9, 27.1, 21.6, 18.9,  7.6],
           :etacap => [11.9, 18.9, 18.5, 17.9, 32.8],
        }

 # -- nello stesso script o meglio in 'user_functions.rb'
 require 'universi'

 # -- nello script
 rake :names => [:ipf, :esp],
      :counts => [uni_18_n],
      :population => uni_18

47_431_000 viene letto come un semplice numero intero. Nei numeri, il carattere sottolineato _ può essere utilizzato come separatore delle migliaia per facilitare la lettura.

Callbacks

I callbacks sono funzioni che vengono richiamate durante la generazione dei report. Per comportamento predefinito non svolgono nessuna azione, ma l’utente può ridefinirle per inserire dei comportamenti specifici.

Funzioni dell’oggetto report

Queste funzioni vanno ridefinite con deffun.

render_title

render_title permette di modificare il titolo della tabella.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Segnatura
render_title(title, index)

# Esempio di ridefinizione:
# - aggiunge "TAB.# - " e mette in maiuscolo il titolo della tavola
deffun :render_title do |title, index|
  "TAB.#{index} - "+title.upcase
end

# Esempio di ridefinizione:
# - elimina "V###_#: Question ###_#: " dal titolo della tavola
deffun :render_title do |title, index|
  title.gsub(/V[\d_]+:\sQuestion\s\d+:\s?/,'').strip
end

skip_variable

skip_variable permette di valutare se escludere o meno una variabile/set di variabili dal topline o dalla procedura autotab. Viene eseguito prima del calcolo delle statistiche.

variable è un oggetto PTables::VariableInfo (vedere info).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Segnatura
skip_variable(variable)

# Esempio di ridefinizione:
# - la variabile compare nel topline solo se il metodo restituisce false
deffun :skip_variable do |variable|
  return true if variable.alpha?
  return true if %w(intnr inttime scrcnt internr stime).include?(variable.name)
  return true if variable.label =~ /^INTERVISTATORE:/i
  return true if variable.levels.include?("CATI")
  false
end

skip_summary

skip_summary permette di valutare se escludere o meno una variabile/set di variabili dall’output del topline. Viene eseguito dopo aver calcolato le statistiche.

summary è un oggetto PTables::TopLine::Summary.

Metodi dell’oggetto Summary:

  • varname: restituisce il nome della variabile o la radice del nome del set di variabili
  • varnames: restituisce un Array con i nomi delle variabili
  • caption: restituisce il titolo
  • labels: restituisce un Array con le etichette
  • id: restituisce il progressivo della tabella
  • numeric?: restituisce true se la variabile è numerica
  • single?: restituisce true se la variabile è singola
  • multi?: restituisce true se la variabile è multipla
  • base: restituisce la base
1
2
3
4
5
6
7
8
9
# Segnatura
skip_summary(summary)

# Esempio di ridefinizione:
# - la variabile compare nel topline solo se il metodo restituisce false
deffun :skip_summary do |summary|
  return true if summary.base < 10
  false
end

auto_tab

auto_tab viene chiamato dalla procedura autotab per produrre automaticamente tutte le tabelle per un elenco di variabili o, se non specificato, per tutte le variabili del file di dati.

Di auto_tab viene già fornita un’implementazione completa, ma è possibile personalizzare la procedura ridefinendo la funzione.

 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
# Segnatura
auto_tab(variables_list)

# Implementazione di default
deffun :auto_tab do |variables|
  i = 0
  variables.each do |var|                                         # cicla sulle variabili (e gli eventuali set)
    inf = info var                                                #   recupera le informazioni sulla variabile
    next if skip_variable(inf)                                    #   salta alla variabile successiva se non è da tabulare
    next if inf.alpha?                                            #   salta alla variabile successiva se è una variabile alfanumerica
    if inf.set?                                                   #   se è un set di variabili
      if inf.multi?                                               #     se è un set multiplo
        md var.vars, "Tabella #{i+=1} - [$vn()] #{var.title}"     #       tabella md
      else                                                        #     altri set
        scale var.vars, "Tabella #{i+=1} - [$vn()] #{var.title}"  #       tabella scale riassuntiva
        var.vars.each do |v|                                      #       cicla sulle variabili del set
          ord v, "Tabella #{i+=1} - [$vn()] $vl()"                #         tabella ord di dettaglio
        end
      end
    elsif inf.factor?                                             #   se variabile factor
      cat var, "Tabella #{i+=1} - [$vn()] $vl()"                  #     tabella cat
    elsif inf.numeric?                                            #   se variabile numerica
      scale var, "Tabella #{i+=1} - [$vn()] $vl()"                #     tabella scale
    end
  end
end

Simboli per test significatività

Tre funzioni definiscono l’elenco di simboli utilizzati per identificare le colonne nei test di significatività.

Le funzioni devono restituire un Hash con le chiavi :header e markers:

  • :header: simboli da utilizzare per le intestazioni delle colonne
  • :markers: simboli da utilizzare nelle celle

markers_1_level

Simboli per pairwise comparisons con 1 solo livello di significatività.

1
2
3
4
5
6
7
8
# (implementazione di default)
deffun :markers_1_level do
  markers = ('a'..'z').to_a + %w(* $ & @ # ^ = + - !) + ('A'..'Z').to_a
  {
    :header => markers,
    :markers => markers
  }
end

markers_2_level

Simboli per pairwise comparisons con 2 livelli di significatività. markers contiene un solo vettore di simboli. Per il secondo livello di significatività i simboli vengono convertiti in maiuscolo.

1
2
3
4
5
6
7
8
# (implementazione di default)
deffun :markers_2_level do
  markers = (['']+(1..9).to_a).map{|i| ('a'..'z').to_a.map{|x| "#{x}#{i}"}}.flatten
  {
    :header => markers,
    :markers => markers
  }
end

markers_9_level

Simboli per pairwise comparisons fino a 9 livelli di significatività. markers è un vettore che contiene nove vettori di simboli, uno per ogni livello di significatività.

1
2
3
4
5
6
7
8
# (implementazione di default)
deffun :markers_9_level do
  markers = ('a'..'z').to_a
  {
    :header => markers,
    :markers => ((1..9).to_a).map{|i| markers.map{|x| "#{x}#{i}"}}
  }
end

Funzioni dell’oggetto ptabs

Queste funzioni vanno ridefinite con defpfun.

before_report

before_report viene eseguito prima della definizione del report.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Segnatura
before_report(ptabs)

# Esempio di ridefinizione:
# - scarica il file di dati da un sito ftp
defpfun :before_report do |ptabs|
  require 'net/ftp'
  ftp - Net::FTP.new 'ftp.name.com'
  ftp.login "user", "password"
  ftp.chdir "/path/ftp"
  ftp.getbinaryfile "source.dat", "target.dat"
  ftp.close
end

after_report

after_report viene eseguito al termine della definizione del report.

1
2
# Segnatura
after_report(report)

before_exit

before_exit viene eseguito prima del termine di pTabs.

1
2
3
4
5
6
7
8
# Segnatura
before_exit(ptabs)

# Esempio di ridefinizione:
# - copia il report prodotto
defpfun :before_exit do |ptabs|
  system "copy report.pdf x:\\Public"
end

Note

[1]Tecnicamente in Ruby, come in genere nei linguaggi a oggetti, si tratta di metodi. Qui useremo il termine di funzioni in senso lato.
[2]Tecnicamente defpfun, defpvar, deffun e defvar generano dei nuovi metodi all’interno dei moduli User::Ptabs e User::Report. defpvar è un alias si defpfun e defvar è un alias si deffun
[3]La cartella user si trova nel percorso di installazione di pTabs. Una nuova installazione non ne sovrascrive il contenuto. Nel caso sia stata definita la variabile d’ambiente PTABS_USER la cartella user non è più attiva e bisogna utilizzare al suo posto quella definita in PTABS_USER. In tal caso è necessario copiare il file user_functions.rb.
[4]Qualsiasi funzione in Ruby restituisce un valore: la variabile o il valore presente nell’ultima riga del corpo della funzione o comunque l’ultima espressione valutata.