From ca4d57dd4f9972391e0eb4a035b7d890ab47009a Mon Sep 17 00:00:00 2001 From: onli Date: Sat, 7 Feb 2015 20:55:45 +0100 Subject: [PATCH 001/119] Add an example for chi-square homogeneity test --- examples/chisquare_test.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 examples/chisquare_test.rb diff --git a/examples/chisquare_test.rb b/examples/chisquare_test.rb new file mode 100644 index 0000000..650753c --- /dev/null +++ b/examples/chisquare_test.rb @@ -0,0 +1,23 @@ +#!/usr/bin/ruby +$:.unshift(File.dirname(__FILE__)+'/../lib') +require 'statsample' + +Statsample::Analysis.store(Statsample::Test::ChiSquare) do + # Collect the two vectors with the categorical data (raw number of occurences) into one matrix. Here + #-------------------------------------------- + #| category | observation 1 | observation 2 | + #|------------------------------------------| + #| A | 100 | 20 | + #| B | 50 | 70 | + #| C | 30 | 100 | + #|------------------------------------------| + # + m=Matrix[[100, 50, 30],[20, 70, 100]] + x_2=Statsample::Test.chi_square(m) + # after the test is done, look at the p-value. + puts x_2.probability +end + +if __FILE__==$0 + Statsample::Analysis.run_batch +end From 9050410d18da6d8b715f018a9e86dc028d1d8507 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 17 Mar 2015 23:57:25 -0300 Subject: [PATCH 002/119] Remove Gemfile.lock from the repository --- .gitignore | 1 + Gemfile.lock | 81 ---------------------------------------------------- 2 files changed, 1 insertion(+), 81 deletions(-) delete mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index ce64169..a75b06d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +Gemfile.lock doc.yaml *.swp *.rbc diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index ef5d88d..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,81 +0,0 @@ -GEM - remote: https://www.rubygems.org/ - specs: - activesupport (4.1.6) - i18n (~> 0.6, >= 0.6.9) - json (~> 1.7, >= 1.7.7) - minitest (~> 5.1) - thread_safe (~> 0.1) - tzinfo (~> 1.1) - awesome_print (1.2.0) - clbustos-rtf (0.4.2) - dirty-memoize (0.0.4) - distribution (0.7.1) - extendmatrix (0.3.1) - hoe (3.13.0) - rake (>= 0.8, < 11.0) - i18n (0.6.11) - json (1.8.1) - metaclass (0.0.4) - minimization (0.2.1) - rb-gsl (~> 1.2) - text-table (~> 1.2) - minitest (5.4.2) - mocha (0.14.0) - metaclass (~> 0.0.1) - narray (0.6.0.9) - prawn (0.8.4) - prawn-core (>= 0.8.4, < 0.9) - prawn-layout (>= 0.8.4, < 0.9) - prawn-security (>= 0.8.4, < 0.9) - prawn-core (0.8.4) - prawn-layout (0.8.4) - prawn-security (0.8.4) - prawn-svg (0.9.1.11) - prawn (>= 0.8.4) - rake (10.3.2) - rb-gsl (1.16.0.2) - narray (>= 0.5.9) - rdoc (4.1.2) - json (~> 1.4) - reportbuilder (1.4.2) - clbustos-rtf (~> 0.4.0) - prawn (~> 0.8.4) - prawn-svg (~> 0.9.1) - text-table (~> 1.2) - rserve-client (0.3.1) - ruby-ole (1.2.11.7) - rubyvis (0.6.1) - shoulda (3.5.0) - shoulda-context (~> 1.0, >= 1.0.1) - shoulda-matchers (>= 1.4.1, < 3.0) - shoulda-context (1.2.1) - shoulda-matchers (2.2.0) - activesupport (>= 3.0.0) - spreadsheet (1.0.0) - ruby-ole (>= 1.0) - text-table (1.2.3) - thread_safe (0.3.4) - tzinfo (1.2.2) - thread_safe (~> 0.1) - -PLATFORMS - ruby - -DEPENDENCIES - awesome_print - dirty-memoize - distribution - extendmatrix - hoe - minimization - minitest - mocha (= 0.14.0) - rb-gsl - rdoc - reportbuilder - rserve-client - rubyvis - shoulda (= 3.5.0) - shoulda-matchers (= 2.2.0) - spreadsheet From 3eaa53e072c71856932533ea584ecec887aca503 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Tue, 17 Mar 2015 21:53:44 -0700 Subject: [PATCH 003/119] Remove bad check in cronbach_alpha calculation @clbustos agreed this change should be made in https://github.com/clbustos/statsample/pull/11, but never merged it --- lib/statsample/reliability.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/statsample/reliability.rb b/lib/statsample/reliability.rb index e5fb50c..dde4c76 100644 --- a/lib/statsample/reliability.rb +++ b/lib/statsample/reliability.rb @@ -5,7 +5,6 @@ class << self # only uses tuples without missing data def cronbach_alpha(ods) ds=ods.dup_only_valid - return nil if ds.vectors.any? {|k,v| v.variance==0} n_items=ds.fields.size return nil if n_items<=1 s2_items=ds.vectors.inject(0) {|ac,v| @@ -150,4 +149,4 @@ def curve_field(field, item) require 'statsample/reliability/icc.rb' require 'statsample/reliability/scaleanalysis.rb' require 'statsample/reliability/skillscaleanalysis.rb' -require 'statsample/reliability/multiscaleanalysis.rb' \ No newline at end of file +require 'statsample/reliability/multiscaleanalysis.rb' From 62f8053ad0b8eda669ce72f15943d2216e0126ed Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Wed, 18 Mar 2015 22:27:53 -0300 Subject: [PATCH 004/119] Add travis & code climate badges to README --- README.md | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8c8151d..d6d3232 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Statsample -Homepage :: https://github.com/clbustos/statsample +[![Build Status](https://travis-ci.org/SciRuby/statsample.svg?branch=master)](https://travis-ci.org/SciRuby/statsample) +[![Code Climate](https://codeclimate.com/github/SciRuby/statsample/badges/gpa.svg)](https://codeclimate.com/github/SciRuby/statsample) + +Homepage :: https://github.com/sciruby/statsample [![Build Status](https://travis-ci.org/clbustos/statsample.svg?branch=master)](https://travis-ci.org/clbustos/statsample) [![Gem Version](https://badge.fury.io/rb/statsample.svg)](http://badge.fury.io/rb/statsample) @@ -27,7 +30,7 @@ Include: ## Principles -* Software Design: +* Software Design: * One module/class for each type of analysis * Options can be set as hash on initialize() or as setters methods * Clean API for interactive sessions @@ -37,13 +40,13 @@ Include: * Statistical Design * Results are tested against text results, SPSS and R outputs. * Go beyond Null Hiphotesis Testing, using confidence intervals and effect sizes when possible - * (When possible) All references for methods are documented, providing sensible information on documentation + * (When possible) All references for methods are documented, providing sensible information on documentation ## Features * Classes for manipulation and storage of data: * Statsample::Vector: An extension of an array, with statistical methods like sum, mean and standard deviation - * Statsample::Dataset: a group of Statsample::Vector, analog to a excel spreadsheet or a dataframe on R. The base of almost all operations on statsample. + * Statsample::Dataset: a group of Statsample::Vector, analog to a excel spreadsheet or a dataframe on R. The base of almost all operations on statsample. * Statsample::Multiset: multiple datasets with same fields and type of vectors * Anova module provides generic Statsample::Anova::OneWay and vector based Statsample::Anova::OneWayWithVectors. Also you can create contrast using Statsample::Anova::Contrast * Module Statsample::Bivariate provides covariance and pearson, spearman, point biserial, tau a, tau b, gamma, tetrachoric (see Bivariate::Tetrachoric) and polychoric (see Bivariate::Polychoric) correlations. Include methods to create correlation and covariance matrices @@ -53,10 +56,10 @@ Include: * Logit Regression: Statsample::Regression::Binomial::Logit * Probit Regression: Statsample::Regression::Binomial::Probit * Factorial Analysis algorithms on Statsample::Factor module. - * Classes for Extraction of factors: + * Classes for Extraction of factors: * Statsample::Factor::PCA * Statsample::Factor::PrincipalAxis - * Classes for Rotation of factors: + * Classes for Rotation of factors: * Statsample::Factor::Varimax * Statsample::Factor::Equimax * Statsample::Factor::Quartimax @@ -65,7 +68,7 @@ Include: * Statsample::Factor::MAP performs Velicer's Minimum Average Partial (MAP) test, which retain components as long as the variance in the correlation matrix represents systematic variance. * Dominance Analysis. Based on Budescu and Azen papers, dominance analysis is a method to analyze the relative importance of one predictor relative to another on multiple regression * Statsample::DominanceAnalysis class can report dominance analysis for a sample, using uni or multivariate dependent variables - * Statsample::DominanceAnalysis::Bootstrap can execute bootstrap analysis to determine dominance stability, as recomended by Azen & Budescu (2003) link[http://psycnet.apa.org/journals/met/8/2/129/]. + * Statsample::DominanceAnalysis::Bootstrap can execute bootstrap analysis to determine dominance stability, as recomended by Azen & Budescu (2003) link[http://psycnet.apa.org/journals/met/8/2/129/]. * Module Statsample::Codification, to help to codify open questions * Converters to import and export data: * Statsample::Database : Can create sql to create tables, read and insert data @@ -74,7 +77,7 @@ Include: * Statsample::Mx : Write Mx Files * Statsample::GGobi : Write Ggobi files * Module Statsample::Crosstab provides function to create crosstab for categorical data -* Module Statsample::Reliability provides functions to analyze scales with psychometric methods. +* Module Statsample::Reliability provides functions to analyze scales with psychometric methods. * Class Statsample::Reliability::ScaleAnalysis provides statistics like mean, standard deviation for a scale, Cronbach's alpha and standarized Cronbach's alpha, and for each item: mean, correlation with total scale, mean if deleted, Cronbach's alpha is deleted. * Class Statsample::Reliability::MultiScaleAnalysis provides a DSL to easily analyze reliability of multiple scales and retrieve correlation matrix and factor analysis of them. * Class Statsample::Reliability::ICC provides intra-class correlation, using Shrout & Fleiss(1979) and McGraw & Wong (1996) formulations. @@ -92,9 +95,9 @@ Include: * Statsample::Graph::Boxplot * Statsample::Graph::Histogram * Statsample::Graph::Scatterplot -* Gem bio-statsample-timeseries provides module Statsample::TimeSeries with support for time series, including ARIMA estimation using Kalman-Filter. +* Gem bio-statsample-timeseries provides module Statsample::TimeSeries with support for time series, including ARIMA estimation using Kalman-Filter. * Gem statsample-sem provides a DSL to R libraries +sem+ and +OpenMx+ -* Gem statsample-glm provides you with GML method, to work with Logistic, Poisson and Gaussian regression ,using ML or IRWLS. +* Gem statsample-glm provides you with GML method, to work with Logistic, Poisson and Gaussian regression ,using ML or IRWLS. * Close integration with gem reportbuilder, to easily create reports on text, html and rtf formats. # Examples of use: @@ -106,7 +109,7 @@ See the [examples folder](https://github.com/clbustos/statsample/tree/master/exa ```ruby require 'statsample' -ss_analysis(Statsample::Graph::Boxplot) do +ss_analysis(Statsample::Graph::Boxplot) do n=30 a=rnorm(n-1,50,10) b=rnorm(n, 30,5) @@ -127,11 +130,11 @@ require 'statsample' ss_analysis("Statsample::Bivariate.correlation_matrix") do samples=1000 ds=data_frame( - 'a'=>rnorm(samples), + 'a'=>rnorm(samples), 'b'=>rnorm(samples), 'c'=>rnorm(samples), 'd'=>rnorm(samples)) - cm=cor(ds) + cm=cor(ds) summary(cm) end @@ -140,10 +143,10 @@ Statsample::Analysis.run_batch # Echo output to console # Requirements -Optional: +Optional: * Plotting: gnuplot and rbgnuplot, SVG::Graph -* Factorial analysis and polychorical correlation(joint estimate and polychoric series): gsl library and rb-gsl (https://rubygems.org/gems/rb-gsl/). You should install it using gem install rb-gsl. +* Factorial analysis and polychorical correlation(joint estimate and polychoric series): gsl library and rb-gsl (https://rubygems.org/gems/rb-gsl/). You should install it using gem install rb-gsl. *Note*: Use gsl 1.12.109 or later. @@ -160,7 +163,7 @@ Optional: $ sudo gem install statsample ``` -On *nix, you should install statsample-optimization to retrieve gems gsl, statistics2 and a C extension to speed some methods. +On *nix, you should install statsample-optimization to retrieve gems gsl, statistics2 and a C extension to speed some methods. There are available precompiled version for Ruby 1.9 on x86, x86_64 and mingw32 archs. From c2494cfa4ed415613e01e67eb15964cfe1dd3b3b Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Wed, 18 Mar 2015 23:13:15 -0300 Subject: [PATCH 005/119] Removing old badges from README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d6d3232..f91a6d8 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,10 @@ [![Build Status](https://travis-ci.org/SciRuby/statsample.svg?branch=master)](https://travis-ci.org/SciRuby/statsample) [![Code Climate](https://codeclimate.com/github/SciRuby/statsample/badges/gpa.svg)](https://codeclimate.com/github/SciRuby/statsample) +[![Gem Version](https://badge.fury.io/rb/statsample.svg)](http://badge.fury.io/rb/statsample) Homepage :: https://github.com/sciruby/statsample -[![Build Status](https://travis-ci.org/clbustos/statsample.svg?branch=master)](https://travis-ci.org/clbustos/statsample) -[![Gem Version](https://badge.fury.io/rb/statsample.svg)](http://badge.fury.io/rb/statsample) ## DESCRIPTION A suite for basic and advanced statistics on Ruby. Tested on Ruby 2.1.1p76 (June 2014), 1.8.7, 1.9.1, 1.9.2 (April, 2010), ruby-head(June, 2011) and JRuby 1.4 (Ruby 1.8.7 compatible). From a47eb3f0cfabf104a8f70a1c2fa4b18cbd3d500a Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Wed, 18 Mar 2015 11:41:30 -0300 Subject: [PATCH 006/119] Improve readability of lib/statsample.rb I mostly worked through whitespace issues and replaced some logic with `Enumerable` methods. And Rubocop's --auto-correct flag is wonderful. --- lib/spss.rb | 31 ++++--- lib/statsample.rb | 182 +++++++++++++++++++------------------- lib/statsample/dataset.rb | 163 +++++++++++++++++----------------- lib/statsample/matrix.rb | 86 +++++++++--------- lib/statsample/vector.rb | 18 ++-- 5 files changed, 243 insertions(+), 237 deletions(-) diff --git a/lib/spss.rb b/lib/spss.rb index 3c60dd3..50a2dca 100644 --- a/lib/spss.rb +++ b/lib/spss.rb @@ -1,4 +1,4 @@ -# = spss.rb - +# = spss.rb - # # Provides utilites for working with spss files # @@ -12,40 +12,43 @@ class Element def add(a) @elements.push(a) end - def parse_elements(func=:to_s) - @elements.collect{|e| " "+e.send(func)}.join("\n") + + def parse_elements(func = :to_s) + @elements.collect{ |e| " "+e.send(func) }.join("\n") end + def init_with config - config.each {|key,value| - self.send(key.to_s+"=",value) if methods.include? key.to_s - } + config.each do |key, value| + self.send(key.to_s + "=", value) if methods.include? key.to_s + end end - def initialize(config={}) - @config=config - @elements=[] + + def initialize(config = {}) + @config = config + @elements = [] end end class Dictionary < Element attr_accessor :locale, :date_time, :row_count - def initialize(config={}) + def initialize(config = {}) super init_with ({ - :locale=>"en_US", + :locale=>"en_US", :date_time=>Time.new().strftime("%Y-%m-%dT%H:%M:%S"), :row_count=>1 }) init_with config end - + def to_xml "\n"+parse_elements(:to_xml)+"\n" - + end def to_spss parse_elements(:to_spss) end end - + class MissingValue < Element attr_accessor :data, :type, :from, :to def initialize(data,type=nil) diff --git a/lib/statsample.rb b/lib/statsample.rb index 30b4608..6ebdd7f 100644 --- a/lib/statsample.rb +++ b/lib/statsample.rb @@ -1,4 +1,4 @@ -# = statsample.rb - +# = statsample.rb - # Statsample - Statistic package for Ruby # Copyright (C) 2008-2014 Claudio Bustos # @@ -17,17 +17,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # - -#$:.unshift(File.dirname(__FILE__)) require 'matrix' require 'extendmatrix' require 'distribution' require 'dirty-memoize' require 'reportbuilder' - class Numeric - def square ; self * self ; end + def square + self * self + end end class String @@ -41,10 +40,10 @@ def is_number? end class Module - def include_aliasing(m, suffix="ruby") + def include_aliasing(m, suffix = 'ruby') m.instance_methods.each do |f| if instance_methods.include? f - alias_method("#{f}_#{suffix}",f) + alias_method("#{f}_#{suffix}", f) remove_method f end end @@ -60,15 +59,26 @@ class Array # a.recode_repeated # => ["a","b","c_1","c_2","d_1","d_2","d_3","e"] def recode_repeated - if self.size!=self.uniq.size + if size != uniq.size # Find repeated - repeated=self.inject({}) {|a,v| - (a[v].nil? ? a[v]=1 : a[v]+=1); a }.find_all{|k,v| v>1}.collect{|k,v| k} - ns=repeated.inject({}) {|a,v| a[v]=0;a} - self.collect do |f| + repeated = inject({}) do |acc, v| + if acc[v].nil? + acc[v] = 1 + else + acc[v] += 1 + end + acc + end.select { |_k, v| v > 1 }.keys + + ns = repeated.inject({}) do |acc, v| + acc[v] = 0 + acc + end + + collect do |f| if repeated.include? f - ns[f]+=1 - sprintf("%s_%d",f,ns[f]) + ns[f] += 1 + sprintf('%s_%d', f, ns[f]) else f end @@ -79,61 +89,62 @@ def recode_repeated end end -def create_test(*args,&proc) - description=args.shift - fields=args +def create_test(*args, &_proc) + description = args.shift + fields = args [description, fields, Proc.new] end + #-- # Test extensions begin require 'gettext' rescue LoadError def bindtextdomain(d) #:nodoc: - d + d end - + # Bored module module GetText #:nodoc: - def _(t) - t + def _(t) + t end end end + # Library for statistical analysis on Ruby # # * Classes for manipulation and storage of data: # * Module Statsample::Bivariate provides covariance and pearson, spearman, point biserial, tau a, tau b, gamma, tetrachoric (see Bivariate::Tetrachoric) and polychoric (see Bivariate::Polychoric) correlations. Include methods to create correlation and covariance matrices # * Multiple types of regression on Statsample::Regression # * Factorial Analysis algorithms on Statsample::Factor module. -# * Dominance Analysis. Based on Budescu and Azen papers.link[http://psycnet.apa.org/journals/met/8/2/129/]. +# * Dominance Analysis. Based on Budescu and Azen papers.link[http://psycnet.apa.org/journals/met/8/2/129/]. # * Module Statsample::Codification, to help to codify open questions # * Converters to import and export data from databases, csv and excel files. # * Module Statsample::Crosstab provides function to create crosstab for categorical data # * Reliability analysis provides functions to analyze scales. # * Module Statsample::SRS (Simple Random Sampling) provides a lot of functions to estimate standard error for several type of samples -# * Interfaces to gdchart, gnuplot and SVG::Graph +# * Interfaces to gdchart, gnuplot and SVG::Graph # module Statsample - def self.create_has_library(library) define_singleton_method("has_#{library}?") do - cv="@@#{library}" - if !class_variable_defined? cv - begin + cv = "@@#{library}" + unless class_variable_defined? cv + begin require library.to_s - class_variable_set(cv,true) + class_variable_set(cv, true) rescue LoadError - class_variable_set(cv,false) + class_variable_set(cv, false) end end class_variable_get(cv) end end - + create_has_library :gsl - - SPLIT_TOKEN = "," + + SPLIT_TOKEN = ',' autoload(:Analysis, 'statsample/analysis') autoload(:Database, 'statsample/converters') autoload(:Anova, 'statsample/anova') @@ -154,133 +165,122 @@ def self.create_has_library(library) autoload(:Multivariate, 'statsample/multivariate') autoload(:Multiset, 'statsample/multiset') autoload(:StratifiedSample, 'statsample/multiset') - autoload(:MLE, 'statsample/mle') + autoload(:MLE, 'statsample/mle') autoload(:Regression, 'statsample/regression') autoload(:Test, 'statsample/test') autoload(:Factor, 'statsample/factor') autoload(:Graph, 'statsample/graph') - - + class << self # Load a object saved on a file. def load(filename) if File.exist? filename - o=false - File.open(filename,"r") {|fp| o=Marshal.load(fp) } + o = false + File.open(filename, 'r') { |fp| o = Marshal.load(fp) } o else false end end - - - + # Create a matrix using vectors as columns. # Use: # # matrix=Statsample.vector_cols_matrix(v1,v2) def vector_cols_matrix(*vs) # test - size=vs[0].size - vs.each{|v| - raise ArgumentError,"Arguments should be Vector" unless v.instance_of? Statsample::Vector - raise ArgumentError,"Vectors size should be the same" if v.size!=size - } - Matrix.rows((0...size).to_a.collect() {|i| - vs.collect{|v| v[i]} - }) + size = vs[0].size + + vs.each do |v| + fail ArgumentError, 'Arguments should be Vector' unless v.instance_of? Statsample::Vector + fail ArgumentError, 'Vectors size should be the same' if v.size != size + end + + Matrix.rows((0...size).to_a.collect { |i| vs.collect { |v| v[i] } }) end + # Returns a duplicate of the input vectors, without missing data # for any of the vectors. - # + # # a=[1,2,3,6,7,nil,3,5].to_scale # b=[nil,nil,5,6,4,5,10,2].to_scale # c=[2,4,6,7,4,5,6,7].to_scale # a2,b2,c2=Statsample.only_valid(a,b,c) - # => [#, - # #, + # => [#, + # #, # #] # def only_valid(*vs) - i=1 - h=vs.inject({}) {|a,v| a["v#{i}"]=v;i+=1;a} - ds=Statsample::Dataset.new(h).dup_only_valid + i = 1 + h = vs.inject({}) { |acc, v| acc["v#{i}"] = v; i += 1; acc } + ds = Statsample::Dataset.new(h).dup_only_valid ds.vectors.values end - - # Cheap version of #only_valid. + + # Cheap version of #only_valid. # If any vectors have missing_values, return only valid. # If not, return the vectors itself def only_valid_clone(*vs) - if vs.any? {|v| v.flawed?} + if vs.any?(&:flawed?) only_valid(*vs) else vs end end - end - - - - + end + module Util # Reference: http://www.itl.nist.gov/div898/handbook/eda/section3/normprpl.htm - def normal_order_statistic_medians(i,n) - if i==1 - u= 1.0 - normal_order_statistic_medians(n,n) - elsif i==n - u=0.5**(1 / n.to_f) + def normal_order_statistic_medians(i, n) + if i == 1 + u = 1.0 - normal_order_statistic_medians(n, n) + elsif i == n + u = 0.5**(1 / n.to_f) else - u= (i - 0.3175) / (n + 0.365) + u = (i - 0.3175) / (n + 0.365) end u end - - def self.nice(s,e) # :nodoc: - reverse = etrue).add(self).send(method) + bindtextdomain('statsample') + def summary(method = :to_text) + ReportBuilder.new(no_title: true).add(self).send(method) end end module STATSAMPLE__ #:nodoc: end end - - #-- -begin +begin require 'statsamplert' rescue LoadError module Statsample - OPTIMIZED=false + OPTIMIZED = false end end diff --git a/lib/statsample/dataset.rb b/lib/statsample/dataset.rb index fbeea85..787320c 100644 --- a/lib/statsample/dataset.rb +++ b/lib/statsample/dataset.rb @@ -1,7 +1,7 @@ require 'statsample/vector' class Hash - # Creates a Statsample::Dataset based on a Hash + # Creates a Statsample::Dataset based on a Hash def to_dataset(*args) Statsample::Dataset.new(self, *args) end @@ -29,15 +29,15 @@ def to_s m end end - # Set of cases with values for one or more variables, + # Set of cases with values for one or more variables, # analog to a dataframe on R or a standard data file of SPSS. # Every vector has #field name, which represent it. By default, - # the vectors are ordered by it field name, but you can change it + # the vectors are ordered by it field name, but you can change it # the fields order manually. # The Dataset work as a Hash, with keys are field names - # and values are Statsample::Vector - # - # + # and values are Statsample::Vector + # + # # ==Usage # Create a empty dataset: # Dataset.new() @@ -46,7 +46,7 @@ def to_s # Create a dataset with two vectors, called v1 # and v2: # Dataset.new({'v1'=>%w{1 2 3}.to_vector, 'v2'=>%w{4 5 6}.to_vector}) - # Create a dataset with two given vectors (v1 and v2), + # Create a dataset with two given vectors (v1 and v2), # with vectors on inverted order: # Dataset.new({'v2'=>v2,'v1'=>v1},['v2','v1']) # @@ -54,8 +54,8 @@ def to_s # field order as arguments # v1 = [1,2,3].to_scale # v2 = [1,2,3].to_scale - # ds = {'v1'=>v2, 'v2'=>v2}.to_dataset(%w{v2 v1}) - + # ds = {'v1'=>v2, 'v2'=>v2}.to_dataset(%w{v2 v1}) + class Dataset include Writable include Summarizable @@ -99,7 +99,7 @@ def self.crosstab_by_asignation(rows,columns,values) ;a} values.each_index{|i| h_rows[rows[i]][columns[i]]=values[i] - } + } ds=Dataset.new(["_id"]+cols_values) cols_values.each{|c| ds[c].type=values.type @@ -121,15 +121,15 @@ def has_missing_data? end # Return a nested hash using fields as keys and # an array constructed of hashes with other values. - # If block provided, is used to provide the - # values, with parameters +row+ of dataset, + # If block provided, is used to provide the + # values, with parameters +row+ of dataset, # +current+ last hash on hierarchy and # +name+ of the key to include def nest(*tree_keys,&block) tree_keys=tree_keys[0] if tree_keys[0].is_a? Array - out=Hash.new + out=Hash.new each do |row| - current=out + current=out # Create tree tree_keys[0,tree_keys.size-1].each do |f| root=row[f] @@ -162,7 +162,7 @@ def initialize(vectors={}, fields=[]) @cases=0 @gsl=nil @i=nil - + if vectors.instance_of? Array @fields=vectors.dup @vectors=vectors.inject({}){|a,x| a[x]=Statsample::Vector.new(); a} @@ -174,10 +174,10 @@ def initialize(vectors={}, fields=[]) check_length end end - # + # # Creates a copy of the given dataset, deleting all the cases with # missing data on one of the vectors. - # + # # @param array of fields to include. No value include all fields # def dup_only_valid(*fields_to_include) @@ -201,12 +201,12 @@ def dup_only_valid(*fields_to_include) ds end # - # Returns a duplicate of the Dataset. + # Returns a duplicate of the Dataset. # All vectors are copied, so any modification on new # dataset doesn't affect original dataset's vectors. # If fields given as parameter, only include those vectors. # - # @param array of fields to include. No value include all fields + # @param array of fields to include. No value include all fields # @return {Statsample::Dataset} def dup(*fields_to_include) if fields_to_include.size==1 and fields_to_include[0].is_a? Array @@ -224,15 +224,15 @@ def dup(*fields_to_include) ds.name= self.name ds end - - + + # Returns an array with the fields from first argumen to last argument def from_to(from,to) raise ArgumentError, "Field #{from} should be on dataset" if !@fields.include? from raise ArgumentError, "Field #{to} should be on dataset" if !@fields.include? to @fields.slice(@fields.index(from)..@fields.index(to)) end - + # Returns (when possible) a cheap copy of dataset. # If no vector have missing values, returns original vectors. # If missing values presents, uses Dataset.dup_only_valid. @@ -253,7 +253,7 @@ def clone_only_valid(*fields_to_include) # Returns a shallow copy of Dataset. # Object id will be distinct, but @vectors will be the same. # @param array of fields to include. No value include all fields - # @return {Statsample::Dataset} + # @return {Statsample::Dataset} def clone(*fields_to_include) if fields_to_include.size==1 and fields_to_include[0].is_a? Array fields_to_include=fields_to_include[0] @@ -280,7 +280,7 @@ def dup_empty Dataset.new(vectors,@fields.dup) end # Merge vectors from two datasets - # In case of name collition, the vectors names are changed to + # In case of name collition, the vectors names are changed to # x_1, x_2 .... # # @return {Statsample::Dataset} @@ -354,7 +354,7 @@ def standarize # Generate a matrix, based on fields of dataset # # @return {::Matrix} - + def collect_matrix rows=@fields.collect{|row| @fields.collect{|col| @@ -363,7 +363,7 @@ def collect_matrix } Matrix.rows(rows) end - + # We have the same datasets if +vectors+ and +fields+ are the same # # @return {Boolean} @@ -371,7 +371,7 @@ def ==(d2) @vectors==d2.vectors and @fields==d2.fields end # Returns vector c - # + # # @return {Statsample::Vector} def col(c) @vectors[c] @@ -409,18 +409,18 @@ def bootstrap(n=nil) # Can only add one case and no error check if performed # You SHOULD use #update_valid_data at the end of insertion cycle # - # + # def add_case_array(v) v.each_index {|i| d=@vectors[@fields[i]].data; d.push(v[i])} end # Insert a case, using: # * Array: size equal to number of vectors and values in the same order as fields # * Hash: keys equal to fields - # If uvd is false, #update_valid_data is not executed after - # inserting a case. This is very useful if you want to increase the - # performance on inserting many cases, because #update_valid_data + # If uvd is false, #update_valid_data is not executed after + # inserting a case. This is very useful if you want to increase the + # performance on inserting many cases, because #update_valid_data # performs check on vectors and on the dataset - + def add_case(v,uvd=true) case v when Array @@ -440,7 +440,7 @@ def add_case(v,uvd=true) update_valid_data end end - # Check vectors and fields after inserting data. Use only + # Check vectors and fields after inserting data. Use only # after #add_case_array or #add_case with second parameter to false def update_valid_data @gsl=nil @@ -459,7 +459,7 @@ def delete_vector(*args) @vectors.delete(name) end end - + def add_vectors_by_split_recode(name_,join='-',sep=Statsample::SPLIT_TOKEN) split=@vectors[name_].split_by_separator(sep) i=1 @@ -476,7 +476,7 @@ def add_vectors_by_split(name,join='-',sep=Statsample::SPLIT_TOKEN) add_vector(name+join+k,v) } end - + def vector_by_calculation(type=:scale) a=[] each do |row| @@ -485,7 +485,7 @@ def vector_by_calculation(type=:scale) a.to_vector(type) end # Returns a vector with sumatory of fields - # if fields parameter is empty, sum all fields + # if fields parameter is empty, sum all fields def vector_sum(fields=nil) fields||=@fields vector=collect_with_index do |row, i| @@ -504,7 +504,7 @@ def check_fields(fields) raise "Fields #{(fields-@fields).join(", ")} doesn't exists on dataset" if (fields-@fields).size>0 fields end - + # Returns a vector with the numbers of missing values for a case def vector_missing_values(fields=nil) fields=check_fields(fields) @@ -570,7 +570,7 @@ def check_length # :nodoc: def each_vector # :yield: |key, vector| @fields.each{|k| yield k, @vectors[k]} end - + if Statsample::STATSAMPLE__.respond_to?(:case_as_hash) def case_as_hash(c) # :nodoc: Statsample::STATSAMPLE__.case_as_hash(self,c) @@ -598,7 +598,7 @@ def _case_as_hash(c) # :nodoc: def _case_as_array(c) # :nodoc: @fields.collect {|x| @vectors[x][c]} end - + # Returns each case as a hash def each begin @@ -613,7 +613,7 @@ def each raise DatasetException.new(self, e) end end - + # Returns each case as hash and index def each_with_index # :yield: |case, i| begin @@ -628,7 +628,7 @@ def each_with_index # :yield: |case, i| raise DatasetException.new(self, e) end end - + # Returns each case as an array, coding missing values as nils def each_array_with_nils m=fields.size @@ -702,7 +702,7 @@ def recode!(vector_name) } @vectors[vector_name].set_valid_data end - + def crosstab(v1,v2,opts={}) Statsample::Crosstab.new(@vectors[v1], @vectors[v2],opts) end @@ -714,7 +714,7 @@ def crosstab(v1,v2,opts={}) raise ArgumentError,"Should pass a Statsample::Vector" end end - # Return data as a matrix. Column are ordered by #fields and + # Return data as a matrix. Column are ordered by #fields and # rows by orden of insertion def to_matrix rows=[] @@ -723,12 +723,12 @@ def to_matrix } Matrix.rows(rows) end - + if Statsample.has_gsl? def clear_gsl @gsl=nil end - + def to_gsl if @gsl.nil? if cases.nil? @@ -741,30 +741,31 @@ def to_gsl end @gsl end - + end - + # Return a correlation matrix for fields included as parameters. # By default, uses all fields of dataset - def correlation_matrix(fields=nil) - if fields - ds=clone(fields) - else - ds=self - end - Statsample::Bivariate.correlation_matrix(ds) + def correlation_matrix(fields = nil) + if fields + ds = clone(fields) + else + ds = self end - # Return a correlation matrix for fields included as parameters. - # By default, uses all fields of dataset - def covariance_matrix(fields=nil) - if fields - ds=clone(fields) - else - ds=self - end - Statsample::Bivariate.covariance_matrix(ds) + Statsample::Bivariate.correlation_matrix(ds) + end + + # Return a correlation matrix for fields included as parameters. + # By default, uses all fields of dataset + def covariance_matrix(fields = nil) + if fields + ds = clone(fields) + else + ds = self end - + Statsample::Bivariate.covariance_matrix(ds) + end + # Create a new dataset with all cases which the block returns true def filter ds=self.dup_empty @@ -775,7 +776,7 @@ def filter ds.name=_("%s(filtered)") % @name ds end - + # creates a new vector with the data of a given field which the block returns true def filter_field(field) a=[] @@ -784,11 +785,11 @@ def filter_field(field) end a.to_vector(@vectors[field].type) end - + # Creates a Stastample::Multiset, using one or more fields # to split the dataset. - - + + def to_multiset_by_split(*fields) require 'statsample/multiset' if fields.size==1 @@ -798,7 +799,7 @@ def to_multiset_by_split(*fields) end end # Creates a Statsample::Multiset, using one field - + def to_multiset_by_split_one_field(field) raise ArgumentError,"Should use a correct field name" if !@fields.include? field factors=@vectors[field].factors @@ -815,7 +816,7 @@ def to_multiset_by_split_one_field(field) v1.type=@vectors[k1].type v1.name=@vectors[k1].name v1.labels=@vectors[k1].labels - + } } ms @@ -838,7 +839,7 @@ def to_multiset_by_split_multiple_fields(*fields) p1=eval "Proc.new {|c| ms[["+fields.collect{|f| "c['#{f}']"}.join(",")+"]].add_case(c,false) }" each{|c| p1.call(c)} - + ms.datasets.each do |k,ds| ds.update_valid_data ds.name=fields.size.times.map {|i| @@ -846,15 +847,15 @@ def to_multiset_by_split_multiple_fields(*fields) sk=k[i] @vectors[f].labeling(sk) }.join("-") - ds.vectors.each{|k1,v1| + ds.vectors.each{|k1,v1| v1.type=@vectors[k1].type v1.name=@vectors[k1].name v1.labels=@vectors[k1].labels - + } end ms - + end # Returns a vector, based on a string with a calculation based # on vector @@ -923,14 +924,14 @@ def inspect end # Creates a new dataset for one to many relations # on a dataset, based on pattern of field names. - # + # # for example, you have a survey for number of children # with this structure: # id, name, child_name_1, child_age_1, child_name_2, child_age_2 - # with + # with # ds.one_to_many(%w{id}, "child_%v_%n" # the field of first parameters will be copied verbatim - # to new dataset, and fields which responds to second + # to new dataset, and fields which responds to second # pattern will be added one case for each different %n. # For example # cases=[ @@ -942,13 +943,13 @@ def inspect # cases.each {|c| ds.add_case_array c } # ds.one_to_many(['id'],'car_%v%n').to_matrix # => Matrix[ - # ["red", "1", 10], + # ["red", "1", 10], # ["blue", "1", 20], # ["green", "2", 15], # ["orange", "2", 30], # ["white", "2", 20] # ] - # + # def one_to_many(parent_fields, pattern) #base_pattern=pattern.gsub(/%v|%n/,"") re=Regexp.new pattern.gsub("%v","(.+?)").gsub("%n","(\\d+?)") @@ -962,7 +963,7 @@ def one_to_many(parent_fields, pattern) @fields.each do |f| if f=~re if !vars.include? $1 - vars.push($1) + vars.push($1) h[$1]=Statsample::Vector.new([], @vectors[f].type) end max_n=$2.to_i if max_n < $2.to_i @@ -986,7 +987,7 @@ def one_to_many(parent_fields, pattern) row_out["_col_id"]=n ds.add_case(row_out,false) end - + end end ds.update_valid_data diff --git a/lib/statsample/matrix.rb b/lib/statsample/matrix.rb index 662bd0a..e7ad8b3 100644 --- a/lib/statsample/matrix.rb +++ b/lib/statsample/matrix.rb @@ -27,14 +27,14 @@ def to_dataset if defined? :eigenpairs alias_method :eigenpairs_ruby, :eigenpairs end - + if Statsample.has_gsl? # Optimize eigenpairs of extendmatrix module using gsl def eigenpairs to_gsl.eigenpairs end end - + def eigenvalues eigenpairs.collect {|v| v[0]} end @@ -44,11 +44,11 @@ def eigenvectors def eigenvectors_matrix Matrix.columns(eigenvectors) end - - - - + + + + def to_gsl out=[] self.row_size.times{|i| @@ -76,7 +76,7 @@ class Matrix def to_gsl self end - + def to_dataset f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| _("VAR_%d") % (i+1) } ds=Statsample::Dataset.new(f) @@ -91,7 +91,7 @@ def to_dataset ds.name=self.name if self.respond_to? :name ds end - + def row_size size1 end @@ -110,18 +110,18 @@ def eigenvalues def eigenvectors eigenpairs.collect {|v| v[1]} end - + # Matrix sum of squares def mssq sum=0 to_v.each {|i| sum+=i**2} sum end - + def eigenvectors_matrix eigval, eigvec= GSL::Eigen.symmv(self) GSL::Eigen::symmv_sort(eigval, eigvec, GSL::Eigen::SORT_VAL_DESC) - eigvec + eigvec end def eigenpairs eigval, eigvec= GSL::Eigen.symmv(self) @@ -130,7 +130,7 @@ def eigenpairs [eigval[i],eigvec.get_col(i)] } end - + #def eigenpairs_ruby # self.to_matrix.eigenpairs_ruby #end @@ -158,7 +158,7 @@ def total_sum module Statsample # Module to add names to X and Y fields module NamedMatrix - include Summarizable + include Summarizable def fields raise "Should be square" if !square? @@ -178,10 +178,10 @@ def fields_y=(v) @fields_y=v end def fields_x - @fields_x||=row_size.times.collect {|i| _("X%d") % i} + @fields_x||=row_size.times.collect {|i| _("X%d") % i} end def fields_y - @fields_y||=column_size.times.collect {|i| _("Y%d") % i} + @fields_y||=column_size.times.collect {|i| _("Y%d") % i} end def name @@ -195,13 +195,13 @@ def get_new_name @@named_matrix+=1 _("Matrix %d") % @@named_matrix end - + end # Module to add method for variance/covariance and correlation matrices # == Usage # matrix=Matrix[[1,2],[2,3]] # matrix.extend CovariateMatrix - # + # module CovariateMatrix include NamedMatrix @@covariatematrix=0 @@ -217,7 +217,7 @@ def _type else @type end - + end def _type=(t) @type=t @@ -233,7 +233,7 @@ def correlation end } }) - matrix.extend CovariateMatrix + matrix.extend CovariateMatrix matrix.fields_x=fields_x matrix.fields_y=fields_y matrix._type=:correlation @@ -242,19 +242,19 @@ def correlation self end end - - + + # Get variance for field k - # + # def variance(k) submatrix([k])[0,0] end - + def get_new_name @@covariatematrix+=1 _("Covariate matrix %d") % @@covariatematrix end - + # Select a submatrix of factors. If you have a correlation matrix # with a, b and c, you could obtain a submatrix of correlations of # a and b, b and c or a and b @@ -264,7 +264,7 @@ def get_new_name # # Example: # a=Matrix[[1.0, 0.3, 0.2], - # [0.3, 1.0, 0.5], + # [0.3, 1.0, 0.5], # [0.2, 0.5, 1.0]] # a.extend CovariateMatrix # a.fields=%w{a b c} @@ -272,31 +272,31 @@ def get_new_name # => Matrix[[0.5],[0.3]] # a.submatrix(%w{c a}) # => Matrix[[1.0, 0.2] , [0.2, 1.0]] - def submatrix(rows,columns=nil) - raise ArgumentError, "rows shouldn't be empty" if rows.respond_to? :size and rows.size==0 - columns||=rows + def submatrix(rows,columns = nil) + raise ArgumentError, "rows shouldn't be empty" if rows.respond_to? :size and rows.size == 0 + columns ||= rows # Convert all fields on index - row_index=rows.collect {|v| - r=v.is_a?(Numeric) ? v : fields_x.index(v) + row_index = rows.collect do |v| + r = v.is_a?(Numeric) ? v : fields_x.index(v) raise "Index #{v} doesn't exists on matrix" if r.nil? r - } - column_index=columns.collect {|v| - r=v.is_a?(Numeric) ? v : fields_y.index(v) + end + + column_index = columns.collect do |v| + r = v.is_a?(Numeric) ? v : fields_y.index(v) raise "Index #{v} doesn't exists on matrix" if r.nil? r - } - - + end + + fx=row_index.collect {|v| fields_x[v]} fy=column_index.collect {|v| fields_y[v]} - - matrix= Matrix.rows(row_index.collect {|i| - row=column_index.collect {|j| self[i,j]}}) - matrix.extend CovariateMatrix - matrix.fields_x=fx - matrix.fields_y=fy - matrix._type=_type + + matrix = Matrix.rows(row_index.collect { |i| column_index.collect { |j| self[i, j] }}) + matrix.extend CovariateMatrix + matrix.fields_x = fx + matrix.fields_y = fy + matrix._type = _type matrix end def report_building(generator) diff --git a/lib/statsample/vector.rb b/lib/statsample/vector.rb index 64f5111..1ad98a4 100644 --- a/lib/statsample/vector.rb +++ b/lib/statsample/vector.rb @@ -5,8 +5,9 @@ module Statsample::VectorShorthands # Creates a new Statsample::Vector object # Argument should be equal to Vector.new def to_vector(*args) - Statsample::Vector.new(self,*args) - end + Statsample::Vector.new(self,*args) + end + # Creates a new Statsample::Vector object of type :scale def to_scale(*args) Statsample::Vector.new(self, :scale, *args) @@ -253,21 +254,22 @@ def push(v) @data.push(v) set_valid_data end + # Dicotomize the vector with 0 and 1, based on lowest value # If parameter if defined, this value and lower # will be 0 and higher, 1 - def dichotomize(low=nil) - fs=factors - low||=factors.min - @data_with_nils.collect{|x| + def dichotomize(low = nil) + low ||= factors.min + + @data_with_nils.collect do |x| if x.nil? nil - elsif x>low + elsif x > low 1 else 0 end - }.to_scale + end.to_scale end # Iterate on each item. # Equivalent to From 09b301a2180cf2622a301e56f5da052e59e35bd1 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 24 Mar 2015 16:08:01 -0300 Subject: [PATCH 007/119] Replace Hoe with gemspec This PR should move the dependencies from the Gemfile the configuration from the Rakefile (via Hoe) to statsample.gemspec. I still want to make sure that all the versions in the gemspec are correct and that I'm not forgetting anything before merging with master. Fix #14 --- Gemfile | 18 +------ Rakefile | 117 +++++---------------------------------------- statsample.gemspec | 99 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 121 deletions(-) create mode 100644 statsample.gemspec diff --git a/Gemfile b/Gemfile index ea8fc56..38eb365 100644 --- a/Gemfile +++ b/Gemfile @@ -1,18 +1,2 @@ source "https://www.rubygems.org" -gem 'minitest' -gem 'rdoc' -gem 'mocha', '0.14.0' #:require=>'mocha/setup' -gem 'shoulda','3.5.0' -gem 'shoulda-matchers','2.2.0' -gem 'hoe' -#gem 'bio-statsample-timeseries' -gem 'reportbuilder' -gem 'dirty-memoize' -gem 'distribution' -gem 'extendmatrix' -gem 'minimization' -gem 'rserve-client' -gem 'rubyvis' -gem 'spreadsheet' -gem 'rb-gsl' -gem 'awesome_print' +gemspec diff --git a/Rakefile b/Rakefile index d4e23b9..980cd99 100644 --- a/Rakefile +++ b/Rakefile @@ -1,32 +1,20 @@ -#!/usr/bin/ruby -# -*- ruby -*- -# -*- coding: utf-8 -*- -$:.unshift(File.dirname(__FILE__)+'/lib/') +$:.unshift File.expand_path("../lib/", __FILE__) -require 'rubygems' -require 'statsample' -require 'hoe' -require 'rdoc' +require 'statsample/version' +require 'rake' +require 'rdoc/task' +require 'bundler/gem_tasks' -Hoe.plugin :git -Hoe.plugin :doofus -desc "Ruby Lint" -task :lint do - executable=Config::CONFIG['RUBY_INSTALL_NAME'] - Dir.glob("lib/**/*.rb") {|f| - if !system %{#{executable} -w -c "#{f}"} - puts "Error on: #{f}" - end - } +# Setup the necessary gems, specified in the gemspec. +require 'bundler' +begin + Bundler.setup(:default, :development) +rescue Bundler::BundlerError => e + $stderr.puts e.message + $stderr.puts "Run `bundle install` to install missing gems" + exit e.status_code end -task :release do -system %{git push origin master} -end - -task "clobber_docs" do - # Only to omit warnings -end desc "Update pot/po files." task "gettext:updatepo" do require 'gettext/tools' @@ -37,83 +25,4 @@ desc "Create mo-files" task "gettext:makemo" do require 'gettext/tools' GetText.create_mofiles() - # GetText.create_mofiles(true, "po", "locale") # This is for "Ruby on Rails". -end - -h=Hoe.spec('statsample') do - self.version=Statsample::VERSION - self.urls=["https://github.com/clbustos/statsample"] - #self.testlib=:minitest - self.readme_file = 'README.md' - self.urls = ['https://github.com/clbustos/statsample'] - self.developer('Claudio Bustos', 'clbustos@gmail.com') - self.extra_deps << ["spreadsheet","~>0.6"] << ["reportbuilder", "~>1.4"] << ["minimization", "~>0.2.0"] << ["fastercsv", ">0"] << ["dirty-memoize", "~>0.0"] << ["extendmatrix","~>0.3.1"] << ["statsample-bivariate-extension", ">0"] << ["rserve-client"] << ["rubyvis"] << ["distribution"] - - self.extra_dev_deps << ["hoe","~>0"] << ["shoulda","~>3"] << ["minitest", "~>2"] << ["gettext", "~>0"] << ["mocha", "~>0"] << ["hoe-git", "~>0"] - - self.clean_globs << "test/images/*" << "demo/item_analysis/*" << "demo/Regression" - self.post_install_message = <<-EOF -*************************************************** -Thanks for installing statsample. - -On *nix, you could install statsample-optimization -to retrieve gems gsl, statistics2 and a C extension -to speed some methods. - - $ sudo gem install statsample-optimization - -On Ubuntu, install build-essential and libgsl0-dev -using apt-get. Compile ruby 1.8 or 1.9 from -source code first. - - $ sudo apt-get install build-essential libgsl0-dev - - -***************************************************** - EOF - self.need_rdoc=false end - -if Rake.const_defined?(:RDocTask) -Rake::RDocTask.new(:docs) do |rd| - rd.main = h.readme_file - rd.options << '-d' if (`which dot` =~ /\/dot/) unless - ENV['NODOT'] || Hoe::WINDOZE - rd.rdoc_dir = 'doc' - - rd.rdoc_files.include("lib/**/*.rb") - rd.rdoc_files += h.spec.extra_rdoc_files - rd.rdoc_files.reject! {|f| f=="Manifest.txt"} - title = h.spec.rdoc_options.grep(/^(-t|--title)=?$/).first - if title then - rd.options << title - - unless title =~ /\=/ then # for ['-t', 'title here'] - title_index = spec.rdoc_options.index(title) - rd.options << spec.rdoc_options[title_index + 1] - end - else - title = "#{h.name}-#{h.version} Documentation" - title = "#{h.rubyforge_name}'s " + title if h.rubyforge_name != h.name - rd.options << '--title' << title - end -end - -end - -desc 'Publish rdocs with analytics support' -task :publicar_docs => [:clean] do -# ruby %{agregar_adsense_a_doc.rb} - path = File.expand_path("./doc.yaml") - config = YAML.load(File.read(path)) - host = "#{config["user"]}@#{config["host"]}" - - remote_dir = config["dir"] - local_dir = h.local_rdoc_dir - Dir.glob(local_dir+"/**/*") {|file| - sh %{chmod 755 #{file}} - } - sh %{rsync #{h.rsync_args} #{local_dir}/ #{host}:#{remote_dir}} -end - -# vim: syntax=Ruby diff --git a/statsample.gemspec b/statsample.gemspec new file mode 100644 index 0000000..c95ace1 --- /dev/null +++ b/statsample.gemspec @@ -0,0 +1,99 @@ +$:.unshift File.expand_path("../lib/", __FILE__) + +require 'statsample/version' +require 'date' + +DESCRIPTION = < 0.6.5' + s.add_runtime_dependency 'reportbuilder', '~> 1.4' + s.add_runtime_dependency 'minimization' + s.add_runtime_dependency 'dirty-memoize' + s.add_runtime_dependency 'extendmatrix', '~> 0.3.1' + s.add_runtime_dependency 'statsample-bivariate-extension' + s.add_runtime_dependency 'rserve-client', '~> 0.2.5' + s.add_runtime_dependency 'rubyvis', '~> 0.5.0' + s.add_runtime_dependency 'distribution' + s.add_runtime_dependency 'rubyvis' + s.add_runtime_dependency 'rb-gsl' + s.add_runtime_dependency 'awesome_print' + + s.add_development_dependency 'bundler' + s.add_development_dependency 'rake' + s.add_development_dependency 'shoulda' + s.add_development_dependency 'shoulda-matchers', '~> 2.2' + s.add_development_dependency 'minitest' + s.add_development_dependency 'gettext' + s.add_development_dependency 'mocha' +end From 404983e304cfb2d24dd7e889c2e607503346b5a3 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Wed, 25 Mar 2015 21:05:31 -0300 Subject: [PATCH 008/119] Update dependencies in gemspec --- statsample.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/statsample.gemspec b/statsample.gemspec index c95ace1..36d3fed 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -80,9 +80,8 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'reportbuilder', '~> 1.4' s.add_runtime_dependency 'minimization' s.add_runtime_dependency 'dirty-memoize' - s.add_runtime_dependency 'extendmatrix', '~> 0.3.1' - s.add_runtime_dependency 'statsample-bivariate-extension' - s.add_runtime_dependency 'rserve-client', '~> 0.2.5' + s.add_runtime_dependency 'extendmatrix' + s.add_runtime_dependency 'rserve-client' s.add_runtime_dependency 'rubyvis', '~> 0.5.0' s.add_runtime_dependency 'distribution' s.add_runtime_dependency 'rubyvis' @@ -91,6 +90,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'bundler' s.add_development_dependency 'rake' + s.add_development_dependency 'rdoc' s.add_development_dependency 'shoulda' s.add_development_dependency 'shoulda-matchers', '~> 2.2' s.add_development_dependency 'minitest' From e6e9737140d688ba8159bbd2580410e87dcb411c Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Wed, 25 Mar 2015 22:24:20 -0300 Subject: [PATCH 009/119] Add `test` task to Rakefile --- Rakefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rakefile b/Rakefile index 980cd99..f201fd6 100644 --- a/Rakefile +++ b/Rakefile @@ -2,6 +2,7 @@ $:.unshift File.expand_path("../lib/", __FILE__) require 'statsample/version' require 'rake' +require 'rake/testtask' require 'rdoc/task' require 'bundler/gem_tasks' @@ -15,6 +16,10 @@ rescue Bundler::BundlerError => e exit e.status_code end +Rake::TestTask.new do |t| + t.pattern = "test/test_*.rb" +end + desc "Update pot/po files." task "gettext:updatepo" do require 'gettext/tools' From 322bfdb2a7e8eaf31930fa7e1b76bd058fee49cd Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 00:07:30 -0300 Subject: [PATCH 010/119] Add rdoc task to Rakefile --- Rakefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rakefile b/Rakefile index f201fd6..170eec1 100644 --- a/Rakefile +++ b/Rakefile @@ -20,6 +20,11 @@ Rake::TestTask.new do |t| t.pattern = "test/test_*.rb" end +RDoc::Task.new do |rdoc| + rdoc.main = "README.md" + rdoc.rdoc_files.include("README.md", "lib", "History.txt", "LICENSE.txt", "references.txt") +end + desc "Update pot/po files." task "gettext:updatepo" do require 'gettext/tools' From 56fa28149912631e3ffd67243eea5d38064a9024 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 00:31:33 -0300 Subject: [PATCH 011/119] Updated README with installation procedure Also removed setup.rb (outdated and unnecessary given rubygems) and Manifest.txt (also unnecessary; we can use `git` to build it when packaging the gem). The gemspec changes reflect the README's. --- LICENSE.txt | 2 +- Manifest.txt | 157 ----- README.md | 282 ++++---- setup.rb | 1585 -------------------------------------------- statsample.gemspec | 45 +- 5 files changed, 155 insertions(+), 1916 deletions(-) delete mode 100644 Manifest.txt delete mode 100644 setup.rb diff --git a/LICENSE.txt b/LICENSE.txt index 9d0b178..6886323 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2009-2014, Claudio Bustos +Copyright (c) 2009-2015, Claudio Bustos All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Manifest.txt b/Manifest.txt deleted file mode 100644 index f465a24..0000000 --- a/Manifest.txt +++ /dev/null @@ -1,157 +0,0 @@ -.travis.yml -Gemfile -Gemfile.lock -History.txt -LICENSE.txt -Manifest.txt -README.md -Rakefile -benchmarks/correlation_matrix_15_variables.rb -benchmarks/correlation_matrix_5_variables.rb -benchmarks/correlation_matrix_methods/correlation_matrix.ds -benchmarks/correlation_matrix_methods/correlation_matrix.html -benchmarks/correlation_matrix_methods/correlation_matrix.rb -benchmarks/correlation_matrix_methods/correlation_matrix.xls -benchmarks/correlation_matrix_methods/correlation_matrix_gsl_ruby.ods -benchmarks/correlation_matrix_methods/correlation_matrix_with_graphics.ods -benchmarks/correlation_matrix_methods/results.ds -benchmarks/factor_map.rb -benchmarks/helpers_benchmark.rb -data/locale/es/LC_MESSAGES/statsample.mo -doc_latex/manual/equations.tex -examples/boxplot.rb -examples/correlation_matrix.rb -examples/dataset.rb -examples/dominance_analysis.rb -examples/dominance_analysis_bootstrap.rb -examples/histogram.rb -examples/icc.rb -examples/levene.rb -examples/multiple_regression.rb -examples/multivariate_correlation.rb -examples/parallel_analysis.rb -examples/polychoric.rb -examples/principal_axis.rb -examples/reliability.rb -examples/scatterplot.rb -examples/t_test.rb -examples/tetrachoric.rb -examples/u_test.rb -examples/vector.rb -examples/velicer_map_test.rb -grab_references.rb -lib/spss.rb -lib/statsample.rb -lib/statsample/analysis.rb -lib/statsample/analysis/suite.rb -lib/statsample/analysis/suitereportbuilder.rb -lib/statsample/anova.rb -lib/statsample/anova/contrast.rb -lib/statsample/anova/oneway.rb -lib/statsample/anova/twoway.rb -lib/statsample/bivariate.rb -lib/statsample/bivariate/pearson.rb -lib/statsample/codification.rb -lib/statsample/converter/csv.rb -lib/statsample/converter/spss.rb -lib/statsample/converters.rb -lib/statsample/crosstab.rb -lib/statsample/dataset.rb -lib/statsample/dominanceanalysis.rb -lib/statsample/dominanceanalysis/bootstrap.rb -lib/statsample/factor.rb -lib/statsample/factor/map.rb -lib/statsample/factor/parallelanalysis.rb -lib/statsample/factor/pca.rb -lib/statsample/factor/principalaxis.rb -lib/statsample/factor/rotation.rb -lib/statsample/graph.rb -lib/statsample/graph/boxplot.rb -lib/statsample/graph/histogram.rb -lib/statsample/graph/scatterplot.rb -lib/statsample/histogram.rb -lib/statsample/matrix.rb -lib/statsample/multiset.rb -lib/statsample/regression.rb -lib/statsample/regression/multiple.rb -lib/statsample/regression/multiple/alglibengine.rb -lib/statsample/regression/multiple/baseengine.rb -lib/statsample/regression/multiple/gslengine.rb -lib/statsample/regression/multiple/matrixengine.rb -lib/statsample/regression/multiple/rubyengine.rb -lib/statsample/regression/simple.rb -lib/statsample/reliability.rb -lib/statsample/reliability/icc.rb -lib/statsample/reliability/multiscaleanalysis.rb -lib/statsample/reliability/scaleanalysis.rb -lib/statsample/reliability/skillscaleanalysis.rb -lib/statsample/resample.rb -lib/statsample/rserve_extension.rb -lib/statsample/shorthand.rb -lib/statsample/srs.rb -lib/statsample/test.rb -lib/statsample/test/bartlettsphericity.rb -lib/statsample/test/chisquare.rb -lib/statsample/test/f.rb -lib/statsample/test/kolmogorovsmirnov.rb -lib/statsample/test/levene.rb -lib/statsample/test/t.rb -lib/statsample/test/umannwhitney.rb -lib/statsample/test/wilcoxonsignedrank.rb -lib/statsample/vector.rb -lib/statsample/vector/gsl.rb -lib/statsample/version.rb -po/es/statsample.mo -po/es/statsample.po -po/statsample.pot -references.txt -setup.rb -test/fixtures/bank2.dat -test/fixtures/correlation_matrix.rb -test/fixtures/hartman_23.matrix -test/fixtures/repeated_fields.csv -test/fixtures/stock_data.csv -test/fixtures/test_csv.csv -test/fixtures/test_xls.xls -test/fixtures/tetmat_matrix.txt -test/fixtures/tetmat_test.txt -test/helpers_tests.rb -test/test_analysis.rb -test/test_anova_contrast.rb -test/test_anovaoneway.rb -test/test_anovatwoway.rb -test/test_anovatwowaywithdataset.rb -test/test_anovawithvectors.rb -test/test_bartlettsphericity.rb -test/test_bivariate.rb -test/test_codification.rb -test/test_crosstab.rb -test/test_csv.rb -test/test_dataset.rb -test/test_dominance_analysis.rb -test/test_factor.rb -test/test_factor_map.rb -test/test_factor_pa.rb -test/test_ggobi.rb -test/test_gsl.rb -test/test_histogram.rb -test/test_matrix.rb -test/test_multiset.rb -test/test_regression.rb -test/test_reliability.rb -test/test_reliability_icc.rb -test/test_reliability_skillscale.rb -test/test_resample.rb -test/test_rserve_extension.rb -test/test_srs.rb -test/test_statistics.rb -test/test_stest.rb -test/test_stratified.rb -test/test_test_f.rb -test/test_test_kolmogorovsmirnov.rb -test/test_test_t.rb -test/test_umannwhitney.rb -test/test_vector.rb -test/test_wilcoxonsignedrank.rb -test/test_xls.rb -web/Rakefile diff --git a/README.md b/README.md index f91a6d8..935fc16 100644 --- a/README.md +++ b/README.md @@ -6,100 +6,127 @@ Homepage :: https://github.com/sciruby/statsample -## DESCRIPTION +# Installation + +You should have a recent version of GSL and R (with the `irr` and `Rserve` libraries) installed. In Ubuntu: -A suite for basic and advanced statistics on Ruby. Tested on Ruby 2.1.1p76 (June 2014), 1.8.7, 1.9.1, 1.9.2 (April, 2010), ruby-head(June, 2011) and JRuby 1.4 (Ruby 1.8.7 compatible). +```bash +$ sudo apt-get install libgs10-dev r-base r-base-dev +$ sudo Rscript -e "install.packages(c('Rserve', 'irr'))" +``` + +With these libraries in place, just install from rubygems: + +```bash +$ [sudo] gem install statsample +``` + +On *nix, you should install statsample-optimization to retrieve gems gsl, statistics2 and a C extension to speed some methods. + +```bash +$ [sudo] gem install statsample-optimization +``` + +If you need to work on Structural Equation Modeling, you could see +statsample-sem+. You need R with +sem+ or +OpenMx+ [http://openmx.psyc.virginia.edu/] libraries installed + +```bash +$ [sudo] gem install statsample-sem +``` + +# Description + +A suite for basic and advanced statistics on Ruby. Tested on CRuby 1.9.3, 2.0.0 and 2.1.1. See `.travis.yml` for more information. Include: -* Descriptive statistics: frequencies, median, mean, standard error, skew, kurtosis (and many others). -* Imports and exports datasets from and to Excel, CSV and plain text files. -* Correlations: Pearson's r, Spearman's rank correlation (rho), point biserial, tau a, tau b and gamma. Tetrachoric and Polychoric correlation provides by +statsample-bivariate-extension+ gem. -* Intra-class correlation -* Anova: generic and vector-based One-way ANOVA and Two-way ANOVA, with contrasts for One-way ANOVA. -* Tests: F, T, Levene, U-Mannwhitney. -* Regression: Simple, Multiple (OLS), Probit and Logit -* Factorial Analysis: Extraction (PCA and Principal Axis), Rotation (Varimax, Equimax, Quartimax) and Parallel Analysis and Velicer's MAP test, for estimation of number of factors. -* Reliability analysis for simple scale and a DSL to easily analyze multiple scales using factor analysis and correlations, if you want it. -* Basic time series support -* Dominance Analysis, with multivariate dependent and bootstrap (Azen & Budescu) -* Sample calculation related formulas -* Structural Equation Modeling (SEM), using R libraries +sem+ and +OpenMx+ -* Creates reports on text, html and rtf, using ReportBuilder gem -* Graphics: Histogram, Boxplot and Scatterplot +- Descriptive statistics: frequencies, median, mean, standard error, skew, kurtosis (and many others). +- Imports and exports datasets from and to Excel, CSV and plain text files. +- Correlations: Pearson's r, Spearman's rank correlation (rho), point biserial, tau a, tau b and gamma. Tetrachoric and Polychoric correlation provides by +statsample-bivariate-extension+ gem. +- Intra-class correlation +- Anova: generic and vector-based One-way ANOVA and Two-way ANOVA, with contrasts for One-way ANOVA. +- Tests: F, T, Levene, U-Mannwhitney. +- Regression: Simple, Multiple (OLS), Probit and Logit +- Factorial Analysis: Extraction (PCA and Principal Axis), Rotation (Varimax, Equimax, Quartimax) and Parallel Analysis and Velicer's MAP test, for estimation of number of factors. +- Reliability analysis for simple scale and a DSL to easily analyze multiple scales using factor analysis and correlations, if you want it. +- Basic time series support +- Dominance Analysis, with multivariate dependent and bootstrap (Azen & Budescu) +- Sample calculation related formulas +- Structural Equation Modeling (SEM), using R libraries +sem+ and +OpenMx+ +- Creates reports on text, html and rtf, using ReportBuilder gem +- Graphics: Histogram, Boxplot and Scatterplot ## Principles -* Software Design: - * One module/class for each type of analysis - * Options can be set as hash on initialize() or as setters methods - * Clean API for interactive sessions - * summary() returns all necessary informacion for interactive sessions - * All statistical data available though methods on objects - * All (important) methods should be tested. Better with random data. -* Statistical Design - * Results are tested against text results, SPSS and R outputs. - * Go beyond Null Hiphotesis Testing, using confidence intervals and effect sizes when possible - * (When possible) All references for methods are documented, providing sensible information on documentation - -## Features - -* Classes for manipulation and storage of data: - * Statsample::Vector: An extension of an array, with statistical methods like sum, mean and standard deviation - * Statsample::Dataset: a group of Statsample::Vector, analog to a excel spreadsheet or a dataframe on R. The base of almost all operations on statsample. - * Statsample::Multiset: multiple datasets with same fields and type of vectors -* Anova module provides generic Statsample::Anova::OneWay and vector based Statsample::Anova::OneWayWithVectors. Also you can create contrast using Statsample::Anova::Contrast -* Module Statsample::Bivariate provides covariance and pearson, spearman, point biserial, tau a, tau b, gamma, tetrachoric (see Bivariate::Tetrachoric) and polychoric (see Bivariate::Polychoric) correlations. Include methods to create correlation and covariance matrices -* Multiple types of regression. - * Simple Regression : Statsample::Regression::Simple - * Multiple Regression: Statsample::Regression::Multiple - * Logit Regression: Statsample::Regression::Binomial::Logit - * Probit Regression: Statsample::Regression::Binomial::Probit -* Factorial Analysis algorithms on Statsample::Factor module. - * Classes for Extraction of factors: - * Statsample::Factor::PCA - * Statsample::Factor::PrincipalAxis - * Classes for Rotation of factors: - * Statsample::Factor::Varimax - * Statsample::Factor::Equimax - * Statsample::Factor::Quartimax - * Classes for calculation of factors to retain - * Statsample::Factor::ParallelAnalysis performs Horn's 'parallel analysis' to a principal components analysis to adjust for sample bias in the retention of components. - * Statsample::Factor::MAP performs Velicer's Minimum Average Partial (MAP) test, which retain components as long as the variance in the correlation matrix represents systematic variance. -* Dominance Analysis. Based on Budescu and Azen papers, dominance analysis is a method to analyze the relative importance of one predictor relative to another on multiple regression - * Statsample::DominanceAnalysis class can report dominance analysis for a sample, using uni or multivariate dependent variables - * Statsample::DominanceAnalysis::Bootstrap can execute bootstrap analysis to determine dominance stability, as recomended by Azen & Budescu (2003) link[http://psycnet.apa.org/journals/met/8/2/129/]. -* Module Statsample::Codification, to help to codify open questions -* Converters to import and export data: - * Statsample::Database : Can create sql to create tables, read and insert data - * Statsample::CSV : Read and write CSV files - * Statsample::Excel : Read and write Excel files - * Statsample::Mx : Write Mx Files - * Statsample::GGobi : Write Ggobi files -* Module Statsample::Crosstab provides function to create crosstab for categorical data -* Module Statsample::Reliability provides functions to analyze scales with psychometric methods. - * Class Statsample::Reliability::ScaleAnalysis provides statistics like mean, standard deviation for a scale, Cronbach's alpha and standarized Cronbach's alpha, and for each item: mean, correlation with total scale, mean if deleted, Cronbach's alpha is deleted. - * Class Statsample::Reliability::MultiScaleAnalysis provides a DSL to easily analyze reliability of multiple scales and retrieve correlation matrix and factor analysis of them. - * Class Statsample::Reliability::ICC provides intra-class correlation, using Shrout & Fleiss(1979) and McGraw & Wong (1996) formulations. -* Module Statsample::SRS (Simple Random Sampling) provides a lot of functions to estimate standard error for several type of samples -* Module Statsample::Test provides several methods and classes to perform inferencial statistics - * Statsample::Test::BartlettSphericity - * Statsample::Test::ChiSquare - * Statsample::Test::F - * Statsample::Test::KolmogorovSmirnov (only D value) - * Statsample::Test::Levene - * Statsample::Test::UMannWhitney - * Statsample::Test::T - * Statsample::Test::WilcoxonSignedRank -* Module Graph provides several classes to create beautiful graphs using rubyvis - * Statsample::Graph::Boxplot - * Statsample::Graph::Histogram - * Statsample::Graph::Scatterplot -* Gem bio-statsample-timeseries provides module Statsample::TimeSeries with support for time series, including ARIMA estimation using Kalman-Filter. -* Gem statsample-sem provides a DSL to R libraries +sem+ and +OpenMx+ -* Gem statsample-glm provides you with GML method, to work with Logistic, Poisson and Gaussian regression ,using ML or IRWLS. -* Close integration with gem reportbuilder, to easily create reports on text, html and rtf formats. - -# Examples of use: +- Software Design: + - One module/class for each type of analysis + - Options can be set as hash on initialize() or as setters methods + - Clean API for interactive sessions + - summary() returns all necessary informacion for interactive sessions + - All statistical data available though methods on objects + - All (important) methods should be tested. Better with random data. +- Statistical Design + - Results are tested against text results, SPSS and R outputs. + - Go beyond Null Hiphotesis Testing, using confidence intervals and effect sizes when possible + - (When possible) All references for methods are documented, providing sensible information on documentation + +# Features + +- Classes for manipulation and storage of data: + - Statsample::Vector: An extension of an array, with statistical methods like sum, mean and standard deviation + - Statsample::Dataset: a group of Statsample::Vector, analog to a excel spreadsheet or a dataframe on R. The base of almost all operations on statsample. + - Statsample::Multiset: multiple datasets with same fields and type of vectors +- Anova module provides generic Statsample::Anova::OneWay and vector based Statsample::Anova::OneWayWithVectors. Also you can create contrast using Statsample::Anova::Contrast +- Module Statsample::Bivariate provides covariance and pearson, spearman, point biserial, tau a, tau b, gamma, tetrachoric (see Bivariate::Tetrachoric) and polychoric (see Bivariate::Polychoric) correlations. Include methods to create correlation and covariance matrices +- Multiple types of regression. + - Simple Regression : Statsample::Regression::Simple + - Multiple Regression: Statsample::Regression::Multiple + - Logit Regression: Statsample::Regression::Binomial::Logit + - Probit Regression: Statsample::Regression::Binomial::Probit +- Factorial Analysis algorithms on Statsample::Factor module. + - Classes for Extraction of factors: + - Statsample::Factor::PCA + - Statsample::Factor::PrincipalAxis + - Classes for Rotation of factors: + - Statsample::Factor::Varimax + - Statsample::Factor::Equimax + - Statsample::Factor::Quartimax + - Classes for calculation of factors to retain + - Statsample::Factor::ParallelAnalysis performs Horn's 'parallel analysis' to a principal components analysis to adjust for sample bias in the retention of components. + - Statsample::Factor::MAP performs Velicer's Minimum Average Partial (MAP) test, which retain components as long as the variance in the correlation matrix represents systematic variance. +- Dominance Analysis. Based on Budescu and Azen papers, dominance analysis is a method to analyze the relative importance of one predictor relative to another on multiple regression + - Statsample::DominanceAnalysis class can report dominance analysis for a sample, using uni or multivariate dependent variables + - Statsample::DominanceAnalysis::Bootstrap can execute bootstrap analysis to determine dominance stability, as recomended by Azen & Budescu (2003) link[http://psycnet.apa.org/journals/met/8/2/129/]. +- Module Statsample::Codification, to help to codify open questions +- Converters to import and export data: + - Statsample::Database : Can create sql to create tables, read and insert data + - Statsample::CSV : Read and write CSV files + - Statsample::Excel : Read and write Excel files + - Statsample::Mx : Write Mx Files + - Statsample::GGobi : Write Ggobi files +- Module Statsample::Crosstab provides function to create crosstab for categorical data +- Module Statsample::Reliability provides functions to analyze scales with psychometric methods. + - Class Statsample::Reliability::ScaleAnalysis provides statistics like mean, standard deviation for a scale, Cronbach's alpha and standarized Cronbach's alpha, and for each item: mean, correlation with total scale, mean if deleted, Cronbach's alpha is deleted. + - Class Statsample::Reliability::MultiScaleAnalysis provides a DSL to easily analyze reliability of multiple scales and retrieve correlation matrix and factor analysis of them. + - Class Statsample::Reliability::ICC provides intra-class correlation, using Shrout & Fleiss(1979) and McGraw & Wong (1996) formulations. +- Module Statsample::SRS (Simple Random Sampling) provides a lot of functions to estimate standard error for several type of samples +- Module Statsample::Test provides several methods and classes to perform inferencial statistics + - Statsample::Test::BartlettSphericity + - Statsample::Test::ChiSquare + - Statsample::Test::F + - Statsample::Test::KolmogorovSmirnov (only D value) + - Statsample::Test::Levene + - Statsample::Test::UMannWhitney + - Statsample::Test::T + - Statsample::Test::WilcoxonSignedRank +- Module Graph provides several classes to create beautiful graphs using rubyvis + - Statsample::Graph::Boxplot + - Statsample::Graph::Histogram + - Statsample::Graph::Scatterplot +- Gem bio-statsample-timeseries provides module Statsample::TimeSeries with support for time series, including ARIMA estimation using Kalman-Filter. +- Gem statsample-sem provides a DSL to R libraries +sem+ and +OpenMx+ +- Gem statsample-glm provides you with GML method, to work with Logistic, Poisson and Gaussian regression ,using ML or IRWLS. +- Close integration with gem reportbuilder, to easily create reports on text, html and rtf formats. + +# Usage See the [examples folder](https://github.com/clbustos/statsample/tree/master/examples/) too. @@ -109,13 +136,18 @@ See the [examples folder](https://github.com/clbustos/statsample/tree/master/exa require 'statsample' ss_analysis(Statsample::Graph::Boxplot) do - n=30 - a=rnorm(n-1,50,10) - b=rnorm(n, 30,5) - c=rnorm(n,5,1) + n = 30 + a = rnorm(n-1, 50, 10) + b = rnorm(n, 30, 5) + c = rnorm(n, 5, 1) a.push(2) - boxplot(:vectors=>[a,b,c], :width=>300, :height=>300, :groups=>%w{first first second}, :minimum=>0) + boxplot(vectors: [a, b, c], + width: 300, + height: 300, + groups: %w{first first second}, + minimum: 0) end + Statsample::Analysis.run # Open svg file on *nix application defined ``` @@ -127,68 +159,26 @@ require 'statsample' # and correlation matrix ss_analysis("Statsample::Bivariate.correlation_matrix") do - samples=1000 - ds=data_frame( - 'a'=>rnorm(samples), - 'b'=>rnorm(samples), - 'c'=>rnorm(samples), - 'd'=>rnorm(samples)) - cm=cor(ds) + samples = 1000 + ds = data_frame( + 'a' => rnorm(samples), + 'b' => rnorm(samples), + 'c' => rnorm(samples), + 'd' => rnorm(samples)) + cm = cor(ds) summary(cm) end Statsample::Analysis.run_batch # Echo output to console ``` -# Requirements - -Optional: - -* Plotting: gnuplot and rbgnuplot, SVG::Graph -* Factorial analysis and polychorical correlation(joint estimate and polychoric series): gsl library and rb-gsl (https://rubygems.org/gems/rb-gsl/). You should install it using gem install rb-gsl. - -*Note*: Use gsl 1.12.109 or later. - # Resources -* Source code on github :: http://github.com/clbustos/statsample -* Docs :: http://statsample.apsique.cl/ -* Bug report and feature request :: http://github.com/clbustos/statsample/issues -* E-mailing list :: http://groups.google.com/group/statsample - -# Installation - -```bash -$ sudo gem install statsample -``` - -On *nix, you should install statsample-optimization to retrieve gems gsl, statistics2 and a C extension to speed some methods. - -There are available precompiled version for Ruby 1.9 on x86, x86_64 and mingw32 archs. - -```bash -$ sudo gem install statsample-optimization -``` - -If you use Ruby 1.8, you should compile statsample-optimization, usign parameter --platform ruby - -```bash -$ sudo gem install statsample-optimization --platform ruby -``` - -If you need to work on Structural Equation Modeling, you could see +statsample-sem+. You need R with +sem+ or +OpenMx+ [http://openmx.psyc.virginia.edu/] libraries installed - -```bash -$ sudo gem install statsample-sem -``` - -Available setup.rb file - -```bash -sudo gem ruby setup.rb -``` +- Source code on github :: http://github.com/sciruby/statsample +- Bug report and feature request :: http://github.com/sciruby/statsample/issues +- E-mailing list :: https://groups.google.com/forum/#!forum/sciruby-dev -## License +# License BSD-3 (See LICENSE.txt) diff --git a/setup.rb b/setup.rb deleted file mode 100644 index 424a5f3..0000000 --- a/setup.rb +++ /dev/null @@ -1,1585 +0,0 @@ -# -# setup.rb -# -# Copyright (c) 2000-2005 Minero Aoki -# -# This program is free software. -# You can distribute/modify this program under the terms of -# the GNU LGPL, Lesser General Public License version 2.1. -# - -unless Enumerable.method_defined?(:map) # Ruby 1.4.6 - module Enumerable - alias map collect - end -end - -unless File.respond_to?(:read) # Ruby 1.6 - def File.read(fname) - open(fname) {|f| - return f.read - } - end -end - -unless Errno.const_defined?(:ENOTEMPTY) # Windows? - module Errno - class ENOTEMPTY - # We do not raise this exception, implementation is not needed. - end - end -end - -def File.binread(fname) - open(fname, 'rb') {|f| - return f.read - } -end - -# for corrupted Windows' stat(2) -def File.dir?(path) - File.directory?((path[-1,1] == '/') ? path : path + '/') -end - - -class ConfigTable - - include Enumerable - - def initialize(rbconfig) - @rbconfig = rbconfig - @items = [] - @table = {} - # options - @install_prefix = nil - @config_opt = nil - @verbose = true - @no_harm = false - end - - attr_accessor :install_prefix - attr_accessor :config_opt - - attr_writer :verbose - - def verbose? - @verbose - end - - attr_writer :no_harm - - def no_harm? - @no_harm - end - - def [](key) - lookup(key).resolve(self) - end - - def []=(key, val) - lookup(key).set val - end - - def names - @items.map {|i| i.name } - end - - def each(&block) - @items.each(&block) - end - - def key?(name) - @table.key?(name) - end - - def lookup(name) - @table[name] or setup_rb_error "no such config item: #{name}" - end - - def add(item) - @items.push item - @table[item.name] = item - end - - def remove(name) - item = lookup(name) - @items.delete_if {|i| i.name == name } - @table.delete_if {|name, i| i.name == name } - item - end - - def load_script(path, inst = nil) - if File.file?(path) - MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path - end - end - - def savefile - '.config' - end - - def load_savefile - begin - File.foreach(savefile()) do |line| - k, v = *line.split(/=/, 2) - self[k] = v.strip - end - rescue Errno::ENOENT - setup_rb_error $!.message + "\n#{File.basename($0)} config first" - end - end - - def save - @items.each {|i| i.value } - File.open(savefile(), 'w') {|f| - @items.each do |i| - f.printf "%s=%s\n", i.name, i.value if i.value? and i.value - end - } - end - - def load_standard_entries - standard_entries(@rbconfig).each do |ent| - add ent - end - end - - def standard_entries(rbconfig) - c = rbconfig - - rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) - - major = c['MAJOR'].to_i - minor = c['MINOR'].to_i - teeny = c['TEENY'].to_i - version = "#{major}.#{minor}" - - # ruby ver. >= 1.4.4? - newpath_p = ((major >= 2) or - ((major == 1) and - ((minor >= 5) or - ((minor == 4) and (teeny >= 4))))) - - if c['rubylibdir'] - # V > 1.6.3 - libruby = "#{c['prefix']}/lib/ruby" - librubyver = c['rubylibdir'] - librubyverarch = c['archdir'] - siteruby = c['sitedir'] - siterubyver = c['sitelibdir'] - siterubyverarch = c['sitearchdir'] - elsif newpath_p - # 1.4.4 <= V <= 1.6.3 - libruby = "#{c['prefix']}/lib/ruby" - librubyver = "#{c['prefix']}/lib/ruby/#{version}" - librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" - siteruby = c['sitedir'] - siterubyver = "$siteruby/#{version}" - siterubyverarch = "$siterubyver/#{c['arch']}" - else - # V < 1.4.4 - libruby = "#{c['prefix']}/lib/ruby" - librubyver = "#{c['prefix']}/lib/ruby/#{version}" - librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" - siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" - siterubyver = siteruby - siterubyverarch = "$siterubyver/#{c['arch']}" - end - parameterize = lambda {|path| - path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') - } - - if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } - makeprog = arg.sub(/'/, '').split(/=/, 2)[1] - else - makeprog = 'make' - end - - [ - ExecItem.new('installdirs', 'std/site/home', - 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ - {|val, table| - case val - when 'std' - table['rbdir'] = '$librubyver' - table['sodir'] = '$librubyverarch' - when 'site' - table['rbdir'] = '$siterubyver' - table['sodir'] = '$siterubyverarch' - when 'home' - setup_rb_error '$HOME was not set' unless ENV['HOME'] - table['prefix'] = ENV['HOME'] - table['rbdir'] = '$libdir/ruby' - table['sodir'] = '$libdir/ruby' - end - }, - PathItem.new('prefix', 'path', c['prefix'], - 'path prefix of target environment'), - PathItem.new('bindir', 'path', parameterize.call(c['bindir']), - 'the directory for commands'), - PathItem.new('libdir', 'path', parameterize.call(c['libdir']), - 'the directory for libraries'), - PathItem.new('datadir', 'path', parameterize.call(c['datadir']), - 'the directory for shared data'), - PathItem.new('mandir', 'path', parameterize.call(c['mandir']), - 'the directory for man pages'), - PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), - 'the directory for system configuration files'), - PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), - 'the directory for local state data'), - PathItem.new('libruby', 'path', libruby, - 'the directory for ruby libraries'), - PathItem.new('librubyver', 'path', librubyver, - 'the directory for standard ruby libraries'), - PathItem.new('librubyverarch', 'path', librubyverarch, - 'the directory for standard ruby extensions'), - PathItem.new('siteruby', 'path', siteruby, - 'the directory for version-independent aux ruby libraries'), - PathItem.new('siterubyver', 'path', siterubyver, - 'the directory for aux ruby libraries'), - PathItem.new('siterubyverarch', 'path', siterubyverarch, - 'the directory for aux ruby binaries'), - PathItem.new('rbdir', 'path', '$siterubyver', - 'the directory for ruby scripts'), - PathItem.new('sodir', 'path', '$siterubyverarch', - 'the directory for ruby extentions'), - PathItem.new('rubypath', 'path', rubypath, - 'the path to set to #! line'), - ProgramItem.new('rubyprog', 'name', rubypath, - 'the ruby program using for installation'), - ProgramItem.new('makeprog', 'name', makeprog, - 'the make program to compile ruby extentions'), - SelectItem.new('shebang', 'all/ruby/never', 'ruby', - 'shebang line (#!) editing mode'), - BoolItem.new('without-ext', 'yes/no', 'no', - 'does not compile/install ruby extentions') - ] - end - private :standard_entries - - def load_multipackage_entries - multipackage_entries().each do |ent| - add ent - end - end - - def multipackage_entries - [ - PackageSelectionItem.new('with', 'name,name...', '', 'ALL', - 'package names that you want to install'), - PackageSelectionItem.new('without', 'name,name...', '', 'NONE', - 'package names that you do not want to install') - ] - end - private :multipackage_entries - - ALIASES = { - 'std-ruby' => 'librubyver', - 'stdruby' => 'librubyver', - 'rubylibdir' => 'librubyver', - 'archdir' => 'librubyverarch', - 'site-ruby-common' => 'siteruby', # For backward compatibility - 'site-ruby' => 'siterubyver', # For backward compatibility - 'bin-dir' => 'bindir', - 'bin-dir' => 'bindir', - 'rb-dir' => 'rbdir', - 'so-dir' => 'sodir', - 'data-dir' => 'datadir', - 'ruby-path' => 'rubypath', - 'ruby-prog' => 'rubyprog', - 'ruby' => 'rubyprog', - 'make-prog' => 'makeprog', - 'make' => 'makeprog' - } - - def fixup - ALIASES.each do |ali, name| - @table[ali] = @table[name] - end - @items.freeze - @table.freeze - @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ - end - - def parse_opt(opt) - m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" - m.to_a[1,2] - end - - def dllext - @rbconfig['DLEXT'] - end - - def value_config?(name) - lookup(name).value? - end - - class Item - def initialize(name, template, default, desc) - @name = name.freeze - @template = template - @value = default - @default = default - @description = desc - end - - attr_reader :name - attr_reader :description - - attr_accessor :default - alias help_default default - - def help_opt - "--#{@name}=#{@template}" - end - - def value? - true - end - - def value - @value - end - - def resolve(table) - @value.gsub(%r<\$([^/]+)>) { table[$1] } - end - - def set(val) - @value = check(val) - end - - private - - def check(val) - setup_rb_error "config: --#{name} requires argument" unless val - val - end - end - - class BoolItem < Item - def config_type - 'bool' - end - - def help_opt - "--#{@name}" - end - - private - - def check(val) - return 'yes' unless val - case val - when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' - when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' - else - setup_rb_error "config: --#{@name} accepts only yes/no for argument" - end - end - end - - class PathItem < Item - def config_type - 'path' - end - - private - - def check(path) - setup_rb_error "config: --#{@name} requires argument" unless path - path[0,1] == '$' ? path : File.expand_path(path) - end - end - - class ProgramItem < Item - def config_type - 'program' - end - end - - class SelectItem < Item - def initialize(name, selection, default, desc) - super - @ok = selection.split('/') - end - - def config_type - 'select' - end - - private - - def check(val) - unless @ok.include?(val.strip) - setup_rb_error "config: use --#{@name}=#{@template} (#{val})" - end - val.strip - end - end - - class ExecItem < Item - def initialize(name, selection, desc, &block) - super name, selection, nil, desc - @ok = selection.split('/') - @action = block - end - - def config_type - 'exec' - end - - def value? - false - end - - def resolve(table) - setup_rb_error "$#{name()} wrongly used as option value" - end - - undef set - - def evaluate(val, table) - v = val.strip.downcase - unless @ok.include?(v) - setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" - end - @action.call v, table - end - end - - class PackageSelectionItem < Item - def initialize(name, template, default, help_default, desc) - super name, template, default, desc - @help_default = help_default - end - - attr_reader :help_default - - def config_type - 'package' - end - - private - - def check(val) - unless File.dir?("packages/#{val}") - setup_rb_error "config: no such package: #{val}" - end - val - end - end - - class MetaConfigEnvironment - def initialize(config, installer) - @config = config - @installer = installer - end - - def config_names - @config.names - end - - def config?(name) - @config.key?(name) - end - - def bool_config?(name) - @config.lookup(name).config_type == 'bool' - end - - def path_config?(name) - @config.lookup(name).config_type == 'path' - end - - def value_config?(name) - @config.lookup(name).config_type != 'exec' - end - - def add_config(item) - @config.add item - end - - def add_bool_config(name, default, desc) - @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) - end - - def add_path_config(name, default, desc) - @config.add PathItem.new(name, 'path', default, desc) - end - - def set_config_default(name, default) - @config.lookup(name).default = default - end - - def remove_config(name) - @config.remove(name) - end - - # For only multipackage - def packages - raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer - @installer.packages - end - - # For only multipackage - def declare_packages(list) - raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer - @installer.packages = list - end - end - -end # class ConfigTable - - -# This module requires: #verbose?, #no_harm? -module FileOperations - - def mkdir_p(dirname, prefix = nil) - dirname = prefix + File.expand_path(dirname) if prefix - $stderr.puts "mkdir -p #{dirname}" if verbose? - return if no_harm? - - # Does not check '/', it's too abnormal. - dirs = File.expand_path(dirname).split(%r<(?=/)>) - if /\A[a-z]:\z/i =~ dirs[0] - disk = dirs.shift - dirs[0] = disk + dirs[0] - end - dirs.each_index do |idx| - path = dirs[0..idx].join('') - Dir.mkdir path unless File.dir?(path) - end - end - - def rm_f(path) - $stderr.puts "rm -f #{path}" if verbose? - return if no_harm? - force_remove_file path - end - - def rm_rf(path) - $stderr.puts "rm -rf #{path}" if verbose? - return if no_harm? - remove_tree path - end - - def remove_tree(path) - if File.symlink?(path) - remove_file path - elsif File.dir?(path) - remove_tree0 path - else - force_remove_file path - end - end - - def remove_tree0(path) - Dir.foreach(path) do |ent| - next if ent == '.' - next if ent == '..' - entpath = "#{path}/#{ent}" - if File.symlink?(entpath) - remove_file entpath - elsif File.dir?(entpath) - remove_tree0 entpath - else - force_remove_file entpath - end - end - begin - Dir.rmdir path - rescue Errno::ENOTEMPTY - # directory may not be empty - end - end - - def move_file(src, dest) - force_remove_file dest - begin - File.rename src, dest - rescue - File.open(dest, 'wb') {|f| - f.write File.binread(src) - } - File.chmod File.stat(src).mode, dest - File.unlink src - end - end - - def force_remove_file(path) - begin - remove_file path - rescue - end - end - - def remove_file(path) - File.chmod 0777, path - File.unlink path - end - - def install(from, dest, mode, prefix = nil) - $stderr.puts "install #{from} #{dest}" if verbose? - return if no_harm? - - realdest = prefix ? prefix + File.expand_path(dest) : dest - realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) - str = File.binread(from) - if diff?(str, realdest) - verbose_off { - rm_f realdest if File.exist?(realdest) - } - File.open(realdest, 'wb') {|f| - f.write str - } - File.chmod mode, realdest - - File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| - if prefix - f.puts realdest.sub(prefix, '') - else - f.puts realdest - end - } - end - end - - def diff?(new_content, path) - return true unless File.exist?(path) - new_content != File.binread(path) - end - - def command(*args) - $stderr.puts args.join(' ') if verbose? - system(*args) or raise RuntimeError, - "system(#{args.map{|a| a.inspect }.join(' ')}) failed" - end - - def ruby(*args) - command config('rubyprog'), *args - end - - def make(task = nil) - command(*[config('makeprog'), task].compact) - end - - def extdir?(dir) - File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") - end - - def files_of(dir) - Dir.open(dir) {|d| - return d.select {|ent| File.file?("#{dir}/#{ent}") } - } - end - - DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) - - def directories_of(dir) - Dir.open(dir) {|d| - return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT - } - end - -end - - -# This module requires: #srcdir_root, #objdir_root, #relpath -module HookScriptAPI - - def get_config(key) - @config[key] - end - - alias config get_config - - # obsolete: use metaconfig to change configuration - def set_config(key, val) - @config[key] = val - end - - # - # srcdir/objdir (works only in the package directory) - # - - def curr_srcdir - "#{srcdir_root()}/#{relpath()}" - end - - def curr_objdir - "#{objdir_root()}/#{relpath()}" - end - - def srcfile(path) - "#{curr_srcdir()}/#{path}" - end - - def srcexist?(path) - File.exist?(srcfile(path)) - end - - def srcdirectory?(path) - File.dir?(srcfile(path)) - end - - def srcfile?(path) - File.file?(srcfile(path)) - end - - def srcentries(path = '.') - Dir.open("#{curr_srcdir()}/#{path}") {|d| - return d.to_a - %w(. ..) - } - end - - def srcfiles(path = '.') - srcentries(path).select {|fname| - File.file?(File.join(curr_srcdir(), path, fname)) - } - end - - def srcdirectories(path = '.') - srcentries(path).select {|fname| - File.dir?(File.join(curr_srcdir(), path, fname)) - } - end - -end - - -class ToplevelInstaller - - Version = '3.4.1' - Copyright = 'Copyright (c) 2000-2005 Minero Aoki' - - TASKS = [ - [ 'all', 'do config, setup, then install' ], - [ 'config', 'saves your configurations' ], - [ 'show', 'shows current configuration' ], - [ 'setup', 'compiles ruby extentions and others' ], - [ 'install', 'installs files' ], - [ 'test', 'run all tests in test/' ], - [ 'clean', "does `make clean' for each extention" ], - [ 'distclean',"does `make distclean' for each extention" ] - ] - - def ToplevelInstaller.invoke - config = ConfigTable.new(load_rbconfig()) - config.load_standard_entries - config.load_multipackage_entries if multipackage? - config.fixup - klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) - klass.new(File.dirname($0), config).invoke - end - - def ToplevelInstaller.multipackage? - File.dir?(File.dirname($0) + '/packages') - end - - def ToplevelInstaller.load_rbconfig - if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } - ARGV.delete(arg) - load File.expand_path(arg.split(/=/, 2)[1]) - $".push 'rbconfig.rb' - else - require 'rbconfig' - end - ::Config::CONFIG - end - - def initialize(ardir_root, config) - @ardir = File.expand_path(ardir_root) - @config = config - # cache - @valid_task_re = nil - end - - def config(key) - @config[key] - end - - def inspect - "#<#{self.class} #{__id__()}>" - end - - def invoke - run_metaconfigs - case task = parsearg_global() - when nil, 'all' - parsearg_config - init_installers - exec_config - exec_setup - exec_install - else - case task - when 'config', 'test' - ; - when 'clean', 'distclean' - @config.load_savefile if File.exist?(@config.savefile) - else - @config.load_savefile - end - __send__ "parsearg_#{task}" - init_installers - __send__ "exec_#{task}" - end - end - - def run_metaconfigs - @config.load_script "#{@ardir}/metaconfig" - end - - def init_installers - @installer = Installer.new(@config, @ardir, File.expand_path('.')) - end - - # - # Hook Script API bases - # - - def srcdir_root - @ardir - end - - def objdir_root - '.' - end - - def relpath - '.' - end - - # - # Option Parsing - # - - def parsearg_global - while arg = ARGV.shift - case arg - when /\A\w+\z/ - setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) - return arg - when '-q', '--quiet' - @config.verbose = false - when '--verbose' - @config.verbose = true - when '--help' - print_usage $stdout - exit 0 - when '--version' - puts "#{File.basename($0)} version #{Version}" - exit 0 - when '--copyright' - puts Copyright - exit 0 - else - setup_rb_error "unknown global option '#{arg}'" - end - end - nil - end - - def valid_task?(t) - valid_task_re() =~ t - end - - def valid_task_re - @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ - end - - def parsearg_no_options - unless ARGV.empty? - task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) - setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" - end - end - - alias parsearg_show parsearg_no_options - alias parsearg_setup parsearg_no_options - alias parsearg_test parsearg_no_options - alias parsearg_clean parsearg_no_options - alias parsearg_distclean parsearg_no_options - - def parsearg_config - evalopt = [] - set = [] - @config.config_opt = [] - while i = ARGV.shift - if /\A--?\z/ =~ i - @config.config_opt = ARGV.dup - break - end - name, value = *@config.parse_opt(i) - if @config.value_config?(name) - @config[name] = value - else - evalopt.push [name, value] - end - set.push name - end - evalopt.each do |name, value| - @config.lookup(name).evaluate value, @config - end - # Check if configuration is valid - set.each do |n| - @config[n] if @config.value_config?(n) - end - end - - def parsearg_install - @config.no_harm = false - @config.install_prefix = '' - while a = ARGV.shift - case a - when '--no-harm' - @config.no_harm = true - when /\A--prefix=/ - path = a.split(/=/, 2)[1] - path = File.expand_path(path) unless path[0,1] == '/' - @config.install_prefix = path - else - setup_rb_error "install: unknown option #{a}" - end - end - end - - def print_usage(out) - out.puts 'Typical Installation Procedure:' - out.puts " $ ruby #{File.basename $0} config" - out.puts " $ ruby #{File.basename $0} setup" - out.puts " # ruby #{File.basename $0} install (may require root privilege)" - out.puts - out.puts 'Detailed Usage:' - out.puts " ruby #{File.basename $0} " - out.puts " ruby #{File.basename $0} [] []" - - fmt = " %-24s %s\n" - out.puts - out.puts 'Global options:' - out.printf fmt, '-q,--quiet', 'suppress message outputs' - out.printf fmt, ' --verbose', 'output messages verbosely' - out.printf fmt, ' --help', 'print this message' - out.printf fmt, ' --version', 'print version and quit' - out.printf fmt, ' --copyright', 'print copyright and quit' - out.puts - out.puts 'Tasks:' - TASKS.each do |name, desc| - out.printf fmt, name, desc - end - - fmt = " %-24s %s [%s]\n" - out.puts - out.puts 'Options for CONFIG or ALL:' - @config.each do |item| - out.printf fmt, item.help_opt, item.description, item.help_default - end - out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" - out.puts - out.puts 'Options for INSTALL:' - out.printf fmt, '--no-harm', 'only display what to do if given', 'off' - out.printf fmt, '--prefix=path', 'install path prefix', '' - out.puts - end - - # - # Task Handlers - # - - def exec_config - @installer.exec_config - @config.save # must be final - end - - def exec_setup - @installer.exec_setup - end - - def exec_install - @installer.exec_install - end - - def exec_test - @installer.exec_test - end - - def exec_show - @config.each do |i| - printf "%-20s %s\n", i.name, i.value if i.value? - end - end - - def exec_clean - @installer.exec_clean - end - - def exec_distclean - @installer.exec_distclean - end - -end # class ToplevelInstaller - - -class ToplevelInstallerMulti < ToplevelInstaller - - include FileOperations - - def initialize(ardir_root, config) - super - @packages = directories_of("#{@ardir}/packages") - raise 'no package exists' if @packages.empty? - @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) - end - - def run_metaconfigs - @config.load_script "#{@ardir}/metaconfig", self - @packages.each do |name| - @config.load_script "#{@ardir}/packages/#{name}/metaconfig" - end - end - - attr_reader :packages - - def packages=(list) - raise 'package list is empty' if list.empty? - list.each do |name| - raise "directory packages/#{name} does not exist"\ - unless File.dir?("#{@ardir}/packages/#{name}") - end - @packages = list - end - - def init_installers - @installers = {} - @packages.each do |pack| - @installers[pack] = Installer.new(@config, - "#{@ardir}/packages/#{pack}", - "packages/#{pack}") - end - with = extract_selection(config('with')) - without = extract_selection(config('without')) - @selected = @installers.keys.select {|name| - (with.empty? or with.include?(name)) \ - and not without.include?(name) - } - end - - def extract_selection(list) - a = list.split(/,/) - a.each do |name| - setup_rb_error "no such package: #{name}" unless @installers.key?(name) - end - a - end - - def print_usage(f) - super - f.puts 'Inluded packages:' - f.puts ' ' + @packages.sort.join(' ') - f.puts - end - - # - # Task Handlers - # - - def exec_config - run_hook 'pre-config' - each_selected_installers {|inst| inst.exec_config } - run_hook 'post-config' - @config.save # must be final - end - - def exec_setup - run_hook 'pre-setup' - each_selected_installers {|inst| inst.exec_setup } - run_hook 'post-setup' - end - - def exec_install - run_hook 'pre-install' - each_selected_installers {|inst| inst.exec_install } - run_hook 'post-install' - end - - def exec_test - run_hook 'pre-test' - each_selected_installers {|inst| inst.exec_test } - run_hook 'post-test' - end - - def exec_clean - rm_f @config.savefile - run_hook 'pre-clean' - each_selected_installers {|inst| inst.exec_clean } - run_hook 'post-clean' - end - - def exec_distclean - rm_f @config.savefile - run_hook 'pre-distclean' - each_selected_installers {|inst| inst.exec_distclean } - run_hook 'post-distclean' - end - - # - # lib - # - - def each_selected_installers - Dir.mkdir 'packages' unless File.dir?('packages') - @selected.each do |pack| - $stderr.puts "Processing the package `#{pack}' ..." if verbose? - Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") - Dir.chdir "packages/#{pack}" - yield @installers[pack] - Dir.chdir '../..' - end - end - - def run_hook(id) - @root_installer.run_hook id - end - - # module FileOperations requires this - def verbose? - @config.verbose? - end - - # module FileOperations requires this - def no_harm? - @config.no_harm? - end - -end # class ToplevelInstallerMulti - - -class Installer - - FILETYPES = %w( bin lib ext data conf man ) - - include FileOperations - include HookScriptAPI - - def initialize(config, srcroot, objroot) - @config = config - @srcdir = File.expand_path(srcroot) - @objdir = File.expand_path(objroot) - @currdir = '.' - end - - def inspect - "#<#{self.class} #{File.basename(@srcdir)}>" - end - - def noop(rel) - end - - # - # Hook Script API base methods - # - - def srcdir_root - @srcdir - end - - def objdir_root - @objdir - end - - def relpath - @currdir - end - - # - # Config Access - # - - # module FileOperations requires this - def verbose? - @config.verbose? - end - - # module FileOperations requires this - def no_harm? - @config.no_harm? - end - - def verbose_off - begin - save, @config.verbose = @config.verbose?, false - yield - ensure - @config.verbose = save - end - end - - # - # TASK config - # - - def exec_config - exec_task_traverse 'config' - end - - alias config_dir_bin noop - alias config_dir_lib noop - - def config_dir_ext(rel) - extconf if extdir?(curr_srcdir()) - end - - alias config_dir_data noop - alias config_dir_conf noop - alias config_dir_man noop - - def extconf - ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt - end - - # - # TASK setup - # - - def exec_setup - exec_task_traverse 'setup' - end - - def setup_dir_bin(rel) - files_of(curr_srcdir()).each do |fname| - update_shebang_line "#{curr_srcdir()}/#{fname}" - end - end - - alias setup_dir_lib noop - - def setup_dir_ext(rel) - make if extdir?(curr_srcdir()) - end - - alias setup_dir_data noop - alias setup_dir_conf noop - alias setup_dir_man noop - - def update_shebang_line(path) - return if no_harm? - return if config('shebang') == 'never' - old = Shebang.load(path) - if old - $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 - new = new_shebang(old) - return if new.to_s == old.to_s - else - return unless config('shebang') == 'all' - new = Shebang.new(config('rubypath')) - end - $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? - open_atomic_writer(path) {|output| - File.open(path, 'rb') {|f| - f.gets if old # discard - output.puts new.to_s - output.print f.read - } - } - end - - def new_shebang(old) - if /\Aruby/ =~ File.basename(old.cmd) - Shebang.new(config('rubypath'), old.args) - elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' - Shebang.new(config('rubypath'), old.args[1..-1]) - else - return old unless config('shebang') == 'all' - Shebang.new(config('rubypath')) - end - end - - def open_atomic_writer(path, &block) - tmpfile = File.basename(path) + '.tmp' - begin - File.open(tmpfile, 'wb', &block) - File.rename tmpfile, File.basename(path) - ensure - File.unlink tmpfile if File.exist?(tmpfile) - end - end - - class Shebang - def Shebang.load(path) - line = nil - File.open(path) {|f| - line = f.gets - } - return nil unless /\A#!/ =~ line - parse(line) - end - - def Shebang.parse(line) - cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') - new(cmd, args) - end - - def initialize(cmd, args = []) - @cmd = cmd - @args = args - end - - attr_reader :cmd - attr_reader :args - - def to_s - "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") - end - end - - # - # TASK install - # - - def exec_install - rm_f 'InstalledFiles' - exec_task_traverse 'install' - end - - def install_dir_bin(rel) - install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 - end - - def install_dir_lib(rel) - install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 - end - - def install_dir_ext(rel) - return unless extdir?(curr_srcdir()) - install_files rubyextentions('.'), - "#{config('sodir')}/#{File.dirname(rel)}", - 0555 - end - - def install_dir_data(rel) - install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 - end - - def install_dir_conf(rel) - # FIXME: should not remove current config files - # (rename previous file to .old/.org) - install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 - end - - def install_dir_man(rel) - install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 - end - - def install_files(list, dest, mode) - mkdir_p dest, @config.install_prefix - list.each do |fname| - install fname, dest, mode, @config.install_prefix - end - end - - def libfiles - glob_reject(%w(*.y *.output), targetfiles()) - end - - def rubyextentions(dir) - ents = glob_select("*.#{@config.dllext}", targetfiles()) - if ents.empty? - setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" - end - ents - end - - def targetfiles - mapdir(existfiles() - hookfiles()) - end - - def mapdir(ents) - ents.map {|ent| - if File.exist?(ent) - then ent # objdir - else "#{curr_srcdir()}/#{ent}" # srcdir - end - } - end - - # picked up many entries from cvs-1.11.1/src/ignore.c - JUNK_FILES = %w( - core RCSLOG tags TAGS .make.state - .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb - *~ *.old *.bak *.BAK *.orig *.rej _$* *$ - - *.org *.in .* - ) - - def existfiles - glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) - end - - def hookfiles - %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| - %w( config setup install clean ).map {|t| sprintf(fmt, t) } - }.flatten - end - - def glob_select(pat, ents) - re = globs2re([pat]) - ents.select {|ent| re =~ ent } - end - - def glob_reject(pats, ents) - re = globs2re(pats) - ents.reject {|ent| re =~ ent } - end - - GLOB2REGEX = { - '.' => '\.', - '$' => '\$', - '#' => '\#', - '*' => '.*' - } - - def globs2re(pats) - /\A(?:#{ - pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') - })\z/ - end - - # - # TASK test - # - - TESTDIR = 'test' - - def exec_test - unless File.directory?('test') - $stderr.puts 'no test in this package' if verbose? - return - end - $stderr.puts 'Running tests...' if verbose? - begin - require 'test/unit' - rescue LoadError - setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' - end - runner = Test::Unit::AutoRunner.new(true) - runner.to_run << TESTDIR - runner.run - end - - # - # TASK clean - # - - def exec_clean - exec_task_traverse 'clean' - rm_f @config.savefile - rm_f 'InstalledFiles' - end - - alias clean_dir_bin noop - alias clean_dir_lib noop - alias clean_dir_data noop - alias clean_dir_conf noop - alias clean_dir_man noop - - def clean_dir_ext(rel) - return unless extdir?(curr_srcdir()) - make 'clean' if File.file?('Makefile') - end - - # - # TASK distclean - # - - def exec_distclean - exec_task_traverse 'distclean' - rm_f @config.savefile - rm_f 'InstalledFiles' - end - - alias distclean_dir_bin noop - alias distclean_dir_lib noop - - def distclean_dir_ext(rel) - return unless extdir?(curr_srcdir()) - make 'distclean' if File.file?('Makefile') - end - - alias distclean_dir_data noop - alias distclean_dir_conf noop - alias distclean_dir_man noop - - # - # Traversing - # - - def exec_task_traverse(task) - run_hook "pre-#{task}" - FILETYPES.each do |type| - if type == 'ext' and config('without-ext') == 'yes' - $stderr.puts 'skipping ext/* by user option' if verbose? - next - end - traverse task, type, "#{task}_dir_#{type}" - end - run_hook "post-#{task}" - end - - def traverse(task, rel, mid) - dive_into(rel) { - run_hook "pre-#{task}" - __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') - directories_of(curr_srcdir()).each do |d| - traverse task, "#{rel}/#{d}", mid - end - run_hook "post-#{task}" - } - end - - def dive_into(rel) - return unless File.dir?("#{@srcdir}/#{rel}") - - dir = File.basename(rel) - Dir.mkdir dir unless File.dir?(dir) - prevdir = Dir.pwd - Dir.chdir dir - $stderr.puts '---> ' + rel if verbose? - @currdir = rel - yield - Dir.chdir prevdir - $stderr.puts '<--- ' + rel if verbose? - @currdir = File.dirname(rel) - end - - def run_hook(id) - path = [ "#{curr_srcdir()}/#{id}", - "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } - return unless path - begin - instance_eval File.read(path), path, 1 - rescue - raise if $DEBUG - setup_rb_error "hook #{path} failed:\n" + $!.message - end - end - -end # class Installer - - -class SetupError < StandardError; end - -def setup_rb_error(msg) - raise SetupError, msg -end - -if $0 == __FILE__ - begin - ToplevelInstaller.invoke - rescue SetupError - raise if $DEBUG - $stderr.puts $!.message - $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." - exit 1 - end -end diff --git a/statsample.gemspec b/statsample.gemspec index 36d3fed..3561cfa 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -4,32 +4,32 @@ require 'statsample/version' require 'date' DESCRIPTION = < Date: Thu, 26 Mar 2015 00:36:59 -0300 Subject: [PATCH 012/119] Update History.txt --- History.txt | 97 ++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/History.txt b/History.txt index a438896..6352f98 100644 --- a/History.txt +++ b/History.txt @@ -1,9 +1,14 @@ +=== 1.4.1 / 2015-03-26 + * Removed Hoe gem in order to use `statsample.gemspec`. + * Improved readability of some files by using rubocop. + * Removed a bad check in `cronbach_alpha` (#10). + === 1.4.0 / 2014-10-11 * Replaced README.txt for README.md * Replace File.exists? for File.exist? + New Dataset.join to join two dataset based on some fields * Deleted MLE based regression (Probit and logistic). Now all GML methods are on statsample-glm - + === 1.3.1 / 2014-06-26 * Example referred to a SimpleRegression class which doesn't exist. Updated to working example. @@ -23,7 +28,7 @@ * open svg on mac osx === 1.2.0 / 2011-12-15 - + * Added support for time series (TimeSeries object): MA, EMA, MACD, acf, lag and delta. [Rob Britton] * Changed summary attribute to properly display 'b' value for simple linear regression [hstove] * Merge pull request #6 from hstove/patch-1Changed summary attribute to properly display 'b' value for simple linear regression [Claudio Bustos] @@ -34,9 +39,9 @@ * New Statsample::Anova::Contrast * Jacknife and bootstrap for Vector. Thanks to John Firebaugh for the idea * Improved Statsample::Analysis API -* Updated CSV.read. Third argument is a Hash with options to CSV class +* Updated CSV.read. Third argument is a Hash with options to CSV class * Added restriction on Statsample::Excel.read -* Updated spanish po +* Updated spanish po * Better summary for Vector * Improving summary of t related test (confidence interval and estimate output) * Replaced c for vector on Statsample::Analysis examples @@ -51,7 +56,7 @@ === 1.0.0 / 2011-01-27 * Added Statsample::Analysis, a beautiful DSL to perform fast statistical analysis using statsample. See directory /examples -* Created benchmarks directory +* Created benchmarks directory * Removed Distribution module from statsample and moved to a gem. Changes on code to reflect new API * Optimized simple regression. Better library detection * New 'should_with_gsl' to test methods with gsl. Refactored Factor::MAP @@ -62,17 +67,17 @@ * Modified examples using Statsample::Analysis * Simplified eigen calculations * Updated some examples. Added correlation matrix speed suite -* Correlation matrix optimized. Better specs -* Optimized correlation matrix. Use gsl matrix algebra or pairwise correlations depending on empiric calculated equations. See benchmarks/correlation_matrix.rb to see implementation of calculation +* Correlation matrix optimized. Better specs +* Optimized correlation matrix. Use gsl matrix algebra or pairwise correlations depending on empiric calculated equations. See benchmarks/correlation_matrix.rb to see implementation of calculation * Moved tests fixtures from data to test/fixtures * Fixed some errors on tests -* Bug fix: constant_se on binomial regression have an error -* All test should work on ruby 1.9.3 +* Bug fix: constant_se on binomial regression have an error +* All test should work on ruby 1.9.3 * New Vector.[] and Vector.new_scale -* Detect linearly dependent predictors on OLS. +* Detect linearly dependent predictors on OLS. === 0.18.0 / 2011-01-07 -* New Statsample.load_excel +* New Statsample.load_excel * New Statsample.load_csv * Statsample::Dataset#[] accepts an array of fields and uses clone * New Dataset#correlation_matrix and Statsample::Dataset#covariance_matrix @@ -83,19 +88,19 @@ * Improved summary for PCA using covariance matrix * New attribute :label_angle for Statsample::Graph::Boxplot * Fixed Scatterplots scaling problems -* New attributes for Scatterplots: groups, minimum_x, minimum_y, maximum_x, +* New attributes for Scatterplots: groups, minimum_x, minimum_y, maximum_x, * New Statsample::Multiset#union allows to create a new dataset based on a m * New Statsample::Multiset#each to traverse through datasets * Bug fix: Vector#standarized and Vector#percentile crash on nil data * Bug fix: Vector#mean and Vector#sd crash on data without valid values * Modified methods names on Statsample::Factor::PCA : feature_vector to feature_matrix, data_transformation to principal_components * Added Statsample::Vector.vector_centered -* Factor::MAP.with_dataset() implemented -* Bug fix: Factor::MAP with correlation matrix with non-real eigenvalues crashes * Added documentation for Graph::Histogram +* Factor::MAP.with_dataset() implemented +* Bug fix: Factor::MAP with correlation matrix with non-real eigenvalues crashes * Added documentation for Graph::Histogram * Added MPA to Reliability::MultiScaleAnalysis -* Added custom names for returned vectors and datasets -* Updated spanish traslation -* Graph::Histogram updated. Custom x and y max and min, optional normal distribution drawing +* Added custom names for returned vectors and datasets +* Updated spanish traslation +* Graph::Histogram updated. Custom x and y max and min, optional normal distribution drawing * Updated Histogram class, with several new methods compatibles with GSL::Histogram === 0.17.0 / 2010-12-09 @@ -106,18 +111,18 @@ === 0.16.0 / 2010-11-13 * Works on ruby 1.9.2 and HEAD. Updated Rakefile and manifest -* Removed all graph based on Svg::Graph. +* Removed all graph based on Svg::Graph. * First operative version of Graph with Rubyvis -* Corrected bug on Distribution::Normal.cdf. +* Corrected bug on Distribution::Normal.cdf. * Added reference on references.txt * Ruby-based random gaussian distribution generator when gsl not available * Added population average deviation [Al Chou] === 0.15.1 / 2010-10-20 -* Statsample::Excel and Statsample::PlainText add name to vectors equal to field name +* Statsample::Excel and Statsample::PlainText add name to vectors equal to field name * Statsample::Dataset.delete_vector accept multiple fields. -* Statsample::Dataset.dup_only_valid allows duplication of specific fields -* ScaleAnalysis doesn't crash on one-item scales +* Statsample::Dataset.dup_only_valid allows duplication of specific fields +* ScaleAnalysis doesn't crash on one-item scales * Updated references === 0.15.0 / 2010-09-07 @@ -126,14 +131,14 @@ * Added Spearman-Brown prophecy on Reliability module * Distribution::F uses Gsl when available * Added mean r.p.b. and item sd on Scale Analysis -* Corrected bug on Vector.ary_method and example of Anova Two Way using vector. +* Corrected bug on Vector.ary_method and example of Anova Two Way using vector. === 0.14.1 / 2010-08-18 -* Added extra information on $DEBUG=true. -* Changed ParallelAnalysis: with_random_data parameters, bootstrap_method options are data and random, resolve bug related to number of factors to preserve, resolved bug related to original eigenvalues, can support failed bootstrap of data for Tetrachoric correlation. -* Optimized eigenpairs on Matrix when GSL is available. +* Added extra information on $DEBUG=true. +* Changed ParallelAnalysis: with_random_data parameters, bootstrap_method options are data and random, resolve bug related to number of factors to preserve, resolved bug related to original eigenvalues, can support failed bootstrap of data for Tetrachoric correlation. +* Optimized eigenpairs on Matrix when GSL is available. * Added test for parallel analysis using data bootstraping * Updated .pot and Manifest.txt * Added test for kmo(global and univariate), bartlett and anti-image. Kmo and Bartlett have test based on Dziuban and Shirkey with correct results @@ -142,16 +147,16 @@ * Added reference for Statsample::Factor::MAP === 0.14.0 / 2010-08-16 -* Added Statsample::Factor::MAP, to execute Velicer's (1976) MAP to determine the number of factors to retain on EFA +* Added Statsample::Factor::MAP, to execute Velicer's (1976) MAP to determine the number of factors to retain on EFA * Bug fix on test suite on Ruby 1.8.7 * Horn's Parallel Analysis operational and tested for pure random data -* Fixed bug on Excel writer on Ruby1.9 (frozen string on header raises an error). +* Fixed bug on Excel writer on Ruby1.9 (frozen string on header raises an error). * Extra information on Factorial Analysis on summaries -* Fixed bug on Factor::Rotation when used ::Matrix without field method. +* Fixed bug on Factor::Rotation when used ::Matrix without field method. * Added Vector#vector_percentil method -* Summaries for PCA, Rotation, MultiScale and ScaleAnalysis created or improved. +* Summaries for PCA, Rotation, MultiScale and ScaleAnalysis created or improved. * Factor::PCA could have rotation and parallel analysis on summary. -* Cronbach's alpha from covariance matrix raise an error on size<2 +* Cronbach's alpha from covariance matrix raise an error on size<2 * MultiScaleAnalysis could have Parallel Analysis on summary. * Added Chi Square test * Added new information on README.txt @@ -168,7 +173,7 @@ * Polychoric and Tetrachoric moved to gem statsample-bivariate-extension * All classes left with summary method include Summarizable now. Every method which return localizable string is now parsed with _() -* Correct implementation of Reliability::MultiScaleAnalysis. +* Correct implementation of Reliability::MultiScaleAnalysis. * Spanish translation for Mann-Whitney's U * Added example for Mann-Whitney's U test * Better summary for Mann-Whitney's U Test @@ -179,10 +184,10 @@ * Modified Rakefile to remove dependencies based on C extensions. These are moved to statsample-optimization * T test with unequal variance fixed on i686 -* API Change: Renamed Reliability::ItemAnalysis and moved to independent file +* API Change: Renamed Reliability::ItemAnalysis and moved to independent file * New Reliability::MultiScaleAnalysis for easy analysis of scales on a same survey, includind reliability, correlation matrix and Factor Analysis * Updated README to reflect changes on Reliability module -* SvgGraph works with reportbuilder. +* SvgGraph works with reportbuilder. * Added methods on Polychoric based on Olsson(1979): the idea is estimate using second derivatives. * Distribution test changed (reduced precision on 32 bits system @@ -196,7 +201,7 @@ New features: * Added Statsample::Anova::TwoWay and Statsample::Anova::TwoWayWithVectors * Added Statsample.clone_only valid and Statsample::Dataset.clone_only_valid, for cheap copy on already clean vectors -Optimizations and bug fix +Optimizations and bug fix * Removed library statistics2 from package. Used gem statistics2 instead, because have a extension version * Added example for Reliability class * Bug fix on Statsample::DominanceAnalysis @@ -204,7 +209,7 @@ === 0.10.0 / 2010-04-13 API modifications -* Refactoring of Statsample::Anova module. +* Refactoring of Statsample::Anova module. * Statsample::Anova::OneWay :implementation of generic ANOVA One-Way, used by Multiple Regression, for example. * Statsample::Anova::OneWayWithVectors: implementation of ANOVA One-Way to test differences of means. @@ -228,7 +233,7 @@ === 0.8.1 / 2010-03-29 * Fixed Regression summaries === 0.8.0 / 2010-03-29 -* New Statsample::Test::T module, with classes and methods to do Student's t tests for one and two samples. +* New Statsample::Test::T module, with classes and methods to do Student's t tests for one and two samples. * Statsample::PromiseAfter module to set a number of variables without explicitly call the compute or iterate method * All tests ported to MiniUnit * Directory 'demo' renamed to 'examples' @@ -266,7 +271,7 @@ === 0.6.4 / 2010-02-19 -* Dominance Analysis and Dominance Analysis Bootstrap allows multivariate dependent analysis. +* Dominance Analysis and Dominance Analysis Bootstrap allows multivariate dependent analysis. * Test suite for Dominance Analysis, using Azen and Budescu papers as references * X^2 for polychoric correlation @@ -285,12 +290,12 @@ * New Statsample::Factor module. Include classes for extracting factors (Statsample::Factor::PCA and Statsample::Factor::PrincipalAxis) and rotate component matrix ( Statsample::Factor::Rotation subclasses). For now, only orthogonal rotations * New Statsample::Dataset.crosstab_with_asignation, Statsample::Dataset.one_to_many * New class Statsample::Permutation to produce permutations of a given array -* New class Statsample::Histogram, with same interface as GSL one +* New class Statsample::Histogram, with same interface as GSL one * New class Statsample::Test::UMannWhitney, to perform Mann-Whitney's U test. Gives z based and exact calculation of probability * Improved support for ReportBuilder * Statsample::Codification module reworked * Fixed bugs on Dominance Analysis classes -* Fixed bugs on Statsample::Vector.kurtosis and Statsample::Vector.skew +* Fixed bugs on Statsample::Vector.kurtosis and Statsample::Vector.skew === 0.5.1 / 2009-10-06 @@ -354,15 +359,15 @@ * One Way Anova on Statsample::Anova::OneWay * Dominance Analysis!!!! The one and only reason to develop a Multiple Regression on pure ruby. -* Multiple Regression on Multiple Regression module. Pairwise (pure ruby) or MultipleRegressionPairwise and Listwise (optimized) on MultipleRegressionAlglib and +* Multiple Regression on Multiple Regression module. Pairwise (pure ruby) or MultipleRegressionPairwise and Listwise (optimized) on MultipleRegressionAlglib and * New Dataset#to_gsl_matrix, #from_to,#[..],#bootstrap,#vector_missing_values, #vector_count_characters, #each_with_index, #collect_with_index * New Vector#box_cox_transformation * Module Correlation renamed to Bivariate * Some fancy methods and classes to create Summaries * Some documentation about Algorithm used on doc_latex * Deleted 'distributions' extension. Ruby/GSL has all the pdf and cdf you ever need. -* Tests work without any dependency. Only nags about missing deps. -* Test for MultipleRegression, Anova, Excel, Bivariate.correlation_matrix and many others +* Tests work without any dependency. Only nags about missing deps. +* Test for MultipleRegression, Anova, Excel, Bivariate.correlation_matrix and many others === 0.1.9 / 2009-05-22 @@ -372,8 +377,8 @@ * Module SRS: New methods estimation_n0 and estimation_n * Module Reliability: new ItemCharacteristicCurve class * New HtmlReport class -* New experimental SPSS Class. -* Converters: Module CSV with new options. Added write() method for GGobi module +* New experimental SPSS Class. +* Converters: Module CSV with new options. Added write() method for GGobi module * New Mx exporter (http://www.vcu.edu/mx/) * Class SimpleRegression: new methods standard error @@ -404,7 +409,7 @@ === 0.1.4 / 2008-08-27 * New extension, with cdf functions for - chi-square, t, gamma and normal distributions. + chi-square, t, gamma and normal distributions. Based on dcdflib (http://www.netlib.org/random/) Also, has a function to calculate the tail for a noncentral T distribution From 411f5c6cee6a604d3490bd2c3ea0c43c45958a32 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 00:38:56 -0300 Subject: [PATCH 013/119] Fix rubyvis dependency in gemspec --- statsample.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/statsample.gemspec b/statsample.gemspec index 3561cfa..dd014e3 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -75,7 +75,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rserve-client' s.add_runtime_dependency 'rubyvis', '~> 0.5.0' s.add_runtime_dependency 'distribution' - s.add_runtime_dependency 'rubyvis' s.add_runtime_dependency 'rb-gsl' s.add_runtime_dependency 'awesome_print' From 1abc982f7a027ffb4437eeba0fe07ad11c2ab768 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 00:40:59 -0300 Subject: [PATCH 014/119] Update version to 1.4.1 --- lib/statsample/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/statsample/version.rb b/lib/statsample/version.rb index 4da66f2..ca7ab9a 100644 --- a/lib/statsample/version.rb +++ b/lib/statsample/version.rb @@ -1,3 +1,3 @@ module Statsample - VERSION = '1.4.0' + VERSION = '1.4.1' end From 09bd868beb0c6de18370c10655c606dac6844770 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 24 Mar 2015 18:11:43 -0300 Subject: [PATCH 015/119] Update Minitest usage & test task --- test/helpers_tests.rb | 20 +++++------ test/test_analysis.rb | 34 +++++++++---------- test/test_anova_contrast.rb | 2 +- test/test_anovaoneway.rb | 2 +- test/test_anovatwoway.rb | 4 +-- test/test_anovatwowaywithdataset.rb | 6 ++-- test/test_anovawithvectors.rb | 8 ++--- test/test_awesome_print_bug.rb | 6 ++-- test/test_bartlettsphericity.rb | 2 +- test/test_bivariate.rb | 16 ++++----- test/test_codification.rb | 2 +- test/test_crosstab.rb | 4 +-- test/test_csv.rb | 4 +-- test/test_dataset.rb | 16 ++++----- test/test_dominance_analysis.rb | 2 +- test/test_factor.rb | 42 +++++++++++------------ test/test_factor_map.rb | 18 +++++----- test/test_factor_pa.rb | 12 +++---- test/test_ggobi.rb | 2 +- test/test_gsl.rb | 2 +- test/test_histogram.rb | 10 +++--- test/test_matrix.rb | 10 +++--- test/test_multiset.rb | 22 ++++++------ test/test_regression.rb | 20 +++++------ test/test_reliability.rb | 52 ++++++++++++++--------------- test/test_reliability_icc.rb | 42 +++++++++++------------ test/test_reliability_skillscale.rb | 6 ++-- test/test_resample.rb | 2 +- test/test_rserve_extension.rb | 4 +-- test/test_srs.rb | 2 +- test/test_statistics.rb | 4 +-- test/test_stest.rb | 10 +++--- test/test_stratified.rb | 2 +- test/test_test_f.rb | 8 ++--- test/test_test_kolmogorovsmirnov.rb | 6 ++-- test/test_test_t.rb | 6 ++-- test/test_umannwhitney.rb | 6 ++-- test/test_vector.rb | 2 +- test/test_wilcoxonsignedrank.rb | 26 +++++++-------- test/test_xls.rb | 14 ++++---- 40 files changed, 228 insertions(+), 230 deletions(-) diff --git a/test/helpers_tests.rb b/test/helpers_tests.rb index 47495e7..dcbe61a 100644 --- a/test/helpers_tests.rb +++ b/test/helpers_tests.rb @@ -12,22 +12,20 @@ require 'statsample' -module MiniTest +module Minitest class Test include Shoulda::Context::Assertions include Shoulda::Context::InstanceMethods extend Shoulda::Context::ClassMethods - def self.should_with_gsl(name,&block) - should(name) do - if Statsample.has_gsl? - instance_eval(&block) - else - skip("Requires GSL") - end - + + def self.should_with_gsl(name,&block) + should(name) do + if Statsample.has_gsl? + instance_eval(&block) + else + skip("Requires GSL") end - - + end end end diff --git a/test/test_analysis.rb b/test/test_analysis.rb index 9799f57..70886ff 100644 --- a/test/test_analysis.rb +++ b/test/test_analysis.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase +class StatsampleAnalysisTestCase < Minitest::Test context(Statsample::Analysis) do setup do Statsample::Analysis.clear_analysis @@ -12,7 +12,7 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase assert(Statsample::Analysis.stored_analysis[:first]) assert(Statsample::Analysis.stored_analysis[:first].is_a? Statsample::Analysis::Suite) end - + should "ss_analysis should create an Statsample::Analysis" do ss_analysis(:first) {a=1} end @@ -22,12 +22,12 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase end assert_equal(an,Statsample::Analysis.last) end - + should "add_to_reportbuilder() add sections to reportbuilder object" do rb=mock() rb.expects(:add).with {|value| value.is_a? ReportBuilder::Section and value.name==:first} rb.expects(:add).with {|value| value.is_a? ReportBuilder::Section and value.name==:second} - + Statsample::Analysis.store(:first) do echo "first","second" end @@ -49,29 +49,29 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase summary(a) } obs=Statsample::Analysis.to_text(:first) - + assert_equal(exp.split("\n")[1,exp.size], obs.split("\n")[1,obs.size]) end - + should "run() execute all analysis by default" do m1=mock() m1.expects(:run).once m1.expects(:hide).once - + Statsample::Analysis.store(:first) do m1.run end Statsample::Analysis.store(:second) do m1.hide end - + # Should run all test Statsample::Analysis.run end - + should "run() execute blocks specificed on parameters" do m1=mock() - m1.expects(:run).once + m1.expects(:run).once m1.expects(:hide).never Statsample::Analysis.store(:first) do m1.run @@ -82,7 +82,7 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase # Should run all test Statsample::Analysis.run(:first) end - + context(Statsample::Analysis::Suite) do should "echo() uses output#puts with same arguments" do an=Statsample::Analysis::Suite.new(:output) @@ -116,15 +116,15 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase an.attach(ds1) an.attach(ds2) assert_equal(10,an.x.mean) - assert_equal(12,an.y.mean) - assert_equal(13,an.z.mean) + assert_equal(12,an.y.mean) + assert_equal(13,an.z.mean) end - + should "detach() without arguments drop latest object" do an=Statsample::Analysis::Suite.new(:summary) ds1={'x'=>stub(:mean=>100),'y'=>stub(:mean=>120),'z'=>stub(:mean=>13)} ds1.expects(:fields).returns(%w{x y z}).at_least_once - ds2={'x'=>stub(:mean=>10),'y'=>stub(:mean=>12)} + ds2={'x'=>stub(:mean=>10),'y'=>stub(:mean=>12)} ds2.expects(:fields).returns(%w{x y}).at_least_once an.attach(ds1) an.attach(ds2) @@ -140,7 +140,7 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase ds2.expects(:fields).returns(%w{x y}).at_least_once ds3={'y'=>4} ds3.expects(:fields).returns(%w{y}).at_least_once - + an.attach(ds3) an.attach(ds2) an.attach(ds1) @@ -172,6 +172,6 @@ class StatsampleAnalysisTestCase < MiniTest::Unit::TestCase an.summary(:first) end end - + end end diff --git a/test/test_anova_contrast.rb b/test/test_anova_contrast.rb index a335149..5b823fc 100644 --- a/test/test_anova_contrast.rb +++ b/test/test_anova_contrast.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleAnovaContrastTestCase < MiniTest::Unit::TestCase +class StatsampleAnovaContrastTestCase < Minitest::Test context(Statsample::Anova::Contrast) do setup do constant=[12,13,11,12,12].to_scale diff --git a/test/test_anovaoneway.rb b/test/test_anovaoneway.rb index 2f0e1e5..9510cb5 100644 --- a/test/test_anovaoneway.rb +++ b/test/test_anovaoneway.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleAnovaOneWayTestCase < MiniTest::Unit::TestCase +class StatsampleAnovaOneWayTestCase < Minitest::Test context(Statsample::Anova::OneWay) do setup do @ss_num=30.08 diff --git a/test/test_anovatwoway.rb b/test/test_anovatwoway.rb index aa88194..87813e6 100644 --- a/test/test_anovatwoway.rb +++ b/test/test_anovatwoway.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleAnovaTwoWayTestCase < MiniTest::Unit::TestCase +class StatsampleAnovaTwoWayTestCase < Minitest::Test context(Statsample::Anova::TwoWay) do setup do @ss_a=192.2 @@ -17,7 +17,7 @@ class StatsampleAnovaTwoWayTestCase < MiniTest::Unit::TestCase assert_in_delta(192.2, @anova.ms_a, 0.01) assert_in_delta(57.8, @anova.ms_b, 0.01) assert_in_delta(168.2, @anova.ms_axb, 0.01) - + end should "return correct value for f " do assert_in_delta(40.68, @anova.f_a, 0.01) diff --git a/test/test_anovatwowaywithdataset.rb b/test/test_anovatwowaywithdataset.rb index a08eb7d..0ba26d7 100644 --- a/test/test_anovatwowaywithdataset.rb +++ b/test/test_anovatwowaywithdataset.rb @@ -1,7 +1,7 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) # Reference: # * http://www.uwsp.edu/psych/Stat/13/anova-2w.htm#III -class StatsampleAnovaTwoWayWithVectorsTestCase < MiniTest::Unit::TestCase +class StatsampleAnovaTwoWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::TwoWayWithVectors) do setup do @pa=[5,4,3,4,2,18,19,14,12,15,6,7,5,8,4,6,9,5,9,3].to_scale @@ -25,7 +25,7 @@ class StatsampleAnovaTwoWayWithVectorsTestCase < MiniTest::Unit::TestCase assert_in_delta(192.2, @anova.ms_a, 0.01) assert_in_delta(57.8, @anova.ms_b, 0.01) assert_in_delta(168.2, @anova.ms_axb, 0.01) - + end should "return correct value for f " do assert_in_delta(40.68, @anova.f_a, 0.01) @@ -39,7 +39,7 @@ class StatsampleAnovaTwoWayWithVectorsTestCase < MiniTest::Unit::TestCase end should "respond to summary" do - + @anova.summary_descriptives=true @anova.summary_levene=true assert(@anova.respond_to? :summary) diff --git a/test/test_anovawithvectors.rb b/test/test_anovawithvectors.rb index b85c074..2a9269f 100644 --- a/test/test_anovawithvectors.rb +++ b/test/test_anovawithvectors.rb @@ -1,7 +1,7 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleAnovaOneWayWithVectorsTestCase < MiniTest::Unit::TestCase +class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::OneWayWithVectors) do - + context("when initializing") do setup do @v1=10.times.map {rand(100)}.to_scale @@ -37,10 +37,10 @@ class StatsampleAnovaOneWayWithVectorsTestCase < MiniTest::Unit::TestCase end should "store correctly contrasts" do c1=Statsample::Anova::Contrast.new(:vectors=>[@v1,@v2,@v3], :c=>[1,-0.5, -0.5]) - + c2=@anova.contrast(:c=>[1,-0.5,-0.5]) assert_equal(c1.t,c2.t) - + end should "respond to #summary" do assert(@anova.respond_to? :summary) diff --git a/test/test_awesome_print_bug.rb b/test/test_awesome_print_bug.rb index 065d3e7..0996b68 100644 --- a/test/test_awesome_print_bug.rb +++ b/test/test_awesome_print_bug.rb @@ -1,14 +1,14 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleAwesomePrintBug < MiniTest::Test +class StatsampleAwesomePrintBug < Minitest::Test context("Awesome Print integration") do setup do require "awesome_print" end should "should be flawless" do a=[1,2,3].to_scale - + assert(a!=[1,2,3]) - assert_nothing_raised do + assert_nothing_raised do ap a end end diff --git a/test/test_bartlettsphericity.rb b/test/test_bartlettsphericity.rb index 02f43ce..4b90ebe 100644 --- a/test/test_bartlettsphericity.rb +++ b/test/test_bartlettsphericity.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleBartlettSphericityTestCase < MiniTest::Test +class StatsampleBartlettSphericityTestCase < Minitest::Test include Statsample::Test context Statsample::Test::BartlettSphericity do setup do diff --git a/test/test_bivariate.rb b/test/test_bivariate.rb index 2b745cd..14906c8 100644 --- a/test/test_bivariate.rb +++ b/test/test_bivariate.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleBivariateTestCase < MiniTest::Test +class StatsampleBivariateTestCase < Minitest::Test should "method sum of squares should be correct" do v1=[1,2,3,4,5,6].to_vector(:scale) v2=[6,2,4,10,12,8].to_vector(:scale) @@ -68,14 +68,14 @@ class StatsampleBivariateTestCase < MiniTest::Test v5=Statsample::Vector.new_scale(cases) {rand()} ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4,'v5'=>v5}.to_dataset - + cor_opt=Statsample::Bivariate.covariance_matrix_optimized(ds) - + cor_pw =Statsample::Bivariate.covariance_matrix_pairwise(ds) assert_equal_matrix(cor_opt,cor_pw,1e-15) end should_with_gsl "return same values for optimized and pairwise correlation matrix" do - + cases=100 v1=Statsample::Vector.new_scale(cases) {rand()} v2=Statsample::Vector.new_scale(cases) {rand()} @@ -84,12 +84,12 @@ class StatsampleBivariateTestCase < MiniTest::Test v5=Statsample::Vector.new_scale(cases) {rand()} ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4,'v5'=>v5}.to_dataset - + cor_opt=Statsample::Bivariate.correlation_matrix_optimized(ds) - + cor_pw =Statsample::Bivariate.correlation_matrix_pairwise(ds) assert_equal_matrix(cor_opt,cor_pw,1e-15) - + end should "return correct correlation_matrix without nils values" do v1=[6,5,4,7,8,4,3,2].to_vector(:scale) @@ -112,7 +112,7 @@ class StatsampleBivariateTestCase < MiniTest::Test #assert_equal(expected,obt) end - + should "return correct value for prop pearson" do assert_in_delta(0.42, Statsample::Bivariate.prop_pearson(Statsample::Bivariate.t_r(0.084,94), 94),0.01) assert_in_delta(0.65, Statsample::Bivariate.prop_pearson(Statsample::Bivariate.t_r(0.046,95), 95),0.01) diff --git a/test/test_codification.rb b/test/test_codification.rb index 2049d06..5124f58 100644 --- a/test/test_codification.rb +++ b/test/test_codification.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleCodificationTestCase < MiniTest::Unit::TestCase +class StatsampleCodificationTestCase < Minitest::Test def initialize(*args) v1=%w{run walk,run walking running sleep sleeping,dreaming sleep,dream}.to_vector diff --git a/test/test_crosstab.rb b/test/test_crosstab.rb index 2eef2b1..88bf6ff 100644 --- a/test/test_crosstab.rb +++ b/test/test_crosstab.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleCrosstabTestCase < MiniTest::Unit::TestCase +class StatsampleCrosstabTestCase < Minitest::Test def initialize(*args) @v1=%w{black blonde black black red black brown black blonde black red black blonde}.to_vector @@ -57,7 +57,7 @@ def test_crosstab_with_scale v2=%w{0 0 0 0 0 1 1 1 1 1}.to_scale ct=Statsample::Crosstab.new(v1,v2) assert_equal(Matrix[[0,5],[5,0]],ct.to_matrix) - assert_nothing_raised { ct.summary } + assert_nothing_raised { ct.summary } end end diff --git a/test/test_csv.rb b/test/test_csv.rb index 283dadd..6aac9ef 100644 --- a/test/test_csv.rb +++ b/test/test_csv.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleCSVTestCase < MiniTest::Unit::TestCase +class StatsampleCSVTestCase < Minitest::Test def setup @ds=Statsample::CSV.read(File.dirname(__FILE__)+"/fixtures/test_csv.csv") end @@ -39,7 +39,7 @@ def test_write end end =begin -class StatsampleCSVTestCase2 < MiniTest::Unit::TestCase +class StatsampleCSVTestCase2 < Minitest::Test def setup @ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/test_csv.csv") end diff --git a/test/test_dataset.rb b/test/test_dataset.rb index c6fb979..f595bb6 100644 --- a/test/test_dataset.rb +++ b/test/test_dataset.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleDatasetTestCase < MiniTest::Unit::TestCase +class StatsampleDatasetTestCase < Minitest::Test def setup @ds=Statsample::Dataset.new({'id' => Statsample::Vector.new([1,2,3,4,5]), 'name'=>Statsample::Vector.new(%w{Alex Claude Peter Franz George}), 'age'=>Statsample::Vector.new([20,23,25,27,5]), 'city'=>Statsample::Vector.new(['New York','London','London','Paris','Tome']), @@ -15,7 +15,7 @@ def test_nest assert_equal([{'c'=>'f'},{'c'=>'g'}], nest['a']['c']) assert_equal([{'c'=>'h'}], nest['a']['d']) assert_equal([{'c'=>'j'},{'c'=>'k'}], nest['b']['e']) - + end def test_should_have_summary assert(@ds.summary.size>0) @@ -143,7 +143,7 @@ def test_vector_missing_values mva=[2,3,0,1,0,1].to_vector(:scale) assert_equal(mva,ds.vector_missing_values) end - + def test_has_missing_values a1=[1 ,nil ,3 ,4 , 5,nil].to_vector(:scale) a2=[10 ,nil ,20,20 ,20,30].to_vector(:scale) @@ -155,8 +155,8 @@ def test_has_missing_values clean=ds.dup_only_valid assert(!clean.has_missing_data?) end - - + + def test_vector_count_characters a1=[1 ,"abcde" ,3 ,4 , 5,nil].to_vector(:scale) a2=[10 ,20.3 ,20 ,20 ,20,30].to_vector(:scale) @@ -306,7 +306,7 @@ def test_clone assert_equal(ds3.cases,ds_exp.cases) assert_not_same(ds3.fields,ds_exp.fields) - + end def test_dup v1=[1,2,3,4].to_vector @@ -375,8 +375,8 @@ def test_dup_only_valid assert_equal(expected.vectors.values,Statsample::only_valid(v1,v2,v3)) expected_partial=Statsample::Dataset.new({'v1'=>[1,3,4].to_vector(:scale), 'v3'=>[9, 11,12].to_vector(:scale)}) assert_equal(expected_partial, ds1.dup_only_valid(%w{v1 v3})) - - + + end def test_filter @ds['age'].type=:scale diff --git a/test/test_dominance_analysis.rb b/test/test_dominance_analysis.rb index 803262a..2a668eb 100644 --- a/test/test_dominance_analysis.rb +++ b/test/test_dominance_analysis.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleDominanceAnalysisTestCase < MiniTest::Unit::TestCase +class StatsampleDominanceAnalysisTestCase < Minitest::Test def test_dominance_univariate # Example from Budescu (1993) m=Matrix[[1, 0.683, 0.154, 0.460, 0.618],[0.683, 1, -0.050, 0.297, 0.461], [0.154, -0.050, 1, 0.006, 0.262],[0.460, 0.297, 0.006, 1, 0.507],[0.618, 0.461, 0.262, 0.507, 1]] diff --git a/test/test_factor.rb b/test/test_factor.rb index 1884f4e..02dd6b6 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -2,7 +2,7 @@ #require 'rserve' #require 'statsample/rserve_extension' -class StatsampleFactorTestCase < MiniTest::Unit::TestCase +class StatsampleFactorTestCase < Minitest::Test include Statsample::Fixtures # Based on Hardle and Simar def setup @@ -28,15 +28,15 @@ def test_covariance_matrix k.times {|j| # variable ds_id="v#{j+1}" r= Statsample::Bivariate.correlation(ds[ds_id], pcs[pc_id]) - assert_in_delta( r, comp_matrix[j,i]) + assert_in_delta( r, comp_matrix[j,i]) } } - + end def test_principalcomponents_ruby_gsl - + ran=Distribution::Normal.rng - + # @r=::Rserve::Connection.new samples=20 @@ -46,7 +46,7 @@ def test_principalcomponents_ruby_gsl (1...k).each {|i| v["x#{i}"]=samples.times.map {|ii| ran.call()*0.5+v["x#{i-1}"][ii]*0.5}.to_scale.centered } - + ds=v.to_dataset cm=ds.covariance_matrix # @r.assign('ds',ds) @@ -64,7 +64,7 @@ def test_principalcomponents_ruby_gsl pc_id="PC_#{i+1}" assert_in_delta(pca_ruby.eigenvalues[i], pca_gsl.eigenvalues[i],1e-10) # Revert gsl component values - pc_gsl_data= (pc_gsl[pc_id][0]-pc_ruby[pc_id][0]).abs>1e-6 ? pc_gsl[pc_id].recode {|v| -v} : pc_gsl[pc_id] + pc_gsl_data= (pc_gsl[pc_id][0]-pc_ruby[pc_id][0]).abs>1e-6 ? pc_gsl[pc_id].recode {|v| -v} : pc_gsl[pc_id] assert_similar_vector(pc_gsl_data, pc_ruby[pc_id], 1e-6,"PC for #{k} variables") if false k.times {|j| # variable @@ -80,15 +80,15 @@ def test_principalcomponents_ruby_gsl def test_principalcomponents() principalcomponents(true) principalcomponents(false) - - end + + end def principalcomponents(gsl) ran=Distribution::Normal.rng samples=50 x1=samples.times.map { ran.call()}.to_scale x2=samples.times.map {|i| ran.call()*0.5+x1[i]*0.5}.to_scale ds={'x1'=>x1,'x2'=>x2}.to_dataset - + cm=ds.correlation_matrix r=cm[0,1] pca=Statsample::Factor::PCA.new(cm,:m=>2,:use_gsl=>gsl) @@ -97,9 +97,9 @@ def principalcomponents(gsl) hs=1.0 / Math.sqrt(2) assert_equal_vector(Vector[1, 1]*hs, pca.eigenvectors[0]) m_1=gsl ? Vector[-1,1] : Vector[1,-1] - - assert_equal_vector(hs*m_1, pca.eigenvectors[1]) - + + assert_equal_vector(hs*m_1, pca.eigenvectors[1]) + pcs=pca.principal_components(ds) exp_pc_1=ds.collect_with_index {|row,i| hs*(row['x1']+row['x2']) @@ -127,7 +127,7 @@ def test_kmo kmo=Statsample::Factor.kmo(cor) assert_in_delta(0.667, kmo,0.001) assert_in_delta(0.81, Statsample::Factor.kmo(harman_817),0.01) - + end def test_kmo_univariate m=harman_817 @@ -175,14 +175,14 @@ def pca_set(pca,type) def test_principalaxis matrix=::Matrix[ [1.0, 0.709501601093587, 0.877596585880047, 0.272219316266807], [0.709501601093587, 1.0, 0.291633797330304, 0.871141831433844], [0.877596585880047, 0.291633797330304, 1.0, -0.213373722977167], [0.272219316266807, 0.871141831433844, -0.213373722977167, 1.0]] - - + + fa=Statsample::Factor::PrincipalAxis.new(matrix,:m=>1, :max_iterations=>50) cm=::Matrix[[0.923],[0.912],[0.507],[0.483]] - + assert_equal_matrix(cm,fa.component_matrix,0.001) - + h2=[0.852,0.832,0.257,0.233] h2.each_with_index{|ev,i| assert_in_delta(ev,fa.communalities[i],0.001) @@ -191,7 +191,7 @@ def test_principalaxis assert_in_delta(eigen1, fa.eigenvalues[0],0.001) assert(fa.summary.size>0) fa=Statsample::Factor::PrincipalAxis.new(matrix,:smc=>false) - + assert_raise RuntimeError do fa.iterate end @@ -213,10 +213,10 @@ def test_rotation_varimax assert(!varimax.rotated.nil?, "Rotated shouldn't be empty") assert(!varimax.component_transformation_matrix.nil?, "Component matrix shouldn't be empty") assert(!varimax.h2.nil?, "H2 shouldn't be empty") - + assert_equal_matrix(expected,varimax.rotated,1e-6) assert(varimax.summary.size>0) end - + end diff --git a/test/test_factor_map.rb b/test/test_factor_map.rb index 05c94d5..7cb0e2f 100644 --- a/test/test_factor_map.rb +++ b/test/test_factor_map.rb @@ -2,10 +2,10 @@ #require 'rserve' #require 'statsample/rserve_extension' -class StatsampleFactorMpaTestCase < MiniTest::Unit::TestCase +class StatsampleFactorMpaTestCase < Minitest::Test context Statsample::Factor::MAP do setup do - m=Matrix[ + m=Matrix[ [ 1, 0.846, 0.805, 0.859, 0.473, 0.398, 0.301, 0.382], [ 0.846, 1, 0.881, 0.826, 0.376, 0.326, 0.277, 0.415], [ 0.805, 0.881, 1, 0.801, 0.38, 0.319, 0.237, 0.345], @@ -25,19 +25,19 @@ class StatsampleFactorMpaTestCase < MiniTest::Unit::TestCase #require 'ruby-prof' @map.use_gsl=true - map_assertions(@map) + map_assertions(@map) end - - + + end - + def map_assertions(map) assert_in_delta(map.minfm, 0.066445,0.00001) assert_equal(map.number_of_factors, 2) assert_in_delta(map.fm[0], 0.312475,0.00001) - assert_in_delta(map.fm[1], 0.245121,0.00001) + assert_in_delta(map.fm[1], 0.245121,0.00001) end - - + + end diff --git a/test/test_factor_pa.rb b/test/test_factor_pa.rb index b1332ba..c8d6a36 100644 --- a/test/test_factor_pa.rb +++ b/test/test_factor_pa.rb @@ -2,7 +2,7 @@ #require 'rserve' #require 'statsample/rserve_extension' -class StatsampleFactorTestCase < MiniTest::Unit::TestCase +class StatsampleFactorTestCase < Minitest::Test include Statsample::Fixtures # Based on Hardle and Simar def setup @@ -15,7 +15,7 @@ def test_parallelanalysis_with_data iterations=50 rng = Distribution::Normal.rng f1=samples.times.collect {rng.call}.to_scale - f2=samples.times.collect {rng.call}.to_scale + f2=samples.times.collect {rng.call}.to_scale vectors={} variables.times do |i| if i<5 @@ -27,10 +27,10 @@ def test_parallelanalysis_with_data f2[nv]*5+f1[nv]*2+rng.call }.to_scale end - + end ds=vectors.to_dataset - + pa1=Statsample::Factor::ParallelAnalysis.new(ds, :bootstrap_method=>:data, :iterations=>iterations) pa2=Statsample::Factor::ParallelAnalysis.with_random_data(samples,variables,:iterations=>iterations,:percentil=>95) 3.times do |n| @@ -40,7 +40,7 @@ def test_parallelanalysis_with_data else skip("Too slow without GSL") end - + end def test_parallelanalysis pa=Statsample::Factor::ParallelAnalysis.with_random_data(305,8,:iterations=>100,:percentil=>95) @@ -48,5 +48,5 @@ def test_parallelanalysis assert_in_delta(1.1542, pa.ds_eigenvalues['ev_00002'].mean, 0.01) assert_in_delta(1.0836, pa.ds_eigenvalues['ev_00003'].mean, 0.01) assert(pa.summary.size>0) - end + end end diff --git a/test/test_ggobi.rb b/test/test_ggobi.rb index ecef32c..bbe37db 100644 --- a/test/test_ggobi.rb +++ b/test/test_ggobi.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) require 'ostruct' -class StatsampleGGobiTestCase < MiniTest::Unit::TestCase +class StatsampleGGobiTestCase < Minitest::Test def setup v1=([10.2,20.3,10,20,30,40,30,20,30,40]*10).to_vector(:scale) diff --git a/test/test_gsl.rb b/test/test_gsl.rb index 2d841aa..cff4b24 100644 --- a/test/test_gsl.rb +++ b/test/test_gsl.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleGSLTestCase < MiniTest::Unit::TestCase +class StatsampleGSLTestCase < Minitest::Test should_with_gsl "matrix with gsl" do a=[1,2,3,4,20].to_vector(:scale) b=[3,2,3,4,50].to_vector(:scale) diff --git a/test/test_histogram.rb b/test/test_histogram.rb index 1a086e0..c9e51b6 100644 --- a/test/test_histogram.rb +++ b/test/test_histogram.rb @@ -1,7 +1,7 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleHistogramTestCase < MiniTest::Unit::TestCase +class StatsampleHistogramTestCase < Minitest::Test context Statsample::Histogram do should "alloc correctly with integer" do h = Statsample::Histogram.alloc(4) @@ -22,8 +22,8 @@ class StatsampleHistogramTestCase < MiniTest::Unit::TestCase h = Statsample::Histogram.alloc(4) assert_equal(4,h.bins) end - should "increment correctly" do - h = Statsample::Histogram.alloc(5, [0, 5]) + should "increment correctly" do + h = Statsample::Histogram.alloc(5, [0, 5]) h.increment 2.5 assert_equal([0.0,0.0,1.0,0.0,0.0], h.bin) h.increment [0.5,0.5,3.5,3.5] @@ -33,7 +33,7 @@ class StatsampleHistogramTestCase < MiniTest::Unit::TestCase h.increment 5 assert_equal([3.0,0.0,1.0,2.0,0.0], h.bin) end - + should "alloc_uniform correctly with n, min,max" do h = Statsample::Histogram.alloc_uniform(5,0,10) assert_equal(5,h.bins) @@ -107,6 +107,6 @@ class StatsampleHistogramTestCase < MiniTest::Unit::TestCase h.to_svg end end - + end end diff --git a/test/test_matrix.rb b/test/test_matrix.rb index ef44771..0a48cd2 100644 --- a/test/test_matrix.rb +++ b/test/test_matrix.rb @@ -1,7 +1,7 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleMatrixTestCase < MiniTest::Unit::TestCase - +class StatsampleMatrixTestCase < Minitest::Test + def test_to_dataset m=Matrix[[1,4],[2,5],[3,6]] m.extend Statsample::NamedMatrix @@ -16,8 +16,8 @@ def test_to_dataset assert_equal(ds['x1'],obs['x1']) assert_equal(ds['x2'],obs['x2']) assert_equal(ds['x1'].mean,obs['x1'].mean) - - + + end def test_covariate a=Matrix[[1.0, 0.3, 0.2], [0.3, 1.0, 0.5], [0.2, 0.5, 1.0]] @@ -46,5 +46,5 @@ def test_covariate assert_in_delta(corr[i,j], real[i,j],1e-15) end end - end + end end diff --git a/test/test_multiset.rb b/test/test_multiset.rb index 2c5487c..bd676f2 100644 --- a/test/test_multiset.rb +++ b/test/test_multiset.rb @@ -1,7 +1,7 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleMultisetTestCase < MiniTest::Unit::TestCase +class StatsampleMultisetTestCase < Minitest::Test def setup @x=%w{a a a a b b b b}.to_vector @y=[1,2,3,4,5,6,7,8].to_scale @@ -121,17 +121,17 @@ def test_each end def test_multiset_union_with_block - + r1=rand() r2=rand() ye=[1*r1,2*r1,3*r1,4*r1,5*r2,6*r2,7*r2,8*r2].to_scale - + ze=[10*r1,11*r1,12*r1,13*r1, 14*r2,15*r2,16*r2,17*r2].to_scale - + ds2=@ms.union {|k,ds| - ds['y'].recode!{|v| + ds['y'].recode!{|v| k=='a' ? v*r1 : v*r2} - ds['z'].recode!{|v| + ds['z'].recode!{|v| k=='a' ? v*r1 : v*r2} } assert_equal(ye,ds2['y']) @@ -141,18 +141,18 @@ def test_multiset_union r1=rand() r2=rand() ye=[1*r1,2*r1,3*r1,4*r1,5*r2,6*r2,7*r2,8*r2].to_scale - + ze=[10*r1,11*r1,12*r1,13*r1, 14*r2,15*r2,16*r2,17*r2].to_scale @ms.each {|k,ds| - ds['y'].recode!{|v| + ds['y'].recode!{|v| k=='a' ? v*r1 : v*r2} - ds['z'].recode!{|v| + ds['z'].recode!{|v| k=='a' ? v*r1 : v*r2} - + } ds2=@ms.union assert_equal(ye,ds2['y']) assert_equal(ze,ds2['z']) - + end end diff --git a/test/test_regression.rb b/test/test_regression.rb index 8405703..03aa430 100644 --- a/test/test_regression.rb +++ b/test/test_regression.rb @@ -1,11 +1,11 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleRegressionTestCase < MiniTest::Unit::TestCase +class StatsampleRegressionTestCase < Minitest::Test context "Example with missing data" do - setup do + setup do @x=[0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857].to_scale - @y=[nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_scale + @y=[nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_scale @ds={'x'=>@x,'y'=>@y}.to_dataset @lr=Statsample::Regression::Multiple::RubyEngine.new(@ds,'y') end @@ -21,11 +21,11 @@ class StatsampleRegressionTestCase < MiniTest::Unit::TestCase assert_in_delta(0.064, @lr.constant_se,0.001,"constant se") end end - should "return an error if data is linearly dependent" do + should "return an error if data is linearly dependent" do samples=100 - + a,b=rand,rand - + x1=samples.times.map { rand}.to_scale x2=samples.times.map {rand}.to_scale x3=samples.times.map {|i| x1[i]*(1+a)+x2[i]*(1+b)}.to_scale @@ -50,13 +50,13 @@ def test_parameters end def _test_simple_regression(reg) - + assert_in_delta(40.009, reg.a,0.001) assert_in_delta(-0.957, reg.b,0.001) assert_in_delta(4.248,reg.standard_error,0.002) assert(reg.summary) end - + def test_summaries a=10.times.map{rand(100)}.to_scale b=10.times.map{rand(100)}.to_scale @@ -87,7 +87,7 @@ def test_multiple_dependent assert_in_delta(0.07, lr.p2yx,0.001) end - + def test_multiple_regression_pairwise_2 @a=[1,3,2,4,3,5,4,6,5,7,3,nil,3,nil,3].to_vector(:scale) @b=[3,3,4,4,5,5,6,6,4,4,2,2,nil,6,2].to_vector(:scale) @@ -186,7 +186,7 @@ def test_regression_matrix @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:scale) ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset cor=Statsample::Bivariate.correlation_matrix(ds) - + lr=Statsample::Regression::Multiple::MatrixEngine.new(cor,'y', :y_mean=>@y.mean, :x_mean=>{'a'=>ds['a'].mean, 'b'=>ds['b'].mean, 'c'=>ds['c'].mean}, :cases=>@a.size, :y_sd=>@y.sd , :x_sd=>{'a' => @a.sd, 'b' => @b.sd, 'c' => @c.sd}) assert_nil(lr.constant_se) assert_nil(lr.constant_t) diff --git a/test/test_reliability.rb b/test/test_reliability.rb index c7730e6..be7c750 100644 --- a/test/test_reliability.rb +++ b/test/test_reliability.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase +class StatsampleReliabilityTestCase < Minitest::Test context Statsample::Reliability do should "return correct r according to Spearman-Brown prophecy" do r=0.6849 @@ -11,7 +11,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase r_d=0.9 assert_in_delta(62, Statsample::Reliability.n_for_desired_reliability(r, r_d, 15),0.5) end - context "Cronbach's alpha" do + context "Cronbach's alpha" do setup do @samples=40 @n_variables=rand(10)+2 @@ -20,7 +20,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase @n_variables.times do |i| @ds[i]=base.collect {|v| v+rand()}.to_scale end - + @ds.update_valid_data @k=@ds.fields.size @cm=Statsample::Bivariate.covariance_matrix(@ds) @@ -41,7 +41,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase should "method cronbach_alpha_from_n_s2_cov return correct values" do sa=Statsample::Reliability::ScaleAnalysis.new(@ds) vm, cm = sa.variances_mean, sa.covariances_mean - assert_in_delta(sa.alpha, Statsample::Reliability.cronbach_alpha_from_n_s2_cov(@n_variables, vm,cm), 1e-10) + assert_in_delta(sa.alpha, Statsample::Reliability.cronbach_alpha_from_n_s2_cov(@n_variables, vm,cm), 1e-10) end should "method cronbach_alpha_from_covariance_matrix returns correct value" do cov=Statsample::Bivariate.covariance_matrix(@ds) @@ -52,9 +52,9 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase vm, cm = sa.variances_mean, sa.covariances_mean n_obtained=Statsample::Reliability.n_for_desired_alpha(@a, vm,cm) #p n_obtained - assert_in_delta(Statsample::Reliability.cronbach_alpha_from_n_s2_cov(n_obtained, vm,cm) ,@a,0.001) + assert_in_delta(Statsample::Reliability.cronbach_alpha_from_n_s2_cov(n_obtained, vm,cm) ,@a,0.001) end - + should "standarized alpha will be equal to sum of matrix covariance less the individual variances on standarized values" do total_sum=@cme.total_sum ind_var=@dse.fields.inject(0) {|ac,v| ac+@dse[v].variance} @@ -82,7 +82,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase assert_equal(x2, @icc.vector_total) assert_raises(ArgumentError) do inc=(@samples+10).times.map{rand(10)}.to_scale - @icc=Statsample::Reliability::ItemCharacteristicCurve.new(@ds,inc) + @icc=Statsample::Reliability::ItemCharacteristicCurve.new(@ds,inc) end end should "have 0% for 0 points on maximum value values" do @@ -108,13 +108,13 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase total[k]=v.quo(total_g[k]) } assert_equal(expected, @icc.curve_field('a',index)) - + end - + end - + context Statsample::Reliability::MultiScaleAnalysis do - + setup do size=100 @scales=3 @@ -134,7 +134,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase end end should "Retrieve correct ScaleAnalysis for whole scale" do - sa=Statsample::Reliability::ScaleAnalysis.new(@ds, :name=>"Scale complete") + sa=Statsample::Reliability::ScaleAnalysis.new(@ds, :name=>"Scale complete") assert_equal(sa.variances_mean, @msa.scale("complete").variances_mean) end should "Retrieve correct ScaleAnalysis for each scale" do @@ -146,7 +146,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase should "retrieve correct correlation matrix for each scale" do vectors={'complete' => @ds.vector_sum} @scales.times {|s| - vectors["scale_#{s}"]=@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}).vector_sum + vectors["scale_#{s}"]=@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}).vector_sum } ds2=vectors.to_dataset assert_equal(Statsample::Bivariate.correlation_matrix(ds2), @msa.correlation_matrix) @@ -159,7 +159,7 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase @msa.delete_scale("complete") vectors=Hash.new @scales.times {|s| - vectors["scale_#{s}"]=@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}).vector_sum + vectors["scale_#{s}"]=@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}).vector_sum } ds2=vectors.to_dataset cor_matrix=Statsample::Bivariate.correlation_matrix(ds2) @@ -171,17 +171,17 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase @msa.delete_scale("scale_0") @msa.delete_scale("scale_1") @msa.delete_scale("scale_2") - - + + #@msa.summary_correlation_matrix=true #@msa.summary_pca=true - - + + assert(@msa.summary.size>0) end end context Statsample::Reliability::ScaleAnalysis do - setup do + setup do @x1=[1,1,1,1,2,2,2,2,3,3,3,30].to_scale @x2=[1,1,1,2,2,3,3,3,3,4,4,50].to_scale @x3=[2,2,1,1,1,2,2,2,3,4,5,40].to_scale @@ -189,11 +189,11 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase @ds={'x1'=>@x1,'x2'=>@x2,'x3'=>@x3,'x4'=>@x4}.to_dataset @ia=Statsample::Reliability::ScaleAnalysis.new(@ds) @cov_matrix=@ia.cov_m - end - should "return correct values for item analysis" do + end + should "return correct values for item analysis" do assert_in_delta(0.980,@ia.alpha,0.001) assert_in_delta(0.999,@ia.alpha_standarized,0.001) - var_mean=4.times.map{|m| @cov_matrix[m,m]}.to_scale.mean + var_mean=4.times.map{|m| @cov_matrix[m,m]}.to_scale.mean assert_in_delta(var_mean, @ia.variances_mean) assert_equal(@x1.mean, @ia.item_statistics['x1'][:mean]) assert_equal(@x4.mean, @ia.item_statistics['x4'][:mean]) @@ -207,11 +207,11 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase assert_in_delta(vector_sum.variance, @ia.stats_if_deleted['x1'][:variance_sample],1e-10) assert_equal(Statsample::Reliability.cronbach_alpha(ds2), @ia.stats_if_deleted['x1'][:alpha]) - + covariances=[] 4.times.each {|i| 4.times.each {|j| - if i!=j + if i!=j covariances.push(@cov_matrix[i,j]) end } @@ -220,10 +220,10 @@ class StatsampleReliabilityTestCase < MiniTest::Unit::TestCase assert_in_delta(0.999,@ia.item_total_correlation()['x1'],0.001) assert_in_delta(1050.455,@ia.stats_if_deleted()['x1'][:variance_sample],0.001) end - should "return a summary" do + should "return a summary" do assert(@ia.summary.size>0) end - + end end end diff --git a/test/test_reliability_icc.rb b/test/test_reliability_icc.rb index d413cc9..bfdf9bd 100644 --- a/test/test_reliability_icc.rb +++ b/test/test_reliability_icc.rb @@ -2,7 +2,7 @@ $reliability_icc=nil -class StatsampleReliabilityIccTestCase < MiniTest::Test +class StatsampleReliabilityIccTestCase < Minitest::Test context Statsample::Reliability::ICC do setup do a=[9,6,8,7,10,6].to_scale @@ -31,21 +31,21 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test should "ms within targets be correct" do assert_in_delta(6.26, @icc.ms_wt, 0.01) end - should "ms between judges be correct" do + should "ms between judges be correct" do assert_in_delta(32.49, @icc.ms_bj, 0.01) end should "ms residual be correct" do assert_in_delta(1.02, @icc.ms_residual, 0.01) end - context "with McGraw and Wong denominations," do - + context "with McGraw and Wong denominations," do + end - context "with Shrout & Fleiss denominations, " do + context "with Shrout & Fleiss denominations, " do should "icc(1,1) method be correct" do assert_in_delta(0.17, @icc.icc_1_1, 0.01) end # Verified on SPSS and R - should "icc(2,1) method be correct" do + should "icc(2,1) method be correct" do assert_in_delta(0.29, @icc.icc_2_1, 0.01) end should "icc(3,1) method be correct" do @@ -57,11 +57,11 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test # Verified on SPSS and R should "icc(2,k) method be correct" do assert_in_delta(0.62, @icc.icc_2_k, 0.01) - end + end should "icc(3,k) method be correct" do assert_in_delta(0.91, @icc.icc_3_k, 0.01) end - + should "icc(1,1) F be correct" do assert_in_delta(1.795, @icc.icc_1_f.f) end @@ -73,7 +73,7 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test assert_in_delta(-0.884, @icc.icc_1_k_ci[0], 0.001) assert_in_delta(0.912, @icc.icc_1_k_ci[1], 0.001) end - + should "icc(2,1) F be correct" do assert_in_delta(11.027, @icc.icc_2_f.f) end @@ -82,8 +82,8 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test assert_in_delta(0.019, @icc.icc_2_1_ci[0], 0.001) assert_in_delta(0.761, @icc.icc_2_1_ci[1], 0.001) end - - # Verified on SPSS and R + + # Verified on SPSS and R should "icc(2,k) confidence interval should be correct" do #skip("Not yet operational") #p @icc.icc_2_k_ci @@ -94,11 +94,11 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test #should "Shrout icc(2,k) and McGraw icc(a,k) ci be equal" do # assert_in_delta(@icc.icc_2_k_ci_shrout[0], @icc.icc_2_k_ci_mcgraw[0], 10e-5) #end - + should "icc(3,1) F be correct" do assert_in_delta(11.027, @icc.icc_3_f.f) end - + should "icc(3,1) confidence interval should be correct" do assert_in_delta(0.342, @icc.icc_3_1_ci[0], 0.001) assert_in_delta(0.946, @icc.icc_3_1_ci[1], 0.001) @@ -108,12 +108,12 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test assert_in_delta(0.986, @icc.icc_3_k_ci[1], 0.001) end should "incorrect type raises an error" do - assert_raise(::RuntimeError) do + assert_raise(::RuntimeError) do @icc.type=:nonexistant_type end end end - + begin require 'rserve' require 'statsample/rserve_extension' @@ -129,12 +129,12 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test c=a.recode{|i|i+rand(4)-2} d=a.recode{|i|i+rand(4)-2} @ds={'a'=>a,'b'=>b,'c'=>c,'d'=>d}.to_dataset - + @icc=Statsample::Reliability::ICC.new(@ds) @r=Rserve::Connection.new - + @r.assign('ds',@ds) - + @r.void_eval("library(irr); iccs=list( icc_1=icc(ds,'o','c','s'), @@ -147,7 +147,7 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test @iccs=@r.eval('iccs').to_ruby $reliability_icc={ :icc=>@icc, :iccs=>@iccs, :r=>@r } - + end @icc=$reliability_icc[:icc] @iccs=$reliability_icc[:iccs] @@ -186,7 +186,7 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test @icc.type=t @r_icc=@iccs[t.to_s] assert_in_delta(@r_icc['lbound'],@icc.lbound) - assert_in_delta(@r_icc['ubound'],@icc.ubound) + assert_in_delta(@r_icc['ubound'],@icc.ubound) end should "summary generated" do assert(@icc.summary.size>0) @@ -197,6 +197,6 @@ class StatsampleReliabilityIccTestCase < MiniTest::Test rescue puts "requires rserve" end - + end end diff --git a/test/test_reliability_skillscale.rb b/test/test_reliability_skillscale.rb index 456c808..9e78b2c 100644 --- a/test/test_reliability_skillscale.rb +++ b/test/test_reliability_skillscale.rb @@ -1,7 +1,7 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleReliabilitySkillScaleTestCase < MiniTest::Unit::TestCase +class StatsampleReliabilitySkillScaleTestCase < Minitest::Test context Statsample::Reliability::SkillScaleAnalysis do setup do options=%w{a b c d e} @@ -12,7 +12,7 @@ class StatsampleReliabilitySkillScaleTestCase < MiniTest::Unit::TestCase @c=cases.times.map {options[rand(5)]}.to_vector @d=cases.times.map {options[rand(5)]}.to_vector @e=cases.times.map {|i| - i==0 ? options[rand(0)] : + i==0 ? options[rand(0)] : rand()>0.8 ? nil : options[rand(5)] }.to_vector @ds={'id'=>@id,'a'=>@a,'b'=>@b,'c'=>@c,'d'=>@d,'e'=>@e}.to_dataset @@ -49,7 +49,7 @@ class StatsampleReliabilitySkillScaleTestCase < MiniTest::Unit::TestCase ssa=Statsample::Reliability::SkillScaleAnalysis.new(ds, key) assert(ssa.summary) end - + should "return valid summary" do assert(@ssa.summary.size>0) end diff --git a/test/test_resample.rb b/test/test_resample.rb index c1821e1..3612bdc 100644 --- a/test/test_resample.rb +++ b/test/test_resample.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleResampleTestCase < MiniTest::Unit::TestCase +class StatsampleResampleTestCase < Minitest::Test def initialize(*args) super end diff --git a/test/test_rserve_extension.rb b/test/test_rserve_extension.rb index e718978..9466cff 100644 --- a/test/test_rserve_extension.rb +++ b/test/test_rserve_extension.rb @@ -3,7 +3,7 @@ require 'rserve' require 'statsample/rserve_extension' -class StatsampleRserveExtensionTestCase < MiniTest::Unit::TestCase +class StatsampleRserveExtensionTestCase < Minitest::Test context "Statsample Rserve extensions" do setup do @r=Rserve::Connection.new @@ -27,7 +27,7 @@ class StatsampleRserveExtensionTestCase < MiniTest::Unit::TestCase rexp=ds.to_REXP assert(rexp.is_a? Rserve::REXP::GenericVector) ret=rexp.to_ruby - assert_equal(a.data_with_nils, ret['a']) + assert_equal(a.data_with_nils, ret['a']) @r.assign 'df', rexp out_df=@r.eval('df').to_ruby assert_equal('data.frame', out_df.attributes['class']) diff --git a/test/test_srs.rb b/test/test_srs.rb index 1d18cf9..7b70abb 100644 --- a/test/test_srs.rb +++ b/test/test_srs.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleSrsTestCase < MiniTest::Unit::TestCase +class StatsampleSrsTestCase < Minitest::Test def test_std_error assert_equal(384,Statsample::SRS.estimation_n0(0.05,0.5,0.95).to_i) assert_equal(108,Statsample::SRS.estimation_n(0.05,0.5,150,0.95).to_i) diff --git a/test/test_statistics.rb b/test/test_statistics.rb index 7fe47d3..44eaf99 100644 --- a/test/test_statistics.rb +++ b/test/test_statistics.rb @@ -1,5 +1,5 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleStatisicsTestCase < MiniTest::Unit::TestCase +class StatsampleStatisicsTestCase < Minitest::Test def initialize(*args) super @@ -11,7 +11,7 @@ def test_p_using_cdf assert_equal(1, Statsample::Test.p_using_cdf(0.50, tails=:both)) assert_equal(0.05, Statsample::Test.p_using_cdf(0.025, tails=:both)) assert_in_delta(0.05, Statsample::Test.p_using_cdf(0.975, tails=:both),0.0001) - + end def test_recode_repeated a=%w{a b c c d d d e} diff --git a/test/test_stest.rb b/test/test_stest.rb index e13c580..7f5b1fd 100644 --- a/test/test_stest.rb +++ b/test/test_stest.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleTestTestCase < MiniTest::Unit::TestCase +class StatsampleTestTestCase < Minitest::Test def test_chi_square_matrix_with_expected real=Matrix[[95,95],[45,155]] expected=Matrix[[68,122],[72,128]] @@ -9,7 +9,7 @@ def test_chi_square_matrix_with_expected end chi=Statsample::Test.chi_square(real,expected).chi_square assert_in_delta(32.53,chi,0.1) - + end def test_chi_square_matrix_only_observed observed=Matrix[[20,30,40],[30,40,50],[60,70,80],[10,20,40]] @@ -21,9 +21,9 @@ def test_chi_square_matrix_only_observed assert_in_delta(0.1444, chi.probability, 0.0001) assert_equal(6, chi.df) - + end - + def test_u_mannwhitney a=[1,2,3,4,5,6].to_scale b=[0,5,7,9,10,11].to_scale @@ -52,5 +52,5 @@ def assert_levene(levene) assert_in_delta(0.778, levene.f, 0.001) assert_in_delta(0.389, levene.probability, 0.001) end - + end diff --git a/test/test_stratified.rb b/test/test_stratified.rb index eb8ef45..cb8d451 100644 --- a/test/test_stratified.rb +++ b/test/test_stratified.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleStratifiedTestCase < MiniTest::Unit::TestCase +class StatsampleStratifiedTestCase < Minitest::Test def initialize(*args) super diff --git a/test/test_test_f.rb b/test/test_test_f.rb index b7cc4a8..182b087 100644 --- a/test/test_test_f.rb +++ b/test/test_test_f.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleTestFTestCase < MiniTest::Unit::TestCase - context(Statsample::Test::F) do +class StatsampleTestFTestCase < Minitest::Test + context(Statsample::Test::F) do setup do @ssb=84 @ssw=68 @@ -14,13 +14,13 @@ class StatsampleTestFTestCase < MiniTest::Unit::TestCase should "have df total equal to df_num+df_den" do assert_equal(@df_num + @df_den, @f.df_total) end - should "have probability near 0.002" do + should "have probability near 0.002" do assert_in_delta(0.002, @f.probability, 0.0005) end should "be coerced into float" do assert_equal(@f.to_f, @f.f) end - + context("method summary") do setup do @summary=@f.summary diff --git a/test/test_test_kolmogorovsmirnov.rb b/test/test_test_kolmogorovsmirnov.rb index 409d25d..5e7ad2c 100644 --- a/test/test_test_kolmogorovsmirnov.rb +++ b/test/test_test_kolmogorovsmirnov.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleTestKolmogorovSmirnovTestCase < MiniTest::Unit::TestCase - context(Statsample::Test::KolmogorovSmirnov) do +class StatsampleTestKolmogorovSmirnovTestCase < Minitest::Test + context(Statsample::Test::KolmogorovSmirnov) do should "calculate correctly D for two given samples" do a=[1.1,2.5,5.6,9] b=[1,2.3,5.8,10] @@ -18,7 +18,7 @@ class StatsampleTestKolmogorovSmirnovTestCase < MiniTest::Unit::TestCase ks=Statsample::Test::KolmogorovSmirnov.new(a,Distribution::Normal) assert(ks.d<0.15) end - + context(Statsample::Test::KolmogorovSmirnov::EmpiricDistribution) do should "Create a correct empirical distribution for an array" do a=[10,9,8,7,6,5,4,3,2,1] diff --git a/test/test_test_t.rb b/test/test_test_t.rb index 1c39a6b..b2a1836 100644 --- a/test/test_test_t.rb +++ b/test/test_test_t.rb @@ -1,8 +1,8 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleTestTTestCase < MiniTest::Unit::TestCase +class StatsampleTestTTestCase < Minitest::Test include Statsample::Test include Math - context T do + context T do setup do @a=[30.02, 29.99, 30.11, 29.97, 30.01, 29.99].to_scale @b=[29.89, 29.93, 29.72, 29.98, 30.02, 29.98].to_scale @@ -35,7 +35,7 @@ class StatsampleTestTTestCase < MiniTest::Unit::TestCase end should "calculate correctly df for equal and unequal variance" do assert_equal(10, T.df_equal_variance(@n1,@n2)) - assert_in_delta(7.03, T.df_not_equal_variance(@s1,@s2,@n1,@n2),0.001) + assert_in_delta(7.03, T.df_not_equal_variance(@s1,@s2,@n1,@n2),0.001) end should "calculate all values for T object" do t=Statsample::Test.t_two_samples_independent(@a,@b) diff --git a/test/test_umannwhitney.rb b/test/test_umannwhitney.rb index 82817af..a010a87 100644 --- a/test/test_umannwhitney.rb +++ b/test/test_umannwhitney.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleUMannWhitneyTestCase < MiniTest::Unit::TestCase +class StatsampleUMannWhitneyTestCase < Minitest::Test include Statsample::Test context Statsample::Test::UMannWhitney do setup do @@ -8,8 +8,8 @@ class StatsampleUMannWhitneyTestCase < MiniTest::Unit::TestCase @v2=[5,6,11,12,13,16,17,18,19].to_scale @u=Statsample::Test::UMannWhitney.new(@v1,@v2) end - should "have same result using class or Test#u_mannwhitney" do - assert_equal(Statsample::Test.u_mannwhitney(@v1,@v2).u, @u.u) + should "have same result using class or Test#u_mannwhitney" do + assert_equal(Statsample::Test.u_mannwhitney(@v1,@v2).u, @u.u) end should "have correct U values" do assert_equal(73,@u.r1) diff --git a/test/test_vector.rb b/test/test_vector.rb index 2a00252..03d3674 100644 --- a/test/test_vector.rb +++ b/test/test_vector.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleTestVector < MiniTest::Unit::TestCase +class StatsampleTestVector < Minitest::Test include Statsample::Shorthand def setup diff --git a/test/test_wilcoxonsignedrank.rb b/test/test_wilcoxonsignedrank.rb index f10b492..faf115a 100644 --- a/test/test_wilcoxonsignedrank.rb +++ b/test/test_wilcoxonsignedrank.rb @@ -1,6 +1,6 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleUMannWhitneyTestCase < MiniTest::Unit::TestCase +class StatsampleUMannWhitneyTestCase < Minitest::Test include Statsample::Test context Statsample::Test::WilcoxonSignedRank do context "Example 1" do @@ -9,8 +9,8 @@ class StatsampleUMannWhitneyTestCase < MiniTest::Unit::TestCase @v2=[125,115,130,140,140,115,140,125,140,135].to_scale @u=Statsample::Test::WilcoxonSignedRank.new(@v1,@v2) end - should "have same result using class or Test#u_mannwhitney" do - assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1,@v2).w, @u.w) + should "have same result using class or Test#u_mannwhitney" do + assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1,@v2).w, @u.w) end should "have correct W values" do assert_equal(9,@u.w) @@ -26,20 +26,20 @@ class StatsampleUMannWhitneyTestCase < MiniTest::Unit::TestCase end should "have correct value for probability_exact" do assert_in_delta(0.652,@u.probability_exact,0.001) - end + end should "have summary" do assert(@u.summary!="") - end + end end - + context "Example 2" do setup do @v2=[78,24,64,45,64,52,30,50,64,50,78,22,84,40,90,72].to_scale @v1=[78,24,62,48,68,56,25,44,56,40,68,36,68,20,58,32].to_scale @u=Statsample::Test::WilcoxonSignedRank.new(@v1,@v2) end - should "have same result using class or Test#u_mannwhitney" do - assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1,@v2).w, @u.w) + should "have same result using class or Test#u_mannwhitney" do + assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1,@v2).w, @u.w) end should "have correct W values" do assert_equal(67,@u.w) @@ -55,13 +55,13 @@ class StatsampleUMannWhitneyTestCase < MiniTest::Unit::TestCase end should "have correct value for probability_exact" do assert_in_delta(0.036,@u.probability_exact,0.001) - end + end should "have summary" do assert(@u.summary!="") - end + end end - - + + end - + end diff --git a/test/test_xls.rb b/test/test_xls.rb index 0a2584d..bc6728e 100644 --- a/test/test_xls.rb +++ b/test/test_xls.rb @@ -1,16 +1,16 @@ require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -class StatsampleExcelTestCase < MiniTest::Unit::TestCase +class StatsampleExcelTestCase < Minitest::Test context "Excel reader" do - setup do + setup do @ds=Statsample::Excel.read(File.dirname(__FILE__)+"/fixtures/test_xls.xls") end should "set the number of cases" do assert_equal(6,@ds.cases) end - should "set correct field names" do + should "set correct field names" do assert_equal(%w{id name age city a1},@ds.fields) end - should "set a dataset equal to expected" do + should "set a dataset equal to expected" do id=[1,2,3,4,5,6].to_vector(:scale) name=["Alex","Claude","Peter","Franz","George","Fernand"].to_vector(:nominal) age=[20,23,25,nil,5.5,nil].to_vector(:scale) @@ -22,12 +22,12 @@ class StatsampleExcelTestCase < MiniTest::Unit::TestCase } assert_equal(ds_exp,@ds) end - should "set to nil empty cells" do + should "set to nil empty cells" do assert_equal(nil,@ds['age'][5]) end end context "Excel writer" do - setup do + setup do a=100.times.map{rand(100)}.to_scale b=(["b"]*100).to_vector @ds={'b'=>b, 'a'=>a}.to_dataset(%w{b a}) @@ -46,7 +46,7 @@ class StatsampleExcelTestCase < MiniTest::Unit::TestCase @ds2.each_array do |row| assert_equal(@ds.case_as_array(i),row) i+=1 - end + end end end end From 019ea649d349b79074563faea06fedc4359d9078 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 11:48:51 -0300 Subject: [PATCH 016/119] Run rubocop --auto-correct against test/ --- test/helpers_tests.rb | 58 +- test/test_analysis.rb | 165 +++--- test/test_anova_contrast.rb | 42 +- test/test_anovaoneway.rb | 22 +- test/test_anovatwoway.rb | 31 +- test/test_anovatwowaywithdataset.rb | 44 +- test/test_anovawithvectors.rb | 130 +++-- test/test_awesome_print_bug.rb | 12 +- test/test_bartlettsphericity.rb | 24 +- test/test_bivariate.rb | 242 ++++---- test/test_codification.rb | 98 ++-- test/test_crosstab.rb | 80 +-- test/test_csv.rb | 128 ++--- test/test_dataset.rb | 667 +++++++++++----------- test/test_dominance_analysis.rb | 44 +- test/test_factor.rb | 309 +++++------ test/test_factor_map.rb | 49 +- test/test_factor_pa.rb | 50 +- test/test_ggobi.rb | 35 +- test/test_gsl.rb | 26 +- test/test_histogram.rb | 145 +++-- test/test_matrix.rb | 56 +- test/test_multiset.rb | 242 ++++---- test/test_regression.rb | 278 +++++----- test/test_reliability.rb | 284 +++++----- test/test_reliability_icc.rb | 176 +++--- test/test_reliability_skillscale.rb | 74 ++- test/test_resample.rb | 24 +- test/test_rserve_extension.rb | 66 +-- test/test_srs.rb | 8 +- test/test_statistics.rb | 100 ++-- test/test_stest.rb | 51 +- test/test_stratified.rb | 18 +- test/test_test_f.rb | 28 +- test/test_test_kolmogorovsmirnov.rb | 36 +- test/test_test_t.rb | 100 ++-- test/test_umannwhitney.rb | 30 +- test/test_vector.rb | 827 ++++++++++++++-------------- test/test_wilcoxonsignedrank.rb | 119 ++-- test/test_xls.rb | 64 +-- 40 files changed, 2489 insertions(+), 2493 deletions(-) diff --git a/test/helpers_tests.rb b/test/helpers_tests.rb index dcbe61a..85cff59 100644 --- a/test/helpers_tests.rb +++ b/test/helpers_tests.rb @@ -1,5 +1,5 @@ -$:.unshift(File.expand_path(File.dirname(__FILE__)+'/../lib/')) -$:.unshift(File.expand_path(File.dirname(__FILE__)+'/')) +$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib/')) +$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/')) require 'minitest' require 'minitest/unit' require 'mocha/setup' @@ -11,65 +11,65 @@ require 'statsample' - module Minitest class Test include Shoulda::Context::Assertions include Shoulda::Context::InstanceMethods extend Shoulda::Context::ClassMethods - def self.should_with_gsl(name,&block) + def self.should_with_gsl(name, &block) should(name) do if Statsample.has_gsl? instance_eval(&block) else - skip("Requires GSL") + skip('Requires GSL') end end end end module Assertions - def assert_similar_vector(exp, obs, delta=1e-10,msg=nil) - msg||="Different vectors #{exp} - #{obs}" + def assert_similar_vector(exp, obs, delta = 1e-10, msg = nil) + msg ||= "Different vectors #{exp} - #{obs}" assert_equal(exp.size, obs.size) - exp.data_with_nils.each_with_index {|v,i| - assert_in_delta(v,obs[i],delta) + exp.data_with_nils.each_with_index {|v, i| + assert_in_delta(v, obs[i], delta) } end - def assert_equal_vector(exp,obs,delta=1e-10,msg=nil) + + def assert_equal_vector(exp, obs, delta = 1e-10, msg = nil) assert_equal(exp.size, obs.size, "Different size.#{msg}") exp.size.times {|i| - assert_in_delta(exp[i],obs[i],delta, "Different element #{i}. \nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}") + assert_in_delta(exp[i], obs[i], delta, "Different element #{i}. \nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}") } end - def assert_equal_matrix(exp,obs,delta=1e-10,msg=nil) - assert_equal(exp.row_size, obs.row_size, "Different row size.#{msg}") - assert_equal(exp.column_size, obs.column_size, "Different column size.#{msg}") - exp.row_size.times {|i| - exp.column_size.times {|j| - assert_in_delta(exp[i,j],obs[i,j], delta, "Different element #{i},#{j}\nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}") - } - } + + def assert_equal_matrix(exp, obs, delta = 1e-10, msg = nil) + assert_equal(exp.row_size, obs.row_size, "Different row size.#{msg}") + assert_equal(exp.column_size, obs.column_size, "Different column size.#{msg}") + exp.row_size.times {|i| + exp.column_size.times {|j| + assert_in_delta(exp[i, j], obs[i, j], delta, "Different element #{i},#{j}\nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}") + } + } end - alias :assert_raise :assert_raises unless method_defined? :assert_raise - alias :assert_not_equal :refute_equal unless method_defined? :assert_not_equal - alias :assert_not_same :refute_same unless method_defined? :assert_not_same + alias_method :assert_raise, :assert_raises unless method_defined? :assert_raise + alias_method :assert_not_equal, :refute_equal unless method_defined? :assert_not_equal + alias_method :assert_not_same, :refute_same unless method_defined? :assert_not_same unless method_defined? :assert_nothing_raised - def assert_nothing_raised(msg=nil) - msg||="Nothing should be raised, but raised %s" + def assert_nothing_raised(msg = nil) + msg ||= 'Nothing should be raised, but raised %s' begin yield - not_raised=true + not_raised = true rescue Exception => e - not_raised=false - msg=sprintf(msg,e) + not_raised = false + msg = sprintf(msg, e) end - assert(not_raised,msg) + assert(not_raised, msg) end end end end MiniTest.autorun - diff --git a/test/test_analysis.rb b/test/test_analysis.rb index 70886ff..55d5b6f 100644 --- a/test/test_analysis.rb +++ b/test/test_analysis.rb @@ -1,60 +1,60 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleAnalysisTestCase < Minitest::Test context(Statsample::Analysis) do setup do Statsample::Analysis.clear_analysis end - should "store() should create and store Statsample::Analysis::Suite" do + should 'store() should create and store Statsample::Analysis::Suite' do Statsample::Analysis.store(:first) do - a=1 + a = 1 end assert(Statsample::Analysis.stored_analysis[:first]) assert(Statsample::Analysis.stored_analysis[:first].is_a? Statsample::Analysis::Suite) end - should "ss_analysis should create an Statsample::Analysis" do - ss_analysis(:first) {a=1} + should 'ss_analysis should create an Statsample::Analysis' do + ss_analysis(:first) { a = 1 } end - should "store last created analysis" do - an=Statsample::Analysis.store(:first) do - a=1 + should 'store last created analysis' do + an = Statsample::Analysis.store(:first) do + a = 1 end - assert_equal(an,Statsample::Analysis.last) + assert_equal(an, Statsample::Analysis.last) end - should "add_to_reportbuilder() add sections to reportbuilder object" do - rb=mock() - rb.expects(:add).with {|value| value.is_a? ReportBuilder::Section and value.name==:first} - rb.expects(:add).with {|value| value.is_a? ReportBuilder::Section and value.name==:second} + should 'add_to_reportbuilder() add sections to reportbuilder object' do + rb = mock + rb.expects(:add).with { |value| value.is_a? ReportBuilder::Section and value.name == :first } + rb.expects(:add).with { |value| value.is_a? ReportBuilder::Section and value.name == :second } Statsample::Analysis.store(:first) do - echo "first","second" + echo 'first', 'second' end Statsample::Analysis.store(:second) do - echo "third" + echo 'third' end - Statsample::Analysis.add_to_reportbuilder(rb,:first,:second) + Statsample::Analysis.add_to_reportbuilder(rb, :first, :second) end - should "to_text returns the same as a normal ReportBuilder object" do - rb=ReportBuilder.new(:name=>:test) - section=ReportBuilder::Section.new(:name=>"first") - a=[1,2,3].to_scale - section.add("first") + should 'to_text returns the same as a normal ReportBuilder object' do + rb = ReportBuilder.new(name: :test) + section = ReportBuilder::Section.new(name: 'first') + a = [1, 2, 3].to_scale + section.add('first') section.add(a) rb.add(section) - exp=rb.to_text - an=ss_analysis(:first) { + exp = rb.to_text + an = ss_analysis(:first) { echo 'first' summary(a) } - obs=Statsample::Analysis.to_text(:first) + obs = Statsample::Analysis.to_text(:first) - assert_equal(exp.split("\n")[1,exp.size], obs.split("\n")[1,obs.size]) + assert_equal(exp.split("\n")[1, exp.size], obs.split("\n")[1, obs.size]) end - should "run() execute all analysis by default" do - m1=mock() + should 'run() execute all analysis by default' do + m1 = mock m1.expects(:run).once m1.expects(:hide).once @@ -69,8 +69,8 @@ class StatsampleAnalysisTestCase < Minitest::Test Statsample::Analysis.run end - should "run() execute blocks specificed on parameters" do - m1=mock() + should 'run() execute blocks specificed on parameters' do + m1 = mock m1.expects(:run).once m1.expects(:hide).never Statsample::Analysis.store(:first) do @@ -84,76 +84,76 @@ class StatsampleAnalysisTestCase < Minitest::Test end context(Statsample::Analysis::Suite) do - should "echo() uses output#puts with same arguments" do - an=Statsample::Analysis::Suite.new(:output) - obj=mock() - obj.expects(:puts).with(:first,:second).once - an.output=obj - an.echo(:first,:second) - end - should "summary() should call object.summary" do - an=Statsample::Analysis::Suite.new(:summary) - obj=stub('summarizable',:summary=>'summary') - assert_equal(obj.summary,an.summary(obj)) - end - should "attach() allows to call objects on objects which respond to fields" do - an=Statsample::Analysis::Suite.new(:summary) - ds={'x'=>stub(:mean=>10),'y'=>stub(:mean=>12)} - ds.expects(:fields).returns(%w{x y}).at_least_once + should 'echo() uses output#puts with same arguments' do + an = Statsample::Analysis::Suite.new(:output) + obj = mock + obj.expects(:puts).with(:first, :second).once + an.output = obj + an.echo(:first, :second) + end + should 'summary() should call object.summary' do + an = Statsample::Analysis::Suite.new(:summary) + obj = stub('summarizable', summary: 'summary') + assert_equal(obj.summary, an.summary(obj)) + end + should 'attach() allows to call objects on objects which respond to fields' do + an = Statsample::Analysis::Suite.new(:summary) + ds = { 'x' => stub(mean: 10), 'y' => stub(mean: 12) } + ds.expects(:fields).returns(%w(x y)).at_least_once an.attach(ds) - assert_equal(10,an.x.mean) - assert_equal(12,an.y.mean) + assert_equal(10, an.x.mean) + assert_equal(12, an.y.mean) assert_raise(RuntimeError) { an.z } end - should "attached objects should be called LIFO" do - an=Statsample::Analysis::Suite.new(:summary) - ds1={'x'=>stub(:mean=>100),'y'=>stub(:mean=>120),'z'=>stub(:mean=>13)} - ds1.expects(:fields).returns(%w{x y z}).at_least_once - ds2={'x'=>stub(:mean=>10),'y'=>stub(:mean=>12)} - ds2.expects(:fields).returns(%w{x y}).at_least_once + should 'attached objects should be called LIFO' do + an = Statsample::Analysis::Suite.new(:summary) + ds1 = { 'x' => stub(mean: 100), 'y' => stub(mean: 120), 'z' => stub(mean: 13) } + ds1.expects(:fields).returns(%w(x y z)).at_least_once + ds2 = { 'x' => stub(mean: 10), 'y' => stub(mean: 12) } + ds2.expects(:fields).returns(%w(x y)).at_least_once an.attach(ds1) an.attach(ds2) - assert_equal(10,an.x.mean) - assert_equal(12,an.y.mean) - assert_equal(13,an.z.mean) + assert_equal(10, an.x.mean) + assert_equal(12, an.y.mean) + assert_equal(13, an.z.mean) end - should "detach() without arguments drop latest object" do - an=Statsample::Analysis::Suite.new(:summary) - ds1={'x'=>stub(:mean=>100),'y'=>stub(:mean=>120),'z'=>stub(:mean=>13)} - ds1.expects(:fields).returns(%w{x y z}).at_least_once - ds2={'x'=>stub(:mean=>10),'y'=>stub(:mean=>12)} - ds2.expects(:fields).returns(%w{x y}).at_least_once + should 'detach() without arguments drop latest object' do + an = Statsample::Analysis::Suite.new(:summary) + ds1 = { 'x' => stub(mean: 100), 'y' => stub(mean: 120), 'z' => stub(mean: 13) } + ds1.expects(:fields).returns(%w(x y z)).at_least_once + ds2 = { 'x' => stub(mean: 10), 'y' => stub(mean: 12) } + ds2.expects(:fields).returns(%w(x y)).at_least_once an.attach(ds1) an.attach(ds2) - assert_equal(10,an.x.mean) + assert_equal(10, an.x.mean) an.detach assert_equal(100, an.x.mean) end - should "detach() with argument drop select object" do - an=Statsample::Analysis::Suite.new(:summary) - ds1={'x'=>1} - ds1.expects(:fields).returns(%w{x}).at_least_once - ds2={'x'=>2,'y'=>3} - ds2.expects(:fields).returns(%w{x y}).at_least_once - ds3={'y'=>4} - ds3.expects(:fields).returns(%w{y}).at_least_once + should 'detach() with argument drop select object' do + an = Statsample::Analysis::Suite.new(:summary) + ds1 = { 'x' => 1 } + ds1.expects(:fields).returns(%w(x)).at_least_once + ds2 = { 'x' => 2, 'y' => 3 } + ds2.expects(:fields).returns(%w(x y)).at_least_once + ds3 = { 'y' => 4 } + ds3.expects(:fields).returns(%w(y)).at_least_once an.attach(ds3) an.attach(ds2) an.attach(ds1) - assert_equal(1,an.x) - assert_equal(3,an.y) + assert_equal(1, an.x) + assert_equal(3, an.y) an.detach(ds2) - assert_equal(4,an.y) + assert_equal(4, an.y) end - should "perform a simple analysis" do - output=mock() + should 'perform a simple analysis' do + output = mock output.expects(:puts).with(5.5) - an=Statsample::Analysis.store(:simple, :output=>output) do - ds=data_frame(:x=>vector(1..10),:y=>vector(1..10)) + an = Statsample::Analysis.store(:simple, output: output) do + ds = data_frame(x: vector(1..10), y: vector(1..10)) attach(ds) echo x.mean end @@ -161,17 +161,16 @@ class StatsampleAnalysisTestCase < Minitest::Test end end context(Statsample::Analysis::SuiteReportBuilder) do - should "echo() use add on rb object" do - an=Statsample::Analysis::SuiteReportBuilder.new(:puts_to_add) + should 'echo() use add on rb object' do + an = Statsample::Analysis::SuiteReportBuilder.new(:puts_to_add) an.rb.expects(:add).with(:first).twice an.echo(:first, :first) end - should "summary() uses add on rb object" do - an=Statsample::Analysis::SuiteReportBuilder.new(:summary_to_add) + should 'summary() uses add on rb object' do + an = Statsample::Analysis::SuiteReportBuilder.new(:summary_to_add) an.rb.expects(:add).with(:first).once an.summary(:first) end end - end end diff --git a/test/test_anova_contrast.rb b/test/test_anova_contrast.rb index 5b823fc..f86ae0f 100644 --- a/test/test_anova_contrast.rb +++ b/test/test_anova_contrast.rb @@ -1,36 +1,36 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleAnovaContrastTestCase < Minitest::Test context(Statsample::Anova::Contrast) do setup do - constant=[12,13,11,12,12].to_scale - frequent=[9,10,9,13,14].to_scale - infrequent=[15,16,17,16,16].to_scale - never=[17,18,12,18,20].to_scale - @vectors=[constant, frequent, infrequent, never] - @c=Statsample::Anova::Contrast.new(:vectors=>@vectors) + constant = [12, 13, 11, 12, 12].to_scale + frequent = [9, 10, 9, 13, 14].to_scale + infrequent = [15, 16, 17, 16, 16].to_scale + never = [17, 18, 12, 18, 20].to_scale + @vectors = [constant, frequent, infrequent, never] + @c = Statsample::Anova::Contrast.new(vectors: @vectors) end - should "return correct value using c" do - @c.c([1,-1.quo(3),-1.quo(3),-1.quo(3)]) - #@c.c([1,-0.333,-0.333,-0.333]) + should 'return correct value using c' do + @c.c([1, -1.quo(3), -1.quo(3), -1.quo(3)]) + # @c.c([1,-0.333,-0.333,-0.333]) assert_in_delta(-2.6667, @c.psi, 0.0001) assert_in_delta(1.0165, @c.se, 0.0001) assert_in_delta(-2.623, @c.t, 0.001) - assert_in_delta(-4.82, @c.confidence_interval[0],0.01) - assert_in_delta(-0.51, @c.confidence_interval[1],0.01) - assert(@c.summary.size>0) + assert_in_delta(-4.82, @c.confidence_interval[0], 0.01) + assert_in_delta(-0.51, @c.confidence_interval[1], 0.01) + assert(@c.summary.size > 0) end - should "return correct values using c_by_index" do - @c.c_by_index([0],[1,2,3]) + should 'return correct values using c_by_index' do + @c.c_by_index([0], [1, 2, 3]) assert_in_delta(-2.6667, @c.psi, 0.0001) assert_in_delta(1.0165, @c.se, 0.0001) assert_in_delta(-2.623, @c.t, 0.001) end - should "return correct values using incomplete c_by_index" do - c1=Statsample::Anova::Contrast.new(:vectors=>@vectors, :c=>[0.5,0.5,-1,0]) - c2=Statsample::Anova::Contrast.new(:vectors=>@vectors, :c1=>[0,1],:c2=>[2]) - assert_equal(c1.psi,c2.psi) - assert_equal(c1.se,c2.se) - assert_equal(c1.t,c2.t) + should 'return correct values using incomplete c_by_index' do + c1 = Statsample::Anova::Contrast.new(vectors: @vectors, c: [0.5, 0.5, -1, 0]) + c2 = Statsample::Anova::Contrast.new(vectors: @vectors, c1: [0, 1], c2: [2]) + assert_equal(c1.psi, c2.psi) + assert_equal(c1.se, c2.se) + assert_equal(c1.t, c2.t) end end end diff --git a/test/test_anovaoneway.rb b/test/test_anovaoneway.rb index 9510cb5..17c86cb 100644 --- a/test/test_anovaoneway.rb +++ b/test/test_anovaoneway.rb @@ -1,26 +1,26 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleAnovaOneWayTestCase < Minitest::Test context(Statsample::Anova::OneWay) do setup do - @ss_num=30.08 - @ss_den=87.88 - @df_num=2 - @df_den=21 - @anova=Statsample::Anova::OneWay.new(:ss_num=>@ss_num, :ss_den=>@ss_den, :df_num=>@df_num, :df_den=>@df_den) + @ss_num = 30.08 + @ss_den = 87.88 + @df_num = 2 + @df_den = 21 + @anova = Statsample::Anova::OneWay.new(ss_num: @ss_num, ss_den: @ss_den, df_num: @df_num, df_den: @df_den) end - should "Statsample::Anova.oneway respond to #oneway" do + should 'Statsample::Anova.oneway respond to #oneway' do assert(Statsample::Anova.respond_to? :oneway) end - should "return correct value for ms_num and ms_den" do + should 'return correct value for ms_num and ms_den' do assert_in_delta(15.04, @anova.ms_num, 0.01) assert_in_delta(4.18, @anova.ms_den, 0.01) end - should "return correct value for f" do + should 'return correct value for f' do assert_in_delta(3.59, @anova.f, 0.01) end - should "respond to summary" do + should 'respond to summary' do assert(@anova.respond_to? :summary) - assert(@anova.summary.size>0) + assert(@anova.summary.size > 0) end end end diff --git a/test/test_anovatwoway.rb b/test/test_anovatwoway.rb index 87813e6..db110c4 100644 --- a/test/test_anovatwoway.rb +++ b/test/test_anovatwoway.rb @@ -1,38 +1,37 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleAnovaTwoWayTestCase < Minitest::Test context(Statsample::Anova::TwoWay) do setup do - @ss_a=192.2 - @ss_b=57.8 - @ss_axb=168.2 - @ss_within=75.6 - @df_a=@df_b=1 - @df_within=16 - @anova=Statsample::Anova::TwoWay.new(:ss_a=>@ss_a, :ss_b=>@ss_b, :ss_axb=>@ss_axb, :ss_within=>@ss_within , :df_a=>@df_a, :df_b=>@df_b, :df_within=>@df_within) + @ss_a = 192.2 + @ss_b = 57.8 + @ss_axb = 168.2 + @ss_within = 75.6 + @df_a = @df_b = 1 + @df_within = 16 + @anova = Statsample::Anova::TwoWay.new(ss_a: @ss_a, ss_b: @ss_b, ss_axb: @ss_axb, ss_within: @ss_within, df_a: @df_a, df_b: @df_b, df_within: @df_within) end - should "Statsample::Anova.twoway respond to #twoway" do - assert(Statsample::Anova.respond_to? :twoway) + should 'Statsample::Anova.twoway respond to #twoway' do + assert(Statsample::Anova.respond_to? :twoway) end - should "return correct value for ms_a, ms_b and ms_axb" do + should 'return correct value for ms_a, ms_b and ms_axb' do assert_in_delta(192.2, @anova.ms_a, 0.01) assert_in_delta(57.8, @anova.ms_b, 0.01) assert_in_delta(168.2, @anova.ms_axb, 0.01) - end - should "return correct value for f " do + should 'return correct value for f ' do assert_in_delta(40.68, @anova.f_a, 0.01) assert_in_delta(12.23, @anova.f_b, 0.01) assert_in_delta(35.60, @anova.f_axb, 0.01) end - should "return correct value for probability for f " do + should 'return correct value for probability for f ' do assert(@anova.f_a_probability < 0.05) assert(@anova.f_b_probability < 0.05) assert(@anova.f_axb_probability < 0.05) end - should "respond to summary" do + should 'respond to summary' do assert(@anova.respond_to? :summary) - assert(@anova.summary.size>0) + assert(@anova.summary.size > 0) end end end diff --git a/test/test_anovatwowaywithdataset.rb b/test/test_anovatwowaywithdataset.rb index 0ba26d7..a9eecb7 100644 --- a/test/test_anovatwowaywithdataset.rb +++ b/test/test_anovatwowaywithdataset.rb @@ -1,49 +1,47 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) # Reference: # * http://www.uwsp.edu/psych/Stat/13/anova-2w.htm#III class StatsampleAnovaTwoWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::TwoWayWithVectors) do setup do - @pa=[5,4,3,4,2,18,19,14,12,15,6,7,5,8,4,6,9,5,9,3].to_scale - @pa.name="Passive Avoidance" - @a=[0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1].to_vector - @a.labels={0=>'0%',1=>'35%'} - @a.name='Diet' - @b=[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1].to_vector - @b.labels={0=>'Young',1=>'Older'} - @b.name="Age" - @anova=Statsample::Anova::TwoWayWithVectors.new(:a=>@a,:b=>@b, :dependent=>@pa) + @pa = [5, 4, 3, 4, 2, 18, 19, 14, 12, 15, 6, 7, 5, 8, 4, 6, 9, 5, 9, 3].to_scale + @pa.name = 'Passive Avoidance' + @a = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1].to_vector + @a.labels = { 0 => '0%', 1 => '35%' } + @a.name = 'Diet' + @b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].to_vector + @b.labels = { 0 => 'Young', 1 => 'Older' } + @b.name = 'Age' + @anova = Statsample::Anova::TwoWayWithVectors.new(a: @a, b: @b, dependent: @pa) end - should "Statsample::Anova respond to #twoway_with_vectors" do - assert(Statsample::Anova.respond_to? :twoway_with_vectors) + should 'Statsample::Anova respond to #twoway_with_vectors' do + assert(Statsample::Anova.respond_to? :twoway_with_vectors) end - should "#new returns the same as Statsample::Anova.twoway_with_vectors" do - @anova2=Statsample::Anova.twoway_with_vectors(:a=>@a,:b=>@b, :dependent=>@pa) + should '#new returns the same as Statsample::Anova.twoway_with_vectors' do + @anova2 = Statsample::Anova.twoway_with_vectors(a: @a, b: @b, dependent: @pa) assert_equal(@anova.summary, @anova2.summary) end - should "return correct value for ms_a, ms_b and ms_axb" do + should 'return correct value for ms_a, ms_b and ms_axb' do assert_in_delta(192.2, @anova.ms_a, 0.01) assert_in_delta(57.8, @anova.ms_b, 0.01) assert_in_delta(168.2, @anova.ms_axb, 0.01) - end - should "return correct value for f " do + should 'return correct value for f ' do assert_in_delta(40.68, @anova.f_a, 0.01) assert_in_delta(12.23, @anova.f_b, 0.01) assert_in_delta(35.60, @anova.f_axb, 0.01) end - should "return correct value for probability for f " do + should 'return correct value for probability for f ' do assert(@anova.f_a_probability < 0.05) assert(@anova.f_b_probability < 0.05) assert(@anova.f_axb_probability < 0.05) end - should "respond to summary" do - - @anova.summary_descriptives=true - @anova.summary_levene=true + should 'respond to summary' do + @anova.summary_descriptives = true + @anova.summary_levene = true assert(@anova.respond_to? :summary) - assert(@anova.summary.size>0) + assert(@anova.summary.size > 0) end end end diff --git a/test/test_anovawithvectors.rb b/test/test_anovawithvectors.rb index 2a9269f..8793db8 100644 --- a/test/test_anovawithvectors.rb +++ b/test/test_anovawithvectors.rb @@ -1,102 +1,100 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::OneWayWithVectors) do - - context("when initializing") do + context('when initializing') do setup do - @v1=10.times.map {rand(100)}.to_scale - @v2=10.times.map {rand(100)}.to_scale - @v3=10.times.map {rand(100)}.to_scale + @v1 = 10.times.map { rand(100) }.to_scale + @v2 = 10.times.map { rand(100) }.to_scale + @v3 = 10.times.map { rand(100) }.to_scale end - should "be the same using [] or args*" do - a1=Statsample::Anova::OneWayWithVectors.new(@v1,@v2,@v3) - a2=Statsample::Anova::OneWayWithVectors.new([@v1,@v2,@v3]) - assert_equal(a1.f,a2.f) + should 'be the same using [] or args*' do + a1 = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3) + a2 = Statsample::Anova::OneWayWithVectors.new([@v1, @v2, @v3]) + assert_equal(a1.f, a2.f) end - should "be the same using module method or object instantiation" do - a1=Statsample::Anova::OneWayWithVectors.new(@v1,@v2,@v3) - a2=Statsample::Anova.oneway_with_vectors(@v1,@v2,@v3) - assert_equal(a1.f,a2.f) + should 'be the same using module method or object instantiation' do + a1 = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3) + a2 = Statsample::Anova.oneway_with_vectors(@v1, @v2, @v3) + assert_equal(a1.f, a2.f) end - should "detect optional hash" do - a1=Statsample::Anova::OneWayWithVectors.new(@v1,@v2,@v3, {:name=>'aaa'}) + should 'detect optional hash' do + a1 = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3, name: 'aaa') assert_equal('aaa', a1.name) end - should "omit incorrect arguments" do - a1=Statsample::Anova::OneWayWithVectors.new(@v1,@v2,@v3, {:name=>'aaa'}) - a2=Statsample::Anova::OneWayWithVectors.new(@v1,nil,nil,@v2,@v3, {:name=>'aaa'}) - assert_equal(a1.f,a2.f) + should 'omit incorrect arguments' do + a1 = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3, name: 'aaa') + a2 = Statsample::Anova::OneWayWithVectors.new(@v1, nil, nil, @v2, @v3, name: 'aaa') + assert_equal(a1.f, a2.f) end end setup do - @v1=[3,3,2,3,6].to_vector(:scale) - @v2=[7,6,5,6,7].to_vector(:scale) - @v3=[9,8,9,7,8].to_vector(:scale) - @name="Anova testing" - @anova=Statsample::Anova::OneWayWithVectors.new(@v1,@v2,@v3, :name=>@name) - end - should "store correctly contrasts" do - c1=Statsample::Anova::Contrast.new(:vectors=>[@v1,@v2,@v3], :c=>[1,-0.5, -0.5]) - - c2=@anova.contrast(:c=>[1,-0.5,-0.5]) - assert_equal(c1.t,c2.t) + @v1 = [3, 3, 2, 3, 6].to_vector(:scale) + @v2 = [7, 6, 5, 6, 7].to_vector(:scale) + @v3 = [9, 8, 9, 7, 8].to_vector(:scale) + @name = 'Anova testing' + @anova = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3, name: @name) + end + should 'store correctly contrasts' do + c1 = Statsample::Anova::Contrast.new(vectors: [@v1, @v2, @v3], c: [1, -0.5, -0.5]) + c2 = @anova.contrast(c: [1, -0.5, -0.5]) + assert_equal(c1.t, c2.t) end - should "respond to #summary" do + should 'respond to #summary' do assert(@anova.respond_to? :summary) end - should "have correct name of analysis on #summary" do + should 'have correct name of analysis on #summary' do assert_match(/#{@name}/, @anova.summary) end - should "returns same levene values as direct Levene creation" do - assert_equal(@anova.levene.f, Statsample::Test.levene([@v1,@v2,@v3]).f) + should 'returns same levene values as direct Levene creation' do + assert_equal(@anova.levene.f, Statsample::Test.levene([@v1, @v2, @v3]).f) end - should "have correct value for levene" do - assert_in_delta(0.604,@anova.levene.f, 0.001) - assert_in_delta(0.562,@anova.levene.probability, 0.001) + should 'have correct value for levene' do + assert_in_delta(0.604, @anova.levene.f, 0.001) + assert_in_delta(0.562, @anova.levene.probability, 0.001) end - should "have correct value for sst" do - assert_in_delta(72.933, @anova.sst,0.001) + should 'have correct value for sst' do + assert_in_delta(72.933, @anova.sst, 0.001) end - should "have correct value for sswg" do - assert_in_delta(14.8,@anova.sswg,0.001) + should 'have correct value for sswg' do + assert_in_delta(14.8, @anova.sswg, 0.001) end - should "have correct value for ssb" do - assert_in_delta(58.133,@anova.ssbg,0.001) + should 'have correct value for ssb' do + assert_in_delta(58.133, @anova.ssbg, 0.001) end - should "sst=sswg+ssbg" do - assert_in_delta(@anova.sst,@anova.sswg+@anova.ssbg,0.00001) + should 'sst=sswg+ssbg' do + assert_in_delta(@anova.sst, @anova.sswg + @anova.ssbg, 0.00001) end - should "df total equal to number of n-1" do - assert_equal(@v1.n+@v2.n+@v3.n-1,@anova.df_total) + should 'df total equal to number of n-1' do + assert_equal(@v1.n + @v2.n + @v3.n - 1, @anova.df_total) end - should "df wg equal to number of n-k" do - assert_equal(@v1.n+@v2.n+@v3.n-3,@anova.df_wg) + should 'df wg equal to number of n-k' do + assert_equal(@v1.n + @v2.n + @v3.n - 3, @anova.df_wg) end - should "df bg equal to number of k-1" do - assert_equal(2,@anova.df_bg) + should 'df bg equal to number of k-1' do + assert_equal(2, @anova.df_bg) end - should "f=(ssbg/df_bg)/(sswt/df_wt)" do - assert_in_delta((@anova.ssbg.quo(@anova.df_bg)).quo( @anova.sswg.quo(@anova.df_wg)), @anova.f, 0.001) + should 'f=(ssbg/df_bg)/(sswt/df_wt)' do + assert_in_delta((@anova.ssbg.quo(@anova.df_bg)).quo(@anova.sswg.quo(@anova.df_wg)), @anova.f, 0.001) end - should "p be correct" do - assert(@anova.probability<0.01) + should 'p be correct' do + assert(@anova.probability < 0.01) end - should "be correct using different test values" do - anova2=Statsample::Anova::OneWayWithVectors.new([@v1,@v1,@v1,@v1,@v2]) - assert_in_delta(3.960, anova2.f,0.001) - assert_in_delta(0.016, anova2.probability,0.001) + should 'be correct using different test values' do + anova2 = Statsample::Anova::OneWayWithVectors.new([@v1, @v1, @v1, @v1, @v2]) + assert_in_delta(3.960, anova2.f, 0.001) + assert_in_delta(0.016, anova2.probability, 0.001) end - context "with extra information on summary" do + context 'with extra information on summary' do setup do - @anova.summary_descriptives=true - @anova.summary_levene=true - @summary=@anova.summary + @anova.summary_descriptives = true + @anova.summary_levene = true + @summary = @anova.summary end - should "have section with levene statistics" do + should 'have section with levene statistics' do assert_match(/Levene/, @summary) end - should "have section with descriptives" do + should 'have section with descriptives' do assert_match(/Min/, @summary) end end diff --git a/test/test_awesome_print_bug.rb b/test/test_awesome_print_bug.rb index 0996b68..f6db9f6 100644 --- a/test/test_awesome_print_bug.rb +++ b/test/test_awesome_print_bug.rb @@ -1,13 +1,13 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleAwesomePrintBug < Minitest::Test - context("Awesome Print integration") do + context('Awesome Print integration') do setup do - require "awesome_print" + require 'awesome_print' end - should "should be flawless" do - a=[1,2,3].to_scale + should 'should be flawless' do + a = [1, 2, 3].to_scale - assert(a!=[1,2,3]) + assert(a != [1, 2, 3]) assert_nothing_raised do ap a end diff --git a/test/test_bartlettsphericity.rb b/test/test_bartlettsphericity.rb index 4b90ebe..9a65b08 100644 --- a/test/test_bartlettsphericity.rb +++ b/test/test_bartlettsphericity.rb @@ -1,25 +1,25 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleBartlettSphericityTestCase < Minitest::Test include Statsample::Test context Statsample::Test::BartlettSphericity do setup do - @v1=[1 ,2 ,3 ,4 ,7 ,8 ,9 ,10,14,15,20,50,60,70].to_scale - @v2=[5 ,6 ,11,12,13,16,17,18,19,20,30,0,0,0].to_scale - @v3=[10,3 ,20,30,40,50,80,10,20,30,40,2,3,4].to_scale + @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_scale + @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_scale + @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_scale # KMO: 0.490 - ds={'v1'=>@v1,'v2'=>@v2,'v3'=>@v3}.to_dataset - cor=Statsample::Bivariate.correlation_matrix(ds) - @bs=Statsample::Test::BartlettSphericity.new(cor, 14) + ds = { 'v1' => @v1, 'v2' => @v2, 'v3' => @v3 }.to_dataset + cor = Statsample::Bivariate.correlation_matrix(ds) + @bs = Statsample::Test::BartlettSphericity.new(cor, 14) end - should "have correct value for chi" do - assert_in_delta(9.477, @bs.value,0.001) + should 'have correct value for chi' do + assert_in_delta(9.477, @bs.value, 0.001) end - should "have correct value for df" do + should 'have correct value for df' do assert_equal(3, @bs.df) end - should "have correct value for probability" do - assert_in_delta(0.024,@bs.probability,0.001) + should 'have correct value for probability' do + assert_in_delta(0.024, @bs.probability, 0.001) end end end diff --git a/test/test_bivariate.rb b/test/test_bivariate.rb index 14906c8..be95370 100644 --- a/test/test_bivariate.rb +++ b/test/test_bivariate.rb @@ -1,163 +1,159 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleBivariateTestCase < Minitest::Test - should "method sum of squares should be correct" do - v1=[1,2,3,4,5,6].to_vector(:scale) - v2=[6,2,4,10,12,8].to_vector(:scale) - assert_equal(23.0, Statsample::Bivariate.sum_of_squares(v1,v2)) + should 'method sum of squares should be correct' do + v1 = [1, 2, 3, 4, 5, 6].to_vector(:scale) + v2 = [6, 2, 4, 10, 12, 8].to_vector(:scale) + assert_equal(23.0, Statsample::Bivariate.sum_of_squares(v1, v2)) end - should_with_gsl "return same covariance with ruby and gls implementation" do - v1=20.times.collect {|a| rand()}.to_scale - v2=20.times.collect {|a| rand()}.to_scale - assert_in_delta(Statsample::Bivariate.covariance(v1,v2), Statsample::Bivariate.covariance_slow(v1,v2), 0.001) + should_with_gsl 'return same covariance with ruby and gls implementation' do + v1 = 20.times.collect { |_a| rand }.to_scale + v2 = 20.times.collect { |_a| rand }.to_scale + assert_in_delta(Statsample::Bivariate.covariance(v1, v2), Statsample::Bivariate.covariance_slow(v1, v2), 0.001) end - should_with_gsl "return same correlation with ruby and gls implementation" do - v1=20.times.collect {|a| rand()}.to_scale - v2=20.times.collect {|a| rand()}.to_scale + should_with_gsl 'return same correlation with ruby and gls implementation' do + v1 = 20.times.collect { |_a| rand }.to_scale + v2 = 20.times.collect { |_a| rand }.to_scale - assert_in_delta(GSL::Stats::correlation(v1.gsl, v2.gsl), Statsample::Bivariate.pearson_slow(v1,v2), 1e-10) + assert_in_delta(GSL::Stats.correlation(v1.gsl, v2.gsl), Statsample::Bivariate.pearson_slow(v1, v2), 1e-10) end - should "return correct pearson correlation" do - v1=[6,5,4,7,8,4,3,2].to_vector(:scale) - v2=[2,3,7,8,6,4,3,2].to_vector(:scale) - assert_in_delta(0.525,Statsample::Bivariate.pearson(v1,v2), 0.001) - assert_in_delta(0.525,Statsample::Bivariate.pearson_slow(v1,v2), 0.001) - - v3=[6,2, 1000,1000,5,4,7,8,4,3,2,nil].to_vector(:scale) - v4=[2,nil,nil,nil, 3,7,8,6,4,3,2,500].to_vector(:scale) - assert_in_delta(0.525,Statsample::Bivariate.pearson(v3,v4),0.001) + should 'return correct pearson correlation' do + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) + assert_in_delta(0.525, Statsample::Bivariate.pearson(v1, v2), 0.001) + assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v1, v2), 0.001) + + v3 = [6, 2, 1000, 1000, 5, 4, 7, 8, 4, 3, 2, nil].to_vector(:scale) + v4 = [2, nil, nil, nil, 3, 7, 8, 6, 4, 3, 2, 500].to_vector(:scale) + assert_in_delta(0.525, Statsample::Bivariate.pearson(v3, v4), 0.001) # Test ruby method - v3a,v4a=Statsample.only_valid v3, v4 - assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v3a,v4a),0.001) + v3a, v4a = Statsample.only_valid v3, v4 + assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v3a, v4a), 0.001) end - should "return correct values for t_pearson and prop_pearson" do - v1=[6,5,4,7,8,4,3,2].to_vector(:scale) - v2=[2,3,7,8,6,4,3,2].to_vector(:scale) - r=Statsample::Bivariate::Pearson.new(v1,v2) - assert_in_delta(0.525,r.r, 0.001) - assert_in_delta(Statsample::Bivariate.t_pearson(v1,v2), r.t, 0.001) - assert_in_delta(Statsample::Bivariate.prop_pearson(r.t,8,:both), r.probability, 0.001) - assert(r.summary.size>0) + should 'return correct values for t_pearson and prop_pearson' do + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) + r = Statsample::Bivariate::Pearson.new(v1, v2) + assert_in_delta(0.525, r.r, 0.001) + assert_in_delta(Statsample::Bivariate.t_pearson(v1, v2), r.t, 0.001) + assert_in_delta(Statsample::Bivariate.prop_pearson(r.t, 8, :both), r.probability, 0.001) + assert(r.summary.size > 0) end - should "return correct correlation_matrix with nils values" do - v1=[6,5,4,7,8,4,3,2].to_vector(:scale) - v2=[2,3,7,8,6,4,3,2].to_vector(:scale) - v3=[6,2, 1000,1000,5,4,7,8].to_vector(:scale) - v4=[2,nil,nil,nil, 3,7,8,6].to_vector(:scale) - ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4}.to_dataset - c=Proc.new {|n1,n2|Statsample::Bivariate.pearson(n1,n2)} - expected=Matrix[ [c.call(v1,v1),c.call(v1,v2),c.call(v1,v3),c.call(v1,v4)], [c.call(v2,v1),c.call(v2,v2),c.call(v2,v3),c.call(v2,v4)], [c.call(v3,v1),c.call(v3,v2),c.call(v3,v3),c.call(v3,v4)], - [c.call(v4,v1),c.call(v4,v2),c.call(v4,v3),c.call(v4,v4)] + should 'return correct correlation_matrix with nils values' do + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) + v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:scale) + v4 = [2, nil, nil, nil, 3, 7, 8, 6].to_vector(:scale) + ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4 }.to_dataset + c = proc { |n1, n2| Statsample::Bivariate.pearson(n1, n2) } + expected = Matrix[[c.call(v1, v1), c.call(v1, v2), c.call(v1, v3), c.call(v1, v4)], [c.call(v2, v1), c.call(v2, v2), c.call(v2, v3), c.call(v2, v4)], [c.call(v3, v1), c.call(v3, v2), c.call(v3, v3), c.call(v3, v4)], + [c.call(v4, v1), c.call(v4, v2), c.call(v4, v3), c.call(v4, v4)] ] - obt=Statsample::Bivariate.correlation_matrix(ds) + obt = Statsample::Bivariate.correlation_matrix(ds) for i in 0...expected.row_size for j in 0...expected.column_size - #puts expected[i,j].inspect - #puts obt[i,j].inspect - assert_in_delta(expected[i,j], obt[i,j],0.0001, "#{expected[i,j].class}!=#{obt[i,j].class} ") + # puts expected[i,j].inspect + # puts obt[i,j].inspect + assert_in_delta(expected[i, j], obt[i, j], 0.0001, "#{expected[i, j].class}!=#{obt[i, j].class} ") end end - #assert_equal(expected,obt) + # assert_equal(expected,obt) end - should_with_gsl "return same values for optimized and pairwise covariance matrix" do - cases=100 - v1=Statsample::Vector.new_scale(cases) {rand()} - v2=Statsample::Vector.new_scale(cases) {rand()} - v3=Statsample::Vector.new_scale(cases) {rand()} - v4=Statsample::Vector.new_scale(cases) {rand()} - v5=Statsample::Vector.new_scale(cases) {rand()} + should_with_gsl 'return same values for optimized and pairwise covariance matrix' do + cases = 100 + v1 = Statsample::Vector.new_scale(cases) { rand } + v2 = Statsample::Vector.new_scale(cases) { rand } + v3 = Statsample::Vector.new_scale(cases) { rand } + v4 = Statsample::Vector.new_scale(cases) { rand } + v5 = Statsample::Vector.new_scale(cases) { rand } - ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4,'v5'=>v5}.to_dataset + ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'v5' => v5 }.to_dataset - cor_opt=Statsample::Bivariate.covariance_matrix_optimized(ds) + cor_opt = Statsample::Bivariate.covariance_matrix_optimized(ds) - cor_pw =Statsample::Bivariate.covariance_matrix_pairwise(ds) - assert_equal_matrix(cor_opt,cor_pw,1e-15) + cor_pw = Statsample::Bivariate.covariance_matrix_pairwise(ds) + assert_equal_matrix(cor_opt, cor_pw, 1e-15) end - should_with_gsl "return same values for optimized and pairwise correlation matrix" do - - cases=100 - v1=Statsample::Vector.new_scale(cases) {rand()} - v2=Statsample::Vector.new_scale(cases) {rand()} - v3=Statsample::Vector.new_scale(cases) {rand()} - v4=Statsample::Vector.new_scale(cases) {rand()} - v5=Statsample::Vector.new_scale(cases) {rand()} - - ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4,'v5'=>v5}.to_dataset + should_with_gsl 'return same values for optimized and pairwise correlation matrix' do + cases = 100 + v1 = Statsample::Vector.new_scale(cases) { rand } + v2 = Statsample::Vector.new_scale(cases) { rand } + v3 = Statsample::Vector.new_scale(cases) { rand } + v4 = Statsample::Vector.new_scale(cases) { rand } + v5 = Statsample::Vector.new_scale(cases) { rand } - cor_opt=Statsample::Bivariate.correlation_matrix_optimized(ds) + ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'v5' => v5 }.to_dataset - cor_pw =Statsample::Bivariate.correlation_matrix_pairwise(ds) - assert_equal_matrix(cor_opt,cor_pw,1e-15) + cor_opt = Statsample::Bivariate.correlation_matrix_optimized(ds) + cor_pw = Statsample::Bivariate.correlation_matrix_pairwise(ds) + assert_equal_matrix(cor_opt, cor_pw, 1e-15) end - should "return correct correlation_matrix without nils values" do - v1=[6,5,4,7,8,4,3,2].to_vector(:scale) - v2=[2,3,7,8,6,4,3,2].to_vector(:scale) - v3=[6,2, 1000,1000,5,4,7,8].to_vector(:scale) - v4=[2,4,6,7, 3,7,8,6].to_vector(:scale) - ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4}.to_dataset - c=Proc.new {|n1,n2|Statsample::Bivariate.pearson(n1,n2)} - expected=Matrix[ [c.call(v1,v1),c.call(v1,v2),c.call(v1,v3),c.call(v1,v4)], [c.call(v2,v1),c.call(v2,v2),c.call(v2,v3),c.call(v2,v4)], [c.call(v3,v1),c.call(v3,v2),c.call(v3,v3),c.call(v3,v4)], - [c.call(v4,v1),c.call(v4,v2),c.call(v4,v3),c.call(v4,v4)] + should 'return correct correlation_matrix without nils values' do + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) + v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:scale) + v4 = [2, 4, 6, 7, 3, 7, 8, 6].to_vector(:scale) + ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4 }.to_dataset + c = proc { |n1, n2| Statsample::Bivariate.pearson(n1, n2) } + expected = Matrix[[c.call(v1, v1), c.call(v1, v2), c.call(v1, v3), c.call(v1, v4)], [c.call(v2, v1), c.call(v2, v2), c.call(v2, v3), c.call(v2, v4)], [c.call(v3, v1), c.call(v3, v2), c.call(v3, v3), c.call(v3, v4)], + [c.call(v4, v1), c.call(v4, v2), c.call(v4, v3), c.call(v4, v4)] ] - obt=Statsample::Bivariate.correlation_matrix(ds) + obt = Statsample::Bivariate.correlation_matrix(ds) for i in 0...expected.row_size for j in 0...expected.column_size - #puts expected[i,j].inspect - #puts obt[i,j].inspect - assert_in_delta(expected[i,j], obt[i,j],0.0001, "#{expected[i,j].class}!=#{obt[i,j].class} ") + # puts expected[i,j].inspect + # puts obt[i,j].inspect + assert_in_delta(expected[i, j], obt[i, j], 0.0001, "#{expected[i, j].class}!=#{obt[i, j].class} ") end end - #assert_equal(expected,obt) + # assert_equal(expected,obt) end - - should "return correct value for prop pearson" do - assert_in_delta(0.42, Statsample::Bivariate.prop_pearson(Statsample::Bivariate.t_r(0.084,94), 94),0.01) - assert_in_delta(0.65, Statsample::Bivariate.prop_pearson(Statsample::Bivariate.t_r(0.046,95), 95),0.01) - r=0.9 - n=100 - t=Statsample::Bivariate.t_r(r,n) - assert(Statsample::Bivariate.prop_pearson(t,n,:both)<0.05) - assert(Statsample::Bivariate.prop_pearson(t,n,:right)<0.05) - assert(Statsample::Bivariate.prop_pearson(t,n,:left)>0.05) - - r=-0.9 - n=100 - t=Statsample::Bivariate.t_r(r,n) - assert(Statsample::Bivariate.prop_pearson(t,n,:both)<0.05) - assert(Statsample::Bivariate.prop_pearson(t,n,:right)>0.05) - assert(Statsample::Bivariate.prop_pearson(t,n,:left)<0.05) + should 'return correct value for prop pearson' do + assert_in_delta(0.42, Statsample::Bivariate.prop_pearson(Statsample::Bivariate.t_r(0.084, 94), 94), 0.01) + assert_in_delta(0.65, Statsample::Bivariate.prop_pearson(Statsample::Bivariate.t_r(0.046, 95), 95), 0.01) + r = 0.9 + n = 100 + t = Statsample::Bivariate.t_r(r, n) + assert(Statsample::Bivariate.prop_pearson(t, n, :both) < 0.05) + assert(Statsample::Bivariate.prop_pearson(t, n, :right) < 0.05) + assert(Statsample::Bivariate.prop_pearson(t, n, :left) > 0.05) + + r = -0.9 + n = 100 + t = Statsample::Bivariate.t_r(r, n) + assert(Statsample::Bivariate.prop_pearson(t, n, :both) < 0.05) + assert(Statsample::Bivariate.prop_pearson(t, n, :right) > 0.05) + assert(Statsample::Bivariate.prop_pearson(t, n, :left) < 0.05) end should "return correct value for Spearman's rho" do - v1=[86,97,99,100,101,103,106,110,112,113].to_vector(:scale) - v2=[0,20,28,27,50,29,7,17,6,12].to_vector(:scale) - assert_in_delta(-0.175758,Statsample::Bivariate.spearman(v1,v2),0.0001) - + v1 = [86, 97, 99, 100, 101, 103, 106, 110, 112, 113].to_vector(:scale) + v2 = [0, 20, 28, 27, 50, 29, 7, 17, 6, 12].to_vector(:scale) + assert_in_delta(-0.175758, Statsample::Bivariate.spearman(v1, v2), 0.0001) end - should "return correct value for point_biserial correlation" do - c=[1,3,5,6,7,100,200,300,400,300].to_vector(:scale) - d=[1,1,1,1,1,0,0,0,0,0].to_vector(:scale) + should 'return correct value for point_biserial correlation' do + c = [1, 3, 5, 6, 7, 100, 200, 300, 400, 300].to_vector(:scale) + d = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0].to_vector(:scale) assert_raises TypeError do - Statsample::Bivariate.point_biserial(c,d) + Statsample::Bivariate.point_biserial(c, d) end - assert_in_delta(Statsample::Bivariate.point_biserial(d,c), Statsample::Bivariate.pearson(d,c), 0.0001) + assert_in_delta(Statsample::Bivariate.point_biserial(d, c), Statsample::Bivariate.pearson(d, c), 0.0001) end - should "return correct value for tau_a and tau_b" do - v1=[1,2,3,4,5,6,7,8,9,10,11].to_vector(:ordinal) - v2=[1,3,4,5,7,8,2,9,10,6,11].to_vector(:ordinal) - assert_in_delta(0.6727,Statsample::Bivariate.tau_a(v1,v2),0.001) - assert_in_delta(0.6727,Statsample::Bivariate.tau_b((Statsample::Crosstab.new(v1,v2).to_matrix)),0.001) - v1=[12,14,14,17,19,19,19,19,19,20,21,21,21,21,21,22,23,24,24,24,26,26,27].to_vector(:ordinal) - v2=[11,4,4,2,0,0,0,0,0,0,4,0,4,0,0,0,0,4,0,0,0,0,0].to_vector(:ordinal) - assert_in_delta(-0.376201540231705, Statsample::Bivariate.tau_b(Statsample::Crosstab.new(v1,v2).to_matrix),0.001) + should 'return correct value for tau_a and tau_b' do + v1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].to_vector(:ordinal) + v2 = [1, 3, 4, 5, 7, 8, 2, 9, 10, 6, 11].to_vector(:ordinal) + assert_in_delta(0.6727, Statsample::Bivariate.tau_a(v1, v2), 0.001) + assert_in_delta(0.6727, Statsample::Bivariate.tau_b((Statsample::Crosstab.new(v1, v2).to_matrix)), 0.001) + v1 = [12, 14, 14, 17, 19, 19, 19, 19, 19, 20, 21, 21, 21, 21, 21, 22, 23, 24, 24, 24, 26, 26, 27].to_vector(:ordinal) + v2 = [11, 4, 4, 2, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0].to_vector(:ordinal) + assert_in_delta(-0.376201540231705, Statsample::Bivariate.tau_b(Statsample::Crosstab.new(v1, v2).to_matrix), 0.001) end - should "return correct value for gamma correlation" do - m=Matrix[[10,5,2],[10,15,20]] - assert_in_delta(0.636,Statsample::Bivariate.gamma(m),0.001) - m2=Matrix[[15,12,6,5],[12,8,10,8],[4,6,9,10]] - assert_in_delta(0.349,Statsample::Bivariate.gamma(m2),0.001) + should 'return correct value for gamma correlation' do + m = Matrix[[10, 5, 2], [10, 15, 20]] + assert_in_delta(0.636, Statsample::Bivariate.gamma(m), 0.001) + m2 = Matrix[[15, 12, 6, 5], [12, 8, 10, 8], [4, 6, 9, 10]] + assert_in_delta(0.349, Statsample::Bivariate.gamma(m2), 0.001) end end diff --git a/test/test_codification.rb b/test/test_codification.rb index 5124f58..fe5f491 100644 --- a/test/test_codification.rb +++ b/test/test_codification.rb @@ -1,76 +1,78 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleCodificationTestCase < Minitest::Test - def initialize(*args) - v1=%w{run walk,run walking running sleep sleeping,dreaming sleep,dream}.to_vector - @dict={'run'=>'r','walk'=>'w','walking'=>'w','running'=>'r','sleep'=>'s', 'sleeping'=>'s', 'dream'=>'d', 'dreaming'=>'d'} - @ds={"v1"=>v1}.to_dataset + v1 = %w(run walk,run walking running sleep sleeping,dreaming sleep,dream).to_vector + @dict = { 'run' => 'r', 'walk' => 'w', 'walking' => 'w', 'running' => 'r', 'sleep' => 's', 'sleeping' => 's', 'dream' => 'd', 'dreaming' => 'd' } + @ds = { 'v1' => v1 }.to_dataset super end + def test_create_hash - expected_keys_v1=%w{run walk walking running sleep sleeping dream dreaming}.sort - hash=Statsample::Codification.create_hash(@ds,['v1']) - assert_equal(['v1'],hash.keys) - assert_equal(expected_keys_v1,hash['v1'].keys.sort) - assert_equal(expected_keys_v1,hash['v1'].values.sort) + expected_keys_v1 = %w(run walk walking running sleep sleeping dream dreaming).sort + hash = Statsample::Codification.create_hash(@ds, ['v1']) + assert_equal(['v1'], hash.keys) + assert_equal(expected_keys_v1, hash['v1'].keys.sort) + assert_equal(expected_keys_v1, hash['v1'].values.sort) end + def test_create_excel - filename=Dir::tmpdir+"/test_excel"+Time.now().to_s+".xls" - #filename = Tempfile.new("test_codification_"+Time.now().to_s) + filename = Dir.tmpdir + '/test_excel' + Time.now.to_s + '.xls' + # filename = Tempfile.new("test_codification_"+Time.now().to_s) Statsample::Codification.create_excel(@ds, ['v1'], filename) - field=(["v1"]*8).to_vector - keys=%w{dream dreaming run running sleep sleeping walk walking}.to_vector - ds=Statsample::Excel.read(filename) + field = (['v1'] * 8).to_vector + keys = %w(dream dreaming run running sleep sleeping walk walking).to_vector + ds = Statsample::Excel.read(filename) assert_equal(field, ds['field']) assert_equal(keys, ds['original']) assert_equal(keys, ds['recoded']) - hash=Statsample::Codification.excel_to_recoded_hash(filename) + hash = Statsample::Codification.excel_to_recoded_hash(filename) assert_equal(keys.data, hash['v1'].keys.sort) assert_equal(keys.data, hash['v1'].values.sort) - end + def test_create_yaml - assert_raise ArgumentError do - Statsample::Codification.create_yaml(@ds,[]) + assert_raise ArgumentError do + Statsample::Codification.create_yaml(@ds, []) end - expected_keys_v1=%w{run walk walking running sleep sleeping dream dreaming}.sort - yaml_hash=Statsample::Codification.create_yaml(@ds,['v1']) - h=YAML::load(yaml_hash) - assert_equal(['v1'],h.keys) - assert_equal(expected_keys_v1,h['v1'].keys.sort) - tf = Tempfile.new("test_codification") - yaml_hash=Statsample::Codification.create_yaml(@ds,['v1'],tf, Statsample::SPLIT_TOKEN) + expected_keys_v1 = %w(run walk walking running sleep sleeping dream dreaming).sort + yaml_hash = Statsample::Codification.create_yaml(@ds, ['v1']) + h = YAML.load(yaml_hash) + assert_equal(['v1'], h.keys) + assert_equal(expected_keys_v1, h['v1'].keys.sort) + tf = Tempfile.new('test_codification') + yaml_hash = Statsample::Codification.create_yaml(@ds, ['v1'], tf, Statsample::SPLIT_TOKEN) tf.close tf.open - h=YAML::load(tf) - assert_equal(['v1'],h.keys) - assert_equal(expected_keys_v1,h['v1'].keys.sort) + h = YAML.load(tf) + assert_equal(['v1'], h.keys) + assert_equal(expected_keys_v1, h['v1'].keys.sort) tf.close(true) end + def test_recodification - expected=[['r'],['w','r'],['w'],['r'],['s'],['s','d'], ['s','d']] - assert_equal(expected,Statsample::Codification.recode_vector(@ds['v1'],@dict)) - v2=['run','walk,dreaming',nil,'walk,dream,dreaming,walking'].to_vector - expected=[['r'],['w','d'],nil,['w','d']] - assert_equal(expected,Statsample::Codification.recode_vector(v2,@dict)) + expected = [['r'], %w(w r), ['w'], ['r'], ['s'], %w(s d), %w(s d)] + assert_equal(expected, Statsample::Codification.recode_vector(@ds['v1'], @dict)) + v2 = ['run', 'walk,dreaming', nil, 'walk,dream,dreaming,walking'].to_vector + expected = [['r'], %w(w d), nil, %w(w d)] + assert_equal(expected, Statsample::Codification.recode_vector(v2, @dict)) end + def test_recode_dataset_simple - Statsample::Codification.recode_dataset_simple!(@ds,{'v1'=>@dict}) - expected_vector=['r','w,r','w','r','s','s,d', 's,d'].to_vector - assert_not_equal(expected_vector,@ds['v1']) - assert_equal(expected_vector,@ds['v1_recoded']) + Statsample::Codification.recode_dataset_simple!(@ds, 'v1' => @dict) + expected_vector = ['r', 'w,r', 'w', 'r', 's', 's,d', 's,d'].to_vector + assert_not_equal(expected_vector, @ds['v1']) + assert_equal(expected_vector, @ds['v1_recoded']) end - def test_recode_dataset_split - Statsample::Codification.recode_dataset_split!(@ds,{'v1'=>@dict}) - e={} - e['r']=[1,1,0,1,0,0,0].to_vector - e['w']=[0,1,1,0,0,0,0].to_vector - e['s']=[0,0,0,0,1,1,1].to_vector - e['d']=[0,0,0,0,0,1,1].to_vector - e.each{|k,expected| - assert_equal(expected,@ds['v1_'+k],"Error on key #{k}") + def test_recode_dataset_split + Statsample::Codification.recode_dataset_split!(@ds, 'v1' => @dict) + e = {} + e['r'] = [1, 1, 0, 1, 0, 0, 0].to_vector + e['w'] = [0, 1, 1, 0, 0, 0, 0].to_vector + e['s'] = [0, 0, 0, 0, 1, 1, 1].to_vector + e['d'] = [0, 0, 0, 0, 0, 1, 1].to_vector + e.each{|k, expected| + assert_equal(expected, @ds['v1_' + k], "Error on key #{k}") } end - end diff --git a/test/test_crosstab.rb b/test/test_crosstab.rb index 88bf6ff..c926bb2 100644 --- a/test/test_crosstab.rb +++ b/test/test_crosstab.rb @@ -1,63 +1,67 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleCrosstabTestCase < Minitest::Test - def initialize(*args) - @v1=%w{black blonde black black red black brown black blonde black red black blonde}.to_vector - @v2=%w{woman man man woman man man man woman man woman woman man man}.to_vector - @ct=Statsample::Crosstab.new(@v1,@v2) + @v1 = %w(black blonde black black red black brown black blonde black red black blonde).to_vector + @v2 = %w(woman man man woman man man man woman man woman woman man man).to_vector + @ct = Statsample::Crosstab.new(@v1, @v2) super end + def test_crosstab_errors - e1=%w{black blonde black black red black brown black blonde black} + e1 = %w(black blonde black black red black brown black blonde black) assert_raise ArgumentError do - Statsample::Crosstab.new(e1,@v2) + Statsample::Crosstab.new(e1, @v2) end - e2=%w{black blonde black black red black brown black blonde black black}.to_vector + e2 = %w(black blonde black black red black brown black blonde black black).to_vector assert_raise ArgumentError do - Statsample::Crosstab.new(e2,@v2) + Statsample::Crosstab.new(e2, @v2) end assert_nothing_raised do - Statsample::Crosstab.new(@v1,@v2) + Statsample::Crosstab.new(@v1, @v2) end end + def test_crosstab_basic - assert_equal(%w{black blonde brown red}, @ct.rows_names) - assert_equal(%w{man woman}, @ct.cols_names) - assert_equal({'black'=>7,'blonde'=>3,'red'=>2,'brown'=>1}, @ct.rows_total) - assert_equal({'man'=>8,'woman'=>5}, @ct.cols_total) + assert_equal(%w(black blonde brown red), @ct.rows_names) + assert_equal(%w(man woman), @ct.cols_names) + assert_equal({ 'black' => 7, 'blonde' => 3, 'red' => 2, 'brown' => 1 }, @ct.rows_total) + assert_equal({ 'man' => 8, 'woman' => 5 }, @ct.cols_total) end + def test_crosstab_frequencies - fq=@ct.frequencies - assert_equal(8,fq.size) - sum=fq.inject(0) {|s,x| s+x[1]} - assert_equal(13,sum) - fr=@ct.frequencies_by_row - assert_equal(4,fr.size) - assert_equal(%w{black blonde brown red},fr.keys.sort) - fc=@ct.frequencies_by_col - assert_equal(2,fc.size) - assert_equal(%w{man woman},fc.keys.sort) - assert_equal(Matrix.rows([[3,4],[3,0],[1,0],[1,1]]),@ct.to_matrix) + fq = @ct.frequencies + assert_equal(8, fq.size) + sum = fq.inject(0) { |s, x| s + x[1] } + assert_equal(13, sum) + fr = @ct.frequencies_by_row + assert_equal(4, fr.size) + assert_equal(%w(black blonde brown red), fr.keys.sort) + fc = @ct.frequencies_by_col + assert_equal(2, fc.size) + assert_equal(%w(man woman), fc.keys.sort) + assert_equal(Matrix.rows([[3, 4], [3, 0], [1, 0], [1, 1]]), @ct.to_matrix) end + def test_summary - @ct.percentage_row=true - @ct.percentage_column=true - @ct.percentage_total=true - assert(@ct.summary.size>0) + @ct.percentage_row = true + @ct.percentage_column = true + @ct.percentage_total = true + assert(@ct.summary.size > 0) end + def test_expected - v1=%w{1 1 1 1 1 0 0 0 0 0}.to_vector - v2=%w{0 0 0 0 0 1 1 1 1 1}.to_vector - ct=Statsample::Crosstab.new(v1,v2) - assert_equal(Matrix[[2.5,2.5],[2.5,2.5]],ct.matrix_expected) + v1 = %w(1 1 1 1 1 0 0 0 0 0).to_vector + v2 = %w(0 0 0 0 0 1 1 1 1 1).to_vector + ct = Statsample::Crosstab.new(v1, v2) + assert_equal(Matrix[[2.5, 2.5], [2.5, 2.5]], ct.matrix_expected) end + def test_crosstab_with_scale - v1=%w{1 1 1 1 1 0 0 0 0 0}.to_scale - v2=%w{0 0 0 0 0 1 1 1 1 1}.to_scale - ct=Statsample::Crosstab.new(v1,v2) - assert_equal(Matrix[[0,5],[5,0]],ct.to_matrix) + v1 = %w(1 1 1 1 1 0 0 0 0 0).to_scale + v2 = %w(0 0 0 0 0 1 1 1 1 1).to_scale + ct = Statsample::Crosstab.new(v1, v2) + assert_equal(Matrix[[0, 5], [5, 0]], ct.to_matrix) assert_nothing_raised { ct.summary } end - end diff --git a/test/test_csv.rb b/test/test_csv.rb index 6aac9ef..9b2d832 100644 --- a/test/test_csv.rb +++ b/test/test_csv.rb @@ -1,81 +1,83 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleCSVTestCase < Minitest::Test def setup - @ds=Statsample::CSV.read(File.dirname(__FILE__)+"/fixtures/test_csv.csv") + @ds = Statsample::CSV.read(File.dirname(__FILE__) + '/fixtures/test_csv.csv') end + def test_read - assert_equal(6,@ds.cases) - assert_equal(%w{id name age city a1}, @ds.fields) - id=[1,2,3,4,5,6].to_vector(:scale) - name=["Alex","Claude","Peter","Franz","George","Fernand"].to_vector(:nominal) - age=[20,23,25,27,5.5,nil].to_vector(:scale) - city=["New York","London","London","Paris","Tome",nil].to_vector(:nominal) - a1=["a,b","b,c","a",nil,"a,b,c",nil].to_vector(:nominal) - ds_exp=Statsample::Dataset.new({'id'=>id,'name'=>name,'age'=>age,'city'=>city,'a1'=>a1}, %w{id name age city a1}) + assert_equal(6, @ds.cases) + assert_equal(%w(id name age city a1), @ds.fields) + id = [1, 2, 3, 4, 5, 6].to_vector(:scale) + name = %w(Alex Claude Peter Franz George Fernand).to_vector(:nominal) + age = [20, 23, 25, 27, 5.5, nil].to_vector(:scale) + city = ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:nominal) + a1 = ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:nominal) + ds_exp = Statsample::Dataset.new({ 'id' => id, 'name' => name, 'age' => age, 'city' => city, 'a1' => a1 }, %w(id name age city a1)) ds_exp.fields.each{|f| - assert_equal(ds_exp[f],@ds[f]) + assert_equal(ds_exp[f], @ds[f]) } - assert_equal(ds_exp,@ds) + assert_equal(ds_exp, @ds) end + def test_nil - assert_equal(nil,@ds['age'][5]) + assert_equal(nil, @ds['age'][5]) end + def test_repeated - ds=Statsample::CSV.read(File.dirname(__FILE__)+"/fixtures/repeated_fields.csv") - assert_equal(%w{id name_1 age_1 city a1 name_2 age_2},ds.fields) - age=[3,4,5,6,nil,8].to_vector(:scale) - assert_equal(age,ds['age_2']) + ds = Statsample::CSV.read(File.dirname(__FILE__) + '/fixtures/repeated_fields.csv') + assert_equal(%w(id name_1 age_1 city a1 name_2 age_2), ds.fields) + age = [3, 4, 5, 6, nil, 8].to_vector(:scale) + assert_equal(age, ds['age_2']) end + def test_write - filename=Tempfile.new("afile") + filename = Tempfile.new('afile') # filename=Dir::tmpdir+"/test_write.csv" Statsample::CSV.write(@ds, filename.path) - ds2=Statsample::CSV.read(filename.path) - i=0 + ds2 = Statsample::CSV.read(filename.path) + i = 0 ds2.each_array{|row| - assert_equal(@ds.case_as_array(i),row) - i+=1 + assert_equal(@ds.case_as_array(i), row) + i += 1 } end end -=begin -class StatsampleCSVTestCase2 < Minitest::Test - def setup - @ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/test_csv.csv") - end - def test_read - assert_equal(6,@ds.cases) - assert_equal(%w{id name age city a1}, @ds.fields) - id=[1,2,3,4,5,6].to_vector(:scale) - name=["Alex","Claude","Peter","Franz","George","Fernand"].to_vector(:nominal) - age=[20,23,25,27,5.5,nil].to_vector(:scale) - city=["New York","London","London","Paris","Tome",nil].to_vector(:nominal) - a1=["a,b","b,c","a",nil,"a,b,c",nil].to_vector(:nominal) - ds_exp=Statsample::Dataset.new({'id'=>id,'name'=>name,'age'=>age,'city'=>city,'a1'=>a1}, %w{id name age city a1}) - ds_exp.fields.each{|f| - assert_equal(ds_exp[f],@ds[f]) - } - assert_equal(ds_exp,@ds) - end - def test_nil - assert_equal(nil,@ds['age'][5]) - end - def test_repeated - ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/repeated_fields.csv") - assert_equal(%w{id name_1 age_1 city a1 name_2 age_2},ds.fields) - age=[3,4,5,6,nil,8].to_vector(:scale) - assert_equal(age,ds['age_2']) - end - def test_write - filename=Tempfile.new("afile") - # filename=Dir::tmpdir+"/test_write.csv" - Statsample::CSV.write(@ds, filename.path) - ds2=Statsample::CSV.read19(filename.path) - i=0 - ds2.each_array{|row| - assert_equal(@ds.case_as_array(i),row) - i+=1 - } - end -end -=end +# class StatsampleCSVTestCase2 < Minitest::Test +# def setup +# @ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/test_csv.csv") +# end +# def test_read +# assert_equal(6,@ds.cases) +# assert_equal(%w{id name age city a1}, @ds.fields) +# id=[1,2,3,4,5,6].to_vector(:scale) +# name=["Alex","Claude","Peter","Franz","George","Fernand"].to_vector(:nominal) +# age=[20,23,25,27,5.5,nil].to_vector(:scale) +# city=["New York","London","London","Paris","Tome",nil].to_vector(:nominal) +# a1=["a,b","b,c","a",nil,"a,b,c",nil].to_vector(:nominal) +# ds_exp=Statsample::Dataset.new({'id'=>id,'name'=>name,'age'=>age,'city'=>city,'a1'=>a1}, %w{id name age city a1}) +# ds_exp.fields.each{|f| +# assert_equal(ds_exp[f],@ds[f]) +# } +# assert_equal(ds_exp,@ds) +# end +# def test_nil +# assert_equal(nil,@ds['age'][5]) +# end +# def test_repeated +# ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/repeated_fields.csv") +# assert_equal(%w{id name_1 age_1 city a1 name_2 age_2},ds.fields) +# age=[3,4,5,6,nil,8].to_vector(:scale) +# assert_equal(age,ds['age_2']) +# end +# def test_write +# filename=Tempfile.new("afile") +# # filename=Dir::tmpdir+"/test_write.csv" +# Statsample::CSV.write(@ds, filename.path) +# ds2=Statsample::CSV.read19(filename.path) +# i=0 +# ds2.each_array{|row| +# assert_equal(@ds.case_as_array(i),row) +# i+=1 +# } +# end +# end diff --git a/test/test_dataset.rb b/test/test_dataset.rb index f595bb6..411bd2c 100644 --- a/test/test_dataset.rb +++ b/test/test_dataset.rb @@ -1,188 +1,199 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleDatasetTestCase < Minitest::Test def setup - @ds=Statsample::Dataset.new({'id' => Statsample::Vector.new([1,2,3,4,5]), 'name'=>Statsample::Vector.new(%w{Alex Claude Peter Franz George}), 'age'=>Statsample::Vector.new([20,23,25,27,5]), - 'city'=>Statsample::Vector.new(['New York','London','London','Paris','Tome']), - 'a1'=>Statsample::Vector.new(['a,b','b,c','a',nil,'a,b,c'])}, ['id','name','age','city','a1']) + @ds = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([1, 2, 3, 4, 5]), 'name' => Statsample::Vector.new(%w(Alex Claude Peter Franz George)), 'age' => Statsample::Vector.new([20, 23, 25, 27, 5]), + 'city' => Statsample::Vector.new(['New York', 'London', 'London', 'Paris', 'Tome']), + 'a1' => Statsample::Vector.new(['a,b', 'b,c', 'a', nil, 'a,b,c']) }, %w(id name age city a1)) end + def test_nest - ds={ - 'a'=>%w{a a a b b b}.to_vector, - 'b'=>%w{c c d d e e}.to_vector, - 'c'=>%w{f g h i j k}.to_vector + ds = { + 'a' => %w(a a a b b b).to_vector, + 'b' => %w(c c d d e e).to_vector, + 'c' => %w(f g h i j k).to_vector }.to_dataset - nest=ds.nest('a','b') - assert_equal([{'c'=>'f'},{'c'=>'g'}], nest['a']['c']) - assert_equal([{'c'=>'h'}], nest['a']['d']) - assert_equal([{'c'=>'j'},{'c'=>'k'}], nest['b']['e']) - + nest = ds.nest('a', 'b') + assert_equal([{ 'c' => 'f' }, { 'c' => 'g' }], nest['a']['c']) + assert_equal([{ 'c' => 'h' }], nest['a']['d']) + assert_equal([{ 'c' => 'j' }, { 'c' => 'k' }], nest['b']['e']) end + def test_should_have_summary - assert(@ds.summary.size>0) + assert(@ds.summary.size > 0) end + def test_basic - assert_equal(5,@ds.cases) - assert_equal(%w{id name age city a1}, @ds.fields) + assert_equal(5, @ds.cases) + assert_equal(%w(id name age city a1), @ds.fields) end + def test_saveload - outfile=Tempfile.new("dataset.ds") + outfile = Tempfile.new('dataset.ds') @ds.save(outfile.path) - a=Statsample.load(outfile.path) - assert_equal(@ds,a) + a = Statsample.load(outfile.path) + assert_equal(@ds, a) end + def test_gsl if Statsample.has_gsl? - matrix=GSL::Matrix[[1,2],[3,4],[5,6]] - ds=Statsample::Dataset.new('v1'=>[1,3,5].to_vector,'v2'=>[2,4,6].to_vector) - assert_equal(matrix,ds.to_gsl) + matrix = GSL::Matrix[[1, 2], [3, 4], [5, 6]] + ds = Statsample::Dataset.new('v1' => [1, 3, 5].to_vector, 'v2' => [2, 4, 6].to_vector) + assert_equal(matrix, ds.to_gsl) else - skip("Gsl needed") + skip('Gsl needed') end end + def test_matrix - matrix=Matrix[[1,2],[3,4],[5,6]] - ds=Statsample::Dataset.new('v1'=>[1,3,5].to_vector,'v2'=>[2,4,6].to_vector) - assert_equal(matrix,ds.to_matrix) + matrix = Matrix[[1, 2], [3, 4], [5, 6]] + ds = Statsample::Dataset.new('v1' => [1, 3, 5].to_vector, 'v2' => [2, 4, 6].to_vector) + assert_equal(matrix, ds.to_matrix) end def test_fields - @ds.fields=%w{name a1 id age city} - assert_equal(%w{name a1 id age city}, @ds.fields) - @ds.fields=%w{id name age} - assert_equal(%w{id name age a1 city}, @ds.fields) + @ds.fields = %w(name a1 id age city) + assert_equal(%w(name a1 id age city), @ds.fields) + @ds.fields = %w(id name age) + assert_equal(%w(id name age a1 city), @ds.fields) end + def test_merge - a=[1,2,3].to_scale - b=[3,4,5].to_vector - c=[4,5,6].to_scale - d=[7,8,9].to_vector - e=[10,20,30].to_vector - ds1={'a'=>a,'b'=>b}.to_dataset - ds2={'c'=>c,'d'=>d}.to_dataset - exp={'a'=>a,'b'=>b,'c'=>c,'d'=>d}.to_dataset - - assert_equal(exp,ds1.merge(ds2)) - exp.fields=%w{c d a b} - assert_equal(exp,ds2.merge(ds1)) - ds3={'a'=>e}.to_dataset - exp={'a_1'=>a,'b'=>b,'a_2'=>e}.to_dataset - exp.fields=%w{a_1 b a_2} - assert_equal(exp,ds1.merge(ds3)) + a = [1, 2, 3].to_scale + b = [3, 4, 5].to_vector + c = [4, 5, 6].to_scale + d = [7, 8, 9].to_vector + e = [10, 20, 30].to_vector + ds1 = { 'a' => a, 'b' => b }.to_dataset + ds2 = { 'c' => c, 'd' => d }.to_dataset + exp = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset + + assert_equal(exp, ds1.merge(ds2)) + exp.fields = %w(c d a b) + assert_equal(exp, ds2.merge(ds1)) + ds3 = { 'a' => e }.to_dataset + exp = { 'a_1' => a, 'b' => b, 'a_2' => e }.to_dataset + exp.fields = %w(a_1 b a_2) + assert_equal(exp, ds1.merge(ds3)) end + def test_each_vector - a=[1,2,3].to_vector - b=[3,4,5].to_vector - fields=["a","b"] - ds=Statsample::Dataset.new({'a'=>a,'b'=>b},fields) - res=[] - ds.each_vector{|k,v| - res.push([k,v]) + a = [1, 2, 3].to_vector + b = [3, 4, 5].to_vector + fields = %w(a b) + ds = Statsample::Dataset.new({ 'a' => a, 'b' => b }, fields) + res = [] + ds.each_vector{|k, v| + res.push([k, v]) } - assert_equal([["a",a],["b",b]],res) - ds.fields=["b","a"] - res=[] - ds.each_vector{|k,v| - res.push([k,v]) + assert_equal([['a', a], ['b', b]], res) + ds.fields = %w(b a) + res = [] + ds.each_vector{|k, v| + res.push([k, v]) } - assert_equal([["b",b],["a",a]],res) + assert_equal([['b', b], ['a', a]], res) end + def test_equality - v1=[1,2,3,4].to_vector - v2=[5,6,7,8].to_vector - ds1=Statsample::Dataset.new({'v1'=>v1,'v2'=>v2}, %w{v2 v1}) - v3=[1,2,3,4].to_vector - v4=[5,6,7,8].to_vector - ds2=Statsample::Dataset.new({'v1'=>v3,'v2'=>v4}, %w{v2 v1}) - assert_equal(ds1,ds2) - ds2.fields=%w{v1 v2} - assert_not_equal(ds1,ds2) + v1 = [1, 2, 3, 4].to_vector + v2 = [5, 6, 7, 8].to_vector + ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) + v3 = [1, 2, 3, 4].to_vector + v4 = [5, 6, 7, 8].to_vector + ds2 = Statsample::Dataset.new({ 'v1' => v3, 'v2' => v4 }, %w(v2 v1)) + assert_equal(ds1, ds2) + ds2.fields = %w(v1 v2) + assert_not_equal(ds1, ds2) end + def test_add_vector - v=Statsample::Vector.new(%w{a b c d e}) - @ds.add_vector('new',v) - assert_equal(%w{id name age city a1 new},@ds.fields) - x=Statsample::Vector.new(%w{a b c d e f g}) + v = Statsample::Vector.new(%w(a b c d e)) + @ds.add_vector('new', v) + assert_equal(%w(id name age city a1 new), @ds.fields) + x = Statsample::Vector.new(%w(a b c d e f g)) assert_raise ArgumentError do - @ds.add_vector('new2',x) + @ds.add_vector('new2', x) end end + def test_vector_by_calculation - a1=[1,2,3,4,5,6,7].to_vector(:scale) - a2=[10,20,30,40,50,60,70].to_vector(:scale) - a3=[100,200,300,400,500,600,700].to_vector(:scale) - ds={'a1'=>a1,'a2'=>a2,'a3'=>a3}.to_dataset - total=ds.vector_by_calculation() {|row| - row['a1']+row['a2']+row['a3'] + a1 = [1, 2, 3, 4, 5, 6, 7].to_vector(:scale) + a2 = [10, 20, 30, 40, 50, 60, 70].to_vector(:scale) + a3 = [100, 200, 300, 400, 500, 600, 700].to_vector(:scale) + ds = { 'a1' => a1, 'a2' => a2, 'a3' => a3 }.to_dataset + total = ds.vector_by_calculation {|row| + row['a1'] + row['a2'] + row['a3'] } - expected=[111,222,333,444,555,666,777].to_vector(:scale) - assert_equal(expected,total) + expected = [111, 222, 333, 444, 555, 666, 777].to_vector(:scale) + assert_equal(expected, total) end + def test_vector_sum - a1=[1 ,2 ,3 ,4 , 5,nil].to_vector(:scale) - a2=[10 ,10,20,20 ,20,30].to_vector(:scale) - b1=[nil,1 ,1 ,1 ,1 ,2].to_vector(:scale) - b2=[2 ,2 ,2 ,nil,2 ,3].to_vector(:scale) - ds={'a1'=>a1,'a2'=>a2,'b1'=>b1,'b2'=>b2}.to_dataset - total=ds.vector_sum - a=ds.vector_sum(['a1','a2']) - b=ds.vector_sum(['b1','b2']) - expected_a=[11,12,23,24,25,nil].to_vector(:scale) - expected_b=[nil,3,3,nil,3,5].to_vector(:scale) - expected_total=[nil,15,26,nil,28,nil].to_vector(:scale) + a1 = [1, 2, 3, 4, 5, nil].to_vector(:scale) + a2 = [10, 10, 20, 20, 20, 30].to_vector(:scale) + b1 = [nil, 1, 1, 1, 1, 2].to_vector(:scale) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) + ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2 }.to_dataset + total = ds.vector_sum + a = ds.vector_sum(%w(a1 a2)) + b = ds.vector_sum(%w(b1 b2)) + expected_a = [11, 12, 23, 24, 25, nil].to_vector(:scale) + expected_b = [nil, 3, 3, nil, 3, 5].to_vector(:scale) + expected_total = [nil, 15, 26, nil, 28, nil].to_vector(:scale) assert_equal(expected_a, a) assert_equal(expected_b, b) assert_equal(expected_total, total) end + def test_vector_missing_values - a1=[1 ,nil ,3 ,4 , 5,nil].to_vector(:scale) - a2=[10 ,nil ,20,20 ,20,30].to_vector(:scale) - b1=[nil,nil ,1 ,1 ,1 ,2].to_vector(:scale) - b2=[2 ,2 ,2 ,nil,2 ,3].to_vector(:scale) - c= [nil,2 , 4,2 ,2 ,2].to_vector(:scale) - ds={'a1'=>a1,'a2'=>a2,'b1'=>b1,'b2'=>b2,'c'=>c}.to_dataset - mva=[2,3,0,1,0,1].to_vector(:scale) - assert_equal(mva,ds.vector_missing_values) + a1 = [1, nil, 3, 4, 5, nil].to_vector(:scale) + a2 = [10, nil, 20, 20, 20, 30].to_vector(:scale) + b1 = [nil, nil, 1, 1, 1, 2].to_vector(:scale) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) + c = [nil, 2, 4, 2, 2, 2].to_vector(:scale) + ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset + mva = [2, 3, 0, 1, 0, 1].to_vector(:scale) + assert_equal(mva, ds.vector_missing_values) end def test_has_missing_values - a1=[1 ,nil ,3 ,4 , 5,nil].to_vector(:scale) - a2=[10 ,nil ,20,20 ,20,30].to_vector(:scale) - b1=[nil,nil ,1 ,1 ,1 ,2].to_vector(:scale) - b2=[2 ,2 ,2 ,nil,2 ,3].to_vector(:scale) - c= [nil,2 , 4,2 ,2 ,2].to_vector(:scale) - ds={'a1'=>a1,'a2'=>a2,'b1'=>b1,'b2'=>b2,'c'=>c}.to_dataset + a1 = [1, nil, 3, 4, 5, nil].to_vector(:scale) + a2 = [10, nil, 20, 20, 20, 30].to_vector(:scale) + b1 = [nil, nil, 1, 1, 1, 2].to_vector(:scale) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) + c = [nil, 2, 4, 2, 2, 2].to_vector(:scale) + ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset assert(ds.has_missing_data?) - clean=ds.dup_only_valid + clean = ds.dup_only_valid assert(!clean.has_missing_data?) end - def test_vector_count_characters - a1=[1 ,"abcde" ,3 ,4 , 5,nil].to_vector(:scale) - a2=[10 ,20.3 ,20 ,20 ,20,30].to_vector(:scale) - b1=[nil,"343434" ,1 ,1 ,1 ,2].to_vector(:scale) - b2=[2 ,2 ,2 ,nil,2 ,3].to_vector(:scale) - c= [nil,2 ,"This is a nice example",2 ,2 ,2].to_vector(:scale) - ds={'a1'=>a1,'a2'=>a2,'b1'=>b1,'b2'=>b2,'c'=>c}.to_dataset - exp=[4,17,27,5,6,5].to_vector(:scale) - assert_equal(exp,ds.vector_count_characters) - + a1 = [1, 'abcde', 3, 4, 5, nil].to_vector(:scale) + a2 = [10, 20.3, 20, 20, 20, 30].to_vector(:scale) + b1 = [nil, '343434', 1, 1, 1, 2].to_vector(:scale) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) + c = [nil, 2, 'This is a nice example', 2, 2, 2].to_vector(:scale) + ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset + exp = [4, 17, 27, 5, 6, 5].to_vector(:scale) + assert_equal(exp, ds.vector_count_characters) end + def test_vector_mean - a1=[1 ,2 ,3 ,4 , 5,nil].to_vector(:scale) - a2=[10 ,10,20,20 ,20,30].to_vector(:scale) - b1=[nil,1 ,1 ,1 ,1 ,2].to_vector(:scale) - b2=[2 ,2 ,2 ,nil,2 ,3].to_vector(:scale) - c= [nil,2, 4,2 ,2 ,2].to_vector(:scale) - ds={'a1'=>a1,'a2'=>a2,'b1'=>b1,'b2'=>b2,'c'=>c}.to_dataset - total=ds.vector_mean - a=ds.vector_mean(['a1','a2'],1) - b=ds.vector_mean(['b1','b2'],1) - c=ds.vector_mean(['b1','b2','c'],1) - expected_a=[5.5,6,11.5,12,12.5,30].to_vector(:scale) - expected_b=[2,1.5,1.5,1,1.5,2.5].to_vector(:scale) - expected_c=[nil, 5.0/3,7.0/3,1.5,5.0/3,7.0/3].to_vector(:scale) - expected_total=[nil,3.4,6,nil,6.0,nil].to_vector(:scale) + a1 = [1, 2, 3, 4, 5, nil].to_vector(:scale) + a2 = [10, 10, 20, 20, 20, 30].to_vector(:scale) + b1 = [nil, 1, 1, 1, 1, 2].to_vector(:scale) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) + c = [nil, 2, 4, 2, 2, 2].to_vector(:scale) + ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset + total = ds.vector_mean + a = ds.vector_mean(%w(a1 a2), 1) + b = ds.vector_mean(%w(b1 b2), 1) + c = ds.vector_mean(%w(b1 b2 c), 1) + expected_a = [5.5, 6, 11.5, 12, 12.5, 30].to_vector(:scale) + expected_b = [2, 1.5, 1.5, 1, 1.5, 2.5].to_vector(:scale) + expected_c = [nil, 5.0 / 3, 7.0 / 3, 1.5, 5.0 / 3, 7.0 / 3].to_vector(:scale) + expected_total = [nil, 3.4, 6, nil, 6.0, nil].to_vector(:scale) assert_equal(expected_a, a) assert_equal(expected_b, b) assert_equal(expected_c, c) @@ -190,273 +201,279 @@ def test_vector_mean end def test_each_array - expected=[[1,'Alex',20,'New York','a,b'], [2,'Claude',23,'London','b,c'], [3,'Peter',25,'London','a'],[4,'Franz', 27,'Paris',nil],[5,'George',5,'Tome','a,b,c']] - out=[] + expected = [[1, 'Alex', 20, 'New York', 'a,b'], [2, 'Claude', 23, 'London', 'b,c'], [3, 'Peter', 25, 'London', 'a'], [4, 'Franz', 27, 'Paris', nil], [5, 'George', 5, 'Tome', 'a,b,c']] + out = [] @ds.each_array{ |a| out.push(a) } - assert_equal(expected,out) + assert_equal(expected, out) end + def test_recode - @ds['age'].type=:scale - @ds.recode!("age") {|c| c['id']*2} - expected=[2,4,6,8,10].to_vector(:scale) - assert_equal(expected,@ds['age']) + @ds['age'].type = :scale + @ds.recode!('age') { |c| c['id'] * 2 } + expected = [2, 4, 6, 8, 10].to_vector(:scale) + assert_equal(expected, @ds['age']) end + def test_case_as - assert_equal({'id'=>1,'name'=>'Alex','city'=>'New York','age'=>20,'a1'=>'a,b'},@ds.case_as_hash(0)) - assert_equal([5,'George',5,'Tome','a,b,c'],@ds.case_as_array(4)) + assert_equal({ 'id' => 1, 'name' => 'Alex', 'city' => 'New York', 'age' => 20, 'a1' => 'a,b' }, @ds.case_as_hash(0)) + assert_equal([5, 'George', 5, 'Tome', 'a,b,c'], @ds.case_as_array(4)) # Native methods - assert_equal({'id'=>1,'name'=>'Alex','city'=>'New York','age'=>20,'a1'=>'a,b'},@ds._case_as_hash(0)) - assert_equal([5,'George',5,'Tome','a,b,c'],@ds._case_as_array(4)) - - - + assert_equal({ 'id' => 1, 'name' => 'Alex', 'city' => 'New York', 'age' => 20, 'a1' => 'a,b' }, @ds._case_as_hash(0)) + assert_equal([5, 'George', 5, 'Tome', 'a,b,c'], @ds._case_as_array(4)) end + def test_delete_vector @ds.delete_vector('name') - assert_equal(%w{id age city a1},@ds.fields) - assert_equal(%w{a1 age city id},@ds.vectors.keys.sort) + assert_equal(%w(id age city a1), @ds.fields) + assert_equal(%w(a1 age city id), @ds.vectors.keys.sort) end + def test_change_type - @ds.col('age').type=:scale - assert_equal(:scale,@ds.col('age').type) + @ds.col('age').type = :scale + assert_equal(:scale, @ds.col('age').type) end + def test_split_by_separator_recode - @ds.add_vectors_by_split_recode("a1","_") - assert_equal(%w{id name age city a1 a1_1 a1_2 a1_3},@ds.fields) - assert_equal([1,0,1,nil,1],@ds.col('a1_1').to_a) - assert_equal([1,1,0,nil,1],@ds.col('a1_2').to_a) - assert_equal([0,1,0,nil,1],@ds.col('a1_3').to_a) - {'a1_1'=>'a1:a', 'a1_2'=>'a1:b', 'a1_3'=>'a1:c'}.each do |k,v| + @ds.add_vectors_by_split_recode('a1', '_') + assert_equal(%w(id name age city a1 a1_1 a1_2 a1_3), @ds.fields) + assert_equal([1, 0, 1, nil, 1], @ds.col('a1_1').to_a) + assert_equal([1, 1, 0, nil, 1], @ds.col('a1_2').to_a) + assert_equal([0, 1, 0, nil, 1], @ds.col('a1_3').to_a) + { 'a1_1' => 'a1:a', 'a1_2' => 'a1:b', 'a1_3' => 'a1:c' }.each do |k, v| assert_equal(v, @ds[k].name) end end + def test_split_by_separator - @ds.add_vectors_by_split("a1","_") - assert_equal(%w{id name age city a1 a1_a a1_b a1_c},@ds.fields) - assert_equal([1,0,1,nil,1],@ds.col('a1_a').to_a) - assert_equal([1,1,0,nil,1],@ds.col('a1_b').to_a) - assert_equal([0,1,0,nil,1],@ds.col('a1_c').to_a) + @ds.add_vectors_by_split('a1', '_') + assert_equal(%w(id name age city a1 a1_a a1_b a1_c), @ds.fields) + assert_equal([1, 0, 1, nil, 1], @ds.col('a1_a').to_a) + assert_equal([1, 1, 0, nil, 1], @ds.col('a1_b').to_a) + assert_equal([0, 1, 0, nil, 1], @ds.col('a1_c').to_a) end + def test_percentiles - v1=(1..100).to_a.to_scale - assert_equal(50.5,v1.median) + v1 = (1..100).to_a.to_scale + assert_equal(50.5, v1.median) assert_equal(25.5, v1.percentil(25)) - v2=(1..99).to_a.to_scale - assert_equal(50,v2.median) - assert_equal(25,v2.percentil(25)) - v3=(1..50).to_a.to_scale + v2 = (1..99).to_a.to_scale + assert_equal(50, v2.median) + assert_equal(25, v2.percentil(25)) + v3 = (1..50).to_a.to_scale assert_equal(25.5, v3.median) assert_equal(13, v3.percentil(25)) - end + def test_add_case - ds=Statsample::Dataset.new({'a'=>[].to_vector, 'b'=>[].to_vector, 'c'=>[].to_vector}) - ds.add_case([1,2,3]) - ds.add_case({'a'=>4,'b'=>5,'c'=>6}) - ds.add_case([[7,8,9],%w{a b c}]) - assert_equal({'a'=>1,'b'=>2,'c'=>3},ds.case_as_hash(0)) - assert_equal([4,5,6],ds.case_as_array(1)) - assert_equal([7,8,9],ds.case_as_array(2)) - assert_equal(['a','b','c'],ds.case_as_array(3)) - ds.add_case_array([6,7,1]) + ds = Statsample::Dataset.new('a' => [].to_vector, 'b' => [].to_vector, 'c' => [].to_vector) + ds.add_case([1, 2, 3]) + ds.add_case('a' => 4, 'b' => 5, 'c' => 6) + ds.add_case([[7, 8, 9], %w(a b c)]) + assert_equal({ 'a' => 1, 'b' => 2, 'c' => 3 }, ds.case_as_hash(0)) + assert_equal([4, 5, 6], ds.case_as_array(1)) + assert_equal([7, 8, 9], ds.case_as_array(2)) + assert_equal(%w(a b c), ds.case_as_array(3)) + ds.add_case_array([6, 7, 1]) ds.update_valid_data - assert_equal([6,7,1],ds.case_as_array(4)) - + assert_equal([6, 7, 1], ds.case_as_array(4)) end + def test_marshaling - ds_marshal=Marshal.load(Marshal.dump(@ds)) - assert_equal(ds_marshal,@ds) + ds_marshal = Marshal.load(Marshal.dump(@ds)) + assert_equal(ds_marshal, @ds) end - def test_range - v1=[1,2,3,4].to_vector - v2=[5,6,7,8].to_vector - v3=[9,10,11,12].to_vector - ds1=Statsample::Dataset.new({'v1'=>v1,'v2'=>v2,'v3'=>v3}, %w{v3 v2 v1}) - assert_same(v1,ds1['v1']) - ds2=ds1["v2".."v1"] - assert_equal(%w{v2 v1},ds2.fields) - assert_same(ds1['v1'],ds2['v1']) - assert_same(ds1['v2'],ds2['v2']) - + def test_range + v1 = [1, 2, 3, 4].to_vector + v2 = [5, 6, 7, 8].to_vector + v3 = [9, 10, 11, 12].to_vector + ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2, 'v3' => v3 }, %w(v3 v2 v1)) + assert_same(v1, ds1['v1']) + ds2 = ds1['v2'..'v1'] + assert_equal(%w(v2 v1), ds2.fields) + assert_same(ds1['v1'], ds2['v1']) + assert_same(ds1['v2'], ds2['v2']) end + def test_clone - v1=[1,2,3,4].to_vector - v2=[5,6,7,8].to_vector - ds1=Statsample::Dataset.new({'v1'=>v1,'v2'=>v2}, %w{v2 v1}) - ds2=ds1.clone - assert_equal(ds1,ds2) - assert_not_same(ds1,ds2) - assert_equal(ds1['v1'],ds2['v1']) + v1 = [1, 2, 3, 4].to_vector + v2 = [5, 6, 7, 8].to_vector + ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) + ds2 = ds1.clone + assert_equal(ds1, ds2) + assert_not_same(ds1, ds2) + assert_equal(ds1['v1'], ds2['v1']) assert_same(ds1['v1'], ds2['v1']) - assert_equal(ds1.fields,ds2.fields) - assert_not_same(ds1.fields,ds2.fields) - assert_equal(ds1.cases,ds2.cases) + assert_equal(ds1.fields, ds2.fields) + assert_not_same(ds1.fields, ds2.fields) + assert_equal(ds1.cases, ds2.cases) # partial clone - ds3=ds1.clone('v1') - ds_exp=Statsample::Dataset.new({'v1'=>v1},%w{v1}) - assert_equal(ds_exp,ds3) - assert_not_same(ds_exp,ds3) - assert_equal(ds3['v1'],ds_exp['v1']) - assert_same(ds3['v1'],ds_exp['v1']) - assert_equal(ds3.fields,ds_exp.fields) - assert_equal(ds3.cases,ds_exp.cases) - - assert_not_same(ds3.fields,ds_exp.fields) + ds3 = ds1.clone('v1') + ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) + assert_equal(ds_exp, ds3) + assert_not_same(ds_exp, ds3) + assert_equal(ds3['v1'], ds_exp['v1']) + assert_same(ds3['v1'], ds_exp['v1']) + assert_equal(ds3.fields, ds_exp.fields) + assert_equal(ds3.cases, ds_exp.cases) + assert_not_same(ds3.fields, ds_exp.fields) end + def test_dup - v1=[1,2,3,4].to_vector - v2=[5,6,7,8].to_vector - ds1=Statsample::Dataset.new({'v1'=>v1,'v2'=>v2}, %w{v2 v1}) - ds2=ds1.dup - assert_equal(ds1,ds2) - assert_not_same(ds1,ds2) - assert_equal(ds1['v1'],ds2['v1']) - assert_not_same(ds1['v1'],ds2['v1']) - assert_equal(ds1.cases,ds2.cases) - - assert_equal(ds1.fields,ds2.fields) - assert_not_same(ds1.fields,ds2.fields) - ds1['v1'].type=:scale - # dup partial - ds3=ds1.dup('v1') - ds_exp=Statsample::Dataset.new({'v1'=>v1},%w{v1}) - assert_equal(ds_exp,ds3) - assert_not_same(ds_exp,ds3) - assert_equal(ds3['v1'],ds_exp['v1']) - assert_not_same(ds3['v1'],ds_exp['v1']) - assert_equal(ds3.fields,ds_exp.fields) - assert_equal(ds3.cases,ds_exp.cases) + v1 = [1, 2, 3, 4].to_vector + v2 = [5, 6, 7, 8].to_vector + ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) + ds2 = ds1.dup + assert_equal(ds1, ds2) + assert_not_same(ds1, ds2) + assert_equal(ds1['v1'], ds2['v1']) + assert_not_same(ds1['v1'], ds2['v1']) + assert_equal(ds1.cases, ds2.cases) - assert_not_same(ds3.fields,ds_exp.fields) + assert_equal(ds1.fields, ds2.fields) + assert_not_same(ds1.fields, ds2.fields) + ds1['v1'].type = :scale + # dup partial + ds3 = ds1.dup('v1') + ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) + assert_equal(ds_exp, ds3) + assert_not_same(ds_exp, ds3) + assert_equal(ds3['v1'], ds_exp['v1']) + assert_not_same(ds3['v1'], ds_exp['v1']) + assert_equal(ds3.fields, ds_exp.fields) + assert_equal(ds3.cases, ds_exp.cases) + assert_not_same(ds3.fields, ds_exp.fields) # empty - ds3=ds1.dup_empty - assert_not_equal(ds1,ds3) - assert_not_equal(ds1['v1'],ds3['v1']) - assert_equal([],ds3['v1'].data) - assert_equal([],ds3['v2'].data) - assert_equal(:scale,ds3['v1'].type) - assert_equal(ds1.fields,ds2.fields) - assert_not_same(ds1.fields,ds2.fields) + ds3 = ds1.dup_empty + assert_not_equal(ds1, ds3) + assert_not_equal(ds1['v1'], ds3['v1']) + assert_equal([], ds3['v1'].data) + assert_equal([], ds3['v2'].data) + assert_equal(:scale, ds3['v1'].type) + assert_equal(ds1.fields, ds2.fields) + assert_not_same(ds1.fields, ds2.fields) end + def test_from_to - assert_equal(%w{name age city}, @ds.from_to("name","city")) + assert_equal(%w(name age city), @ds.from_to('name', 'city')) assert_raise ArgumentError do - @ds.from_to("name","a2") + @ds.from_to('name', 'a2') end end + def test_each_array_with_nils - v1=[1,-99,3,4,"na"].to_vector(:scale,:missing_values=>[-99,"na"]) - v2=[5,6,-99,8,20].to_vector(:scale,:missing_values=>[-99]) - v3=[9,10,11,12,20].to_vector(:scale,:missing_values=>[-99]) - ds1=Statsample::Dataset.new({'v1'=>v1,'v2'=>v2,'v3'=>v3}) - ds2=ds1.dup_empty + v1 = [1, -99, 3, 4, 'na'].to_vector(:scale, missing_values: [-99, 'na']) + v2 = [5, 6, -99, 8, 20].to_vector(:scale, missing_values: [-99]) + v3 = [9, 10, 11, 12, 20].to_vector(:scale, missing_values: [-99]) + ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) + ds2 = ds1.dup_empty ds1.each_array_with_nils {|row| ds2.add_case_array(row) } ds2.update_valid_data - assert_equal([1,nil,3,4,nil],ds2['v1'].data) - assert_equal([5,6,nil,8,20],ds2['v2'].data) + assert_equal([1, nil, 3, 4, nil], ds2['v1'].data) + assert_equal([5, 6, nil, 8, 20], ds2['v2'].data) end - def test_dup_only_valid - v1=[1,nil,3,4].to_vector(:scale) - v2=[5,6,nil,8].to_vector(:scale) - v3=[9,10,11,12].to_vector(:scale) - ds1=Statsample::Dataset.new({'v1'=>v1,'v2'=>v2,'v3'=>v3}) - ds2=ds1.dup_only_valid - expected=Statsample::Dataset.new({'v1'=>[1,4].to_vector(:scale), 'v2'=> [5,8].to_vector(:scale), 'v3'=>[9, 12].to_vector(:scale)}) - assert_equal(expected,ds2) - assert_equal(expected.vectors.values,Statsample::only_valid(v1,v2,v3)) - expected_partial=Statsample::Dataset.new({'v1'=>[1,3,4].to_vector(:scale), 'v3'=>[9, 11,12].to_vector(:scale)}) - assert_equal(expected_partial, ds1.dup_only_valid(%w{v1 v3})) - + def test_dup_only_valid + v1 = [1, nil, 3, 4].to_vector(:scale) + v2 = [5, 6, nil, 8].to_vector(:scale) + v3 = [9, 10, 11, 12].to_vector(:scale) + ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) + ds2 = ds1.dup_only_valid + expected = Statsample::Dataset.new('v1' => [1, 4].to_vector(:scale), 'v2' => [5, 8].to_vector(:scale), 'v3' => [9, 12].to_vector(:scale)) + assert_equal(expected, ds2) + assert_equal(expected.vectors.values, Statsample.only_valid(v1, v2, v3)) + expected_partial = Statsample::Dataset.new('v1' => [1, 3, 4].to_vector(:scale), 'v3' => [9, 11, 12].to_vector(:scale)) + assert_equal(expected_partial, ds1.dup_only_valid(%w(v1 v3))) end + def test_filter - @ds['age'].type=:scale - filtered=@ds.filter{|c| c['id']==2 or c['id']==4} - expected=Statsample::Dataset.new({'id' => Statsample::Vector.new([2,4]), 'name'=>Statsample::Vector.new(%w{Claude Franz}), 'age'=>Statsample::Vector.new([23,27],:scale), - 'city'=>Statsample::Vector.new(['London','Paris']), - 'a1'=>Statsample::Vector.new(['b,c',nil,])}, ['id','name','age','city','a1']) - assert_equal(expected,filtered) + @ds['age'].type = :scale + filtered = @ds.filter { |c| c['id'] == 2 or c['id'] == 4 } + expected = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([2, 4]), 'name' => Statsample::Vector.new(%w(Claude Franz)), 'age' => Statsample::Vector.new([23, 27], :scale), + 'city' => Statsample::Vector.new(%w(London Paris)), + 'a1' => Statsample::Vector.new(['b,c', nil]) }, %w(id name age city a1)) + assert_equal(expected, filtered) end - def test_filter_field - @ds['age'].type=:scale - filtered=@ds.filter_field('id') {|c| c['id']==2 or c['id']==4} - expected=[2,4].to_vector - assert_equal(expected,filtered) + def test_filter_field + @ds['age'].type = :scale + filtered = @ds.filter_field('id') { |c| c['id'] == 2 or c['id'] == 4 } + expected = [2, 4].to_vector + assert_equal(expected, filtered) end + def test_verify - name=%w{r1 r2 r3 r4}.to_vector(:nominal) - v1=[1,2,3,4].to_vector(:scale) - v2=[4,3,2,1].to_vector(:scale) - v3=[10,20,30,40].to_vector(:scale) - v4=%w{a b a b}.to_vector(:nominal) - ds={'v1'=>v1,'v2'=>v2,'v3'=>v3,'v4'=>v4,'id'=>name}.to_dataset - ds.fields=%w{v1 v2 v3 v4 id} - #Correct - t1=create_test("If v4=a, v1 odd") {|r| r['v4']=='b' or (r['v4']=='a' and r['v1']%2==1)} - t2=create_test("v3=v1*10") {|r| r['v3']==r['v1']*10} + name = %w(r1 r2 r3 r4).to_vector(:nominal) + v1 = [1, 2, 3, 4].to_vector(:scale) + v2 = [4, 3, 2, 1].to_vector(:scale) + v3 = [10, 20, 30, 40].to_vector(:scale) + v4 = %w(a b a b).to_vector(:nominal) + ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'id' => name }.to_dataset + ds.fields = %w(v1 v2 v3 v4 id) + # Correct + t1 = create_test('If v4=a, v1 odd') { |r| r['v4'] == 'b' or (r['v4'] == 'a' and r['v1'].odd?) } + t2 = create_test('v3=v1*10') { |r| r['v3'] == r['v1'] * 10 } # Fail! - t3=create_test("v4='b'") {|r| r['v4']=='b'} - exp1=["1 [1]: v4='b'", "3 [3]: v4='b'"] - exp2=["1 [r1]: v4='b'", "3 [r3]: v4='b'"] - res=ds.verify(t3,t1,t2) - assert_equal(exp1,res) - res=ds.verify('id',t1,t2,t3) - assert_equal(exp2,res) + t3 = create_test("v4='b'") { |r| r['v4'] == 'b' } + exp1 = ["1 [1]: v4='b'", "3 [3]: v4='b'"] + exp2 = ["1 [r1]: v4='b'", "3 [r3]: v4='b'"] + res = ds.verify(t3, t1, t2) + assert_equal(exp1, res) + res = ds.verify('id', t1, t2, t3) + assert_equal(exp2, res) end - def test_compute_operation - v1=[1,2,3,4].to_vector(:scale) - v2=[4,3,2,1].to_vector(:scale) - v3=[10,20,30,40].to_vector(:scale) - vscale=[1.quo(2),1,3.quo(2),2].to_vector(:scale) - vsum=[1+4+10.0,2+3+20.0,3+2+30.0,4+1+40.0].to_vector(:scale) - vmult=[1*4,2*3,3*2,4*1].to_vector(:scale) - ds={'v1'=>v1,'v2'=>v2,'v3'=>v3}.to_dataset - assert_equal(vscale,ds.compute("v1/2")) - assert_equal(vsum,ds.compute("v1+v2+v3")) - assert_equal(vmult,ds.compute("v1*v2")) + def test_compute_operation + v1 = [1, 2, 3, 4].to_vector(:scale) + v2 = [4, 3, 2, 1].to_vector(:scale) + v3 = [10, 20, 30, 40].to_vector(:scale) + vscale = [1.quo(2), 1, 3.quo(2), 2].to_vector(:scale) + vsum = [1 + 4 + 10.0, 2 + 3 + 20.0, 3 + 2 + 30.0, 4 + 1 + 40.0].to_vector(:scale) + vmult = [1 * 4, 2 * 3, 3 * 2, 4 * 1].to_vector(:scale) + ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3 }.to_dataset + assert_equal(vscale, ds.compute('v1/2')) + assert_equal(vsum, ds.compute('v1+v2+v3')) + assert_equal(vmult, ds.compute('v1*v2')) end + def test_crosstab_with_asignation - v1=%w{a a a b b b c c c}.to_vector - v2=%w{a b c a b c a b c}.to_vector - v3=%w{0 1 0 0 1 1 0 0 1}.to_scale - ds=Statsample::Dataset.crosstab_by_asignation(v1,v2,v3) + v1 = %w(a a a b b b c c c).to_vector + v2 = %w(a b c a b c a b c).to_vector + v3 = %w(0 1 0 0 1 1 0 0 1).to_scale + ds = Statsample::Dataset.crosstab_by_asignation(v1, v2, v3) assert_equal(:nominal, ds['_id'].type) assert_equal(:scale, ds['a'].type) assert_equal(:scale, ds['b'].type) - ev_id=%w{a b c}.to_vector - ev_a =%w{0 0 0}.to_scale - ev_b =%w{1 1 0}.to_scale - ev_c =%w{0 1 1}.to_scale - ds2={'_id'=>ev_id, 'a'=>ev_a, 'b'=>ev_b, 'c'=>ev_c}.to_dataset + ev_id = %w(a b c).to_vector + ev_a = %w(0 0 0).to_scale + ev_b = %w(1 1 0).to_scale + ev_c = %w(0 1 1).to_scale + ds2 = { '_id' => ev_id, 'a' => ev_a, 'b' => ev_b, 'c' => ev_c }.to_dataset assert_equal(ds, ds2) end + def test_one_to_many - cases=[ - ['1','george','red',10,'blue',20,nil,nil], - ['2','fred','green',15,'orange',30,'white',20], - ['3','alfred',nil,nil,nil,nil,nil,nil] + cases = [ + ['1', 'george', 'red', 10, 'blue', 20, nil, nil], + ['2', 'fred', 'green', 15, 'orange', 30, 'white', 20], + ['3', 'alfred', nil, nil, nil, nil, nil, nil] ] - ds=Statsample::Dataset.new(%w{id name car_color1 car_value1 car_color2 car_value2 car_color3 car_value3}) - cases.each {|c| ds.add_case_array c } + ds = Statsample::Dataset.new(%w(id name car_color1 car_value1 car_color2 car_value2 car_color3 car_value3)) + cases.each { |c| ds.add_case_array c } ds.update_valid_data - ids=%w{1 1 2 2 2}.to_vector - colors=%w{red blue green orange white}.to_vector - values=[10,20,15,30,20].to_vector - col_ids=[1,2,1,2,3].to_scale - ds_expected={'id'=>ids, '_col_id'=>col_ids, 'color'=>colors, 'value'=>values}.to_dataset(['id','_col_id', 'color','value']) - assert_equal(ds_expected, ds.one_to_many(%w{id}, "car_%v%n")) - + ids = %w(1 1 2 2 2).to_vector + colors = %w(red blue green orange white).to_vector + values = [10, 20, 15, 30, 20].to_vector + col_ids = [1, 2, 1, 2, 3].to_scale + ds_expected = { 'id' => ids, '_col_id' => col_ids, 'color' => colors, 'value' => values }.to_dataset(%w(id _col_id color value)) + assert_equal(ds_expected, ds.one_to_many(%w(id), 'car_%v%n')) end - end diff --git a/test/test_dominance_analysis.rb b/test/test_dominance_analysis.rb index 2a668eb..012d1a6 100644 --- a/test/test_dominance_analysis.rb +++ b/test/test_dominance_analysis.rb @@ -1,41 +1,39 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleDominanceAnalysisTestCase < Minitest::Test def test_dominance_univariate # Example from Budescu (1993) - m=Matrix[[1, 0.683, 0.154, 0.460, 0.618],[0.683, 1, -0.050, 0.297, 0.461], [0.154, -0.050, 1, 0.006, 0.262],[0.460, 0.297, 0.006, 1, 0.507],[0.618, 0.461, 0.262, 0.507, 1]] + m = Matrix[[1, 0.683, 0.154, 0.460, 0.618], [0.683, 1, -0.050, 0.297, 0.461], [0.154, -0.050, 1, 0.006, 0.262], [0.460, 0.297, 0.006, 1, 0.507], [0.618, 0.461, 0.262, 0.507, 1]] m.extend Statsample::CovariateMatrix - m.fields=%w{x1 x2 x3 x4 y} - da=Statsample::DominanceAnalysis.new(m,'y') + m.fields = %w(x1 x2 x3 x4 y) + da = Statsample::DominanceAnalysis.new(m, 'y') - contr_x1={'x2'=>0.003, 'x3'=>0.028, 'x4'=>0.063} - contr_x1.each do |k,v| + contr_x1 = { 'x2' => 0.003, 'x3' => 0.028, 'x4' => 0.063 } + contr_x1.each do |k, v| assert_in_delta(v, da.models_data[['x1']].contributions[k], 0.001) end - assert_in_delta(0.052, da.models_data[['x2','x3','x4']].contributions['x1'], 0.001) - expected_dominances=[1, 1, 0.5, 0.5, 0,0] - expected_g_dominances=[1, 1, 1, 1, 0,0] + assert_in_delta(0.052, da.models_data[%w(x2 x3 x4)].contributions['x1'], 0.001) + expected_dominances = [1, 1, 0.5, 0.5, 0, 0] + expected_g_dominances = [1, 1, 1, 1, 0, 0] - da.pairs.each_with_index do |a,i| - assert_equal(expected_dominances[i], da.total_dominance_pairwise(a[0],a[1])) - assert_equal(expected_dominances[i], da.conditional_dominance_pairwise(a[0],a[1])) - assert_equal(expected_g_dominances[i], da.general_dominance_pairwise(a[0],a[1])) + da.pairs.each_with_index do |a, i| + assert_equal(expected_dominances[i], da.total_dominance_pairwise(a[0], a[1])) + assert_equal(expected_dominances[i], da.conditional_dominance_pairwise(a[0], a[1])) + assert_equal(expected_g_dominances[i], da.general_dominance_pairwise(a[0], a[1])) end - assert(da.summary.size>0) + assert(da.summary.size > 0) end + def test_dominance_multivariate - m=Matrix[[1.0, -0.19, -0.358, -0.343, 0.359, 0.257], [-0.19, 1.0, 0.26, 0.29, -0.11, -0.11], [-0.358, 0.26, 1.0, 0.54, -0.49, -0.23], [-0.343, 0.29, 0.54, 1.0, -0.22, -0.41], [0.359, -0.11, -0.49, -0.22, 1.0, 0.62], [0.257, -0.11, -0.23, -0.41, 0.62, 1]] + m = Matrix[[1.0, -0.19, -0.358, -0.343, 0.359, 0.257], [-0.19, 1.0, 0.26, 0.29, -0.11, -0.11], [-0.358, 0.26, 1.0, 0.54, -0.49, -0.23], [-0.343, 0.29, 0.54, 1.0, -0.22, -0.41], [0.359, -0.11, -0.49, -0.22, 1.0, 0.62], [0.257, -0.11, -0.23, -0.41, 0.62, 1]] m.extend Statsample::CovariateMatrix - m.fields=%w{y1 y2 x1 x2 x3 x4} - m2=m.submatrix(%w{y1 x1 x2 x3 x4}) - + m.fields = %w(y1 y2 x1 x2 x3 x4) + m2 = m.submatrix(%w(y1 x1 x2 x3 x4)) - da=Statsample::DominanceAnalysis.new(m, ['y1','y2'], :cases=>683, :method_association=>:p2yx) + da = Statsample::DominanceAnalysis.new(m, %w(y1 y2), cases: 683, method_association: :p2yx) - contr_x1={'x2'=>0.027, 'x3'=>0.024, 'x4'=>0.017} - contr_x1.each do |k,v| + contr_x1 = { 'x2' => 0.027, 'x3' => 0.024, 'x4' => 0.017 } + contr_x1.each do |k, v| assert_in_delta(v, da.models_data[['x1']].contributions[k], 0.003) end - - end end diff --git a/test/test_factor.rb b/test/test_factor.rb index 02dd6b6..8ca7990 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -1,222 +1,219 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -#require 'rserve' -#require 'statsample/rserve_extension' +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) +# require 'rserve' +# require 'statsample/rserve_extension' class StatsampleFactorTestCase < Minitest::Test include Statsample::Fixtures # Based on Hardle and Simar def setup - @fixtures_dir=File.expand_path(File.dirname(__FILE__)+"/fixtures") + @fixtures_dir = File.expand_path(File.dirname(__FILE__) + '/fixtures') end # Based on Hurdle example def test_covariance_matrix - ds=Statsample::PlainText.read(@fixtures_dir+"/bank2.dat", %w{v1 v2 v3 v4 v5 v6}) + ds = Statsample::PlainText.read(@fixtures_dir + '/bank2.dat', %w(v1 v2 v3 v4 v5 v6)) ds.fields.each {|f| - ds[f]=ds[f].centered + ds[f] = ds[f].centered } - cm=ds.covariance_matrix - pca =Statsample::Factor::PCA.new( cm, :m=>6) - #puts pca.summary - #puts pca.feature_matrix - exp_eig=[2.985, 0.931,0.242, 0.194, 0.085, 0.035].to_scale + cm = ds.covariance_matrix + pca = Statsample::Factor::PCA.new(cm, m: 6) + # puts pca.summary + # puts pca.feature_matrix + exp_eig = [2.985, 0.931, 0.242, 0.194, 0.085, 0.035].to_scale assert_similar_vector(exp_eig, pca.eigenvalues.to_scale, 0.1) - pcs=pca.principal_components(ds) - k=6 - comp_matrix=pca.component_matrix() + pcs = pca.principal_components(ds) + k = 6 + comp_matrix = pca.component_matrix k.times {|i| - pc_id="PC_#{i+1}" + pc_id = "PC_#{i + 1}" k.times {|j| # variable - ds_id="v#{j+1}" - r= Statsample::Bivariate.correlation(ds[ds_id], pcs[pc_id]) - assert_in_delta( r, comp_matrix[j,i]) - } + ds_id = "v#{j + 1}" + r = Statsample::Bivariate.correlation(ds[ds_id], pcs[pc_id]) + assert_in_delta(r, comp_matrix[j, i]) + } } - end - def test_principalcomponents_ruby_gsl - ran=Distribution::Normal.rng + def test_principalcomponents_ruby_gsl + ran = Distribution::Normal.rng -# @r=::Rserve::Connection.new + # @r=::Rserve::Connection.new - samples=20 - [3,5,7].each {|k| - v={} - v["x0"]=samples.times.map { ran.call()}.to_scale.centered + samples = 20 + [3, 5, 7].each {|k| + v = {} + v['x0'] = samples.times.map { ran.call }.to_scale.centered (1...k).each {|i| - v["x#{i}"]=samples.times.map {|ii| ran.call()*0.5+v["x#{i-1}"][ii]*0.5}.to_scale.centered + v["x#{i}"] = samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}"][ii] * 0.5 }.to_scale.centered } - ds=v.to_dataset - cm=ds.covariance_matrix -# @r.assign('ds',ds) -# @r.eval('cm<-cor(ds);sm<-eigen(cm, sym=TRUE);v<-sm$vectors') -# puts "eigenvalues" -# puts @r.eval('v').to_ruby.to_s - pca_ruby=Statsample::Factor::PCA.new( cm, :m=>k, :use_gsl=>false ) - pca_gsl =Statsample::Factor::PCA.new( cm, :m=>k, :use_gsl=>true ) + ds = v.to_dataset + cm = ds.covariance_matrix + # @r.assign('ds',ds) + # @r.eval('cm<-cor(ds);sm<-eigen(cm, sym=TRUE);v<-sm$vectors') + # puts "eigenvalues" + # puts @r.eval('v').to_ruby.to_s + pca_ruby = Statsample::Factor::PCA.new(cm, m: k, use_gsl: false) + pca_gsl = Statsample::Factor::PCA.new(cm, m: k, use_gsl: true) pc_ruby = pca_ruby.principal_components(ds) pc_gsl = pca_gsl.principal_components(ds) # Test component matrix correlation! - cm_ruby=pca_ruby.component_matrix - #puts cm_ruby.summary + cm_ruby = pca_ruby.component_matrix + # puts cm_ruby.summary k.times {|i| - pc_id="PC_#{i+1}" - assert_in_delta(pca_ruby.eigenvalues[i], pca_gsl.eigenvalues[i],1e-10) + pc_id = "PC_#{i + 1}" + assert_in_delta(pca_ruby.eigenvalues[i], pca_gsl.eigenvalues[i], 1e-10) # Revert gsl component values - pc_gsl_data= (pc_gsl[pc_id][0]-pc_ruby[pc_id][0]).abs>1e-6 ? pc_gsl[pc_id].recode {|v| -v} : pc_gsl[pc_id] - assert_similar_vector(pc_gsl_data, pc_ruby[pc_id], 1e-6,"PC for #{k} variables") + pc_gsl_data = (pc_gsl[pc_id][0] - pc_ruby[pc_id][0]).abs > 1e-6 ? pc_gsl[pc_id].recode(&:-@) : pc_gsl[pc_id] + assert_similar_vector(pc_gsl_data, pc_ruby[pc_id], 1e-6, "PC for #{k} variables") if false - k.times {|j| # variable - ds_id="x#{j}" - r= Statsample::Bivariate.correlation(ds[ds_id],pc_ruby[pc_id]) - puts "#{pc_id}-#{ds_id}:#{r}" - } + k.times {|j| # variable + ds_id = "x#{j}" + r = Statsample::Bivariate.correlation(ds[ds_id], pc_ruby[pc_id]) + puts "#{pc_id}-#{ds_id}:#{r}" + } end } } - #@r.close + # @r.close end - def test_principalcomponents() - principalcomponents(true) - principalcomponents(false) + def test_principalcomponents + principalcomponents(true) + principalcomponents(false) end + def principalcomponents(gsl) - ran=Distribution::Normal.rng - samples=50 - x1=samples.times.map { ran.call()}.to_scale - x2=samples.times.map {|i| ran.call()*0.5+x1[i]*0.5}.to_scale - ds={'x1'=>x1,'x2'=>x2}.to_dataset - - cm=ds.correlation_matrix - r=cm[0,1] - pca=Statsample::Factor::PCA.new(cm,:m=>2,:use_gsl=>gsl) - assert_in_delta(1+r,pca.eigenvalues[0],1e-10) - assert_in_delta(1-r,pca.eigenvalues[1],1e-10) - hs=1.0 / Math.sqrt(2) - assert_equal_vector(Vector[1, 1]*hs, pca.eigenvectors[0]) - m_1=gsl ? Vector[-1,1] : Vector[1,-1] - - assert_equal_vector(hs*m_1, pca.eigenvectors[1]) - - pcs=pca.principal_components(ds) - exp_pc_1=ds.collect_with_index {|row,i| - hs*(row['x1']+row['x2']) + ran = Distribution::Normal.rng + samples = 50 + x1 = samples.times.map { ran.call }.to_scale + x2 = samples.times.map { |i| ran.call * 0.5 + x1[i] * 0.5 }.to_scale + ds = { 'x1' => x1, 'x2' => x2 }.to_dataset + + cm = ds.correlation_matrix + r = cm[0, 1] + pca = Statsample::Factor::PCA.new(cm, m: 2, use_gsl: gsl) + assert_in_delta(1 + r, pca.eigenvalues[0], 1e-10) + assert_in_delta(1 - r, pca.eigenvalues[1], 1e-10) + hs = 1.0 / Math.sqrt(2) + assert_equal_vector(Vector[1, 1] * hs, pca.eigenvectors[0]) + m_1 = gsl ? Vector[-1, 1] : Vector[1, -1] + + assert_equal_vector(hs * m_1, pca.eigenvectors[1]) + + pcs = pca.principal_components(ds) + exp_pc_1 = ds.collect_with_index {|row, _i| + hs * (row['x1'] + row['x2']) } - exp_pc_2=ds.collect_with_index {|row,i| - gsl ? hs*(row['x2']-row['x1']) : hs*(row['x1']-row['x2']) - + exp_pc_2 = ds.collect_with_index {|row, _i| + gsl ? hs * (row['x2'] - row['x1']) : hs * (row['x1'] - row['x2']) } - assert_similar_vector(exp_pc_1, pcs["PC_1"]) - assert_similar_vector(exp_pc_2, pcs["PC_2"]) + assert_similar_vector(exp_pc_1, pcs['PC_1']) + assert_similar_vector(exp_pc_2, pcs['PC_2']) end + def test_antiimage - cor=Matrix[[1,0.964, 0.312],[0.964,1,0.411],[0.312,0.411,1]] - expected=Matrix[[0.062,-0.057, 0.074],[-0.057, 0.057, -0.089], [0.074, -0.089, 0.729]] - ai=Statsample::Factor.anti_image_covariance_matrix(cor) - assert(Matrix.equal_in_delta?(expected, ai, 0.01), "#{expected.to_s} not equal to #{ai.to_s}") + cor = Matrix[[1, 0.964, 0.312], [0.964, 1, 0.411], [0.312, 0.411, 1]] + expected = Matrix[[0.062, -0.057, 0.074], [-0.057, 0.057, -0.089], [0.074, -0.089, 0.729]] + ai = Statsample::Factor.anti_image_covariance_matrix(cor) + assert(Matrix.equal_in_delta?(expected, ai, 0.01), "#{expected} not equal to #{ai}") end - def test_kmo - @v1=[1 ,2 ,3 ,4 ,7 ,8 ,9 ,10,14,15,20,50,60,70].to_scale - @v2=[5 ,6 ,11,12,13,16,17,18,19,20,30,0,0,0].to_scale - @v3=[10,3 ,20,30,40,50,80,10,20,30,40,2,3,4].to_scale - # KMO: 0.490 - ds={'v1'=>@v1,'v2'=>@v2,'v3'=>@v3}.to_dataset - cor=Statsample::Bivariate.correlation_matrix(ds) - kmo=Statsample::Factor.kmo(cor) - assert_in_delta(0.667, kmo,0.001) - assert_in_delta(0.81, Statsample::Factor.kmo(harman_817),0.01) + def test_kmo + @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_scale + @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_scale + @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_scale + # KMO: 0.490 + ds = { 'v1' => @v1, 'v2' => @v2, 'v3' => @v3 }.to_dataset + cor = Statsample::Bivariate.correlation_matrix(ds) + kmo = Statsample::Factor.kmo(cor) + assert_in_delta(0.667, kmo, 0.001) + assert_in_delta(0.81, Statsample::Factor.kmo(harman_817), 0.01) end + def test_kmo_univariate - m=harman_817 - expected=[0.73,0.76,0.84,0.87,0.53,0.93,0.78,0.86] + m = harman_817 + expected = [0.73, 0.76, 0.84, 0.87, 0.53, 0.93, 0.78, 0.86] m.row_size.times.map {|i| - assert_in_delta(expected[i], Statsample::Factor.kmo_univariate(m,i),0.01) + assert_in_delta(expected[i], Statsample::Factor.kmo_univariate(m, i), 0.01) } end # Tested with SPSS and R def test_pca - a=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_scale - b=[2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9].to_scale - a.recode! {|c| c-a.mean} - b.recode! {|c| c-b.mean} - ds={'a'=>a,'b'=>b}.to_dataset - cov_matrix=Statsample::Bivariate.covariance_matrix(ds) - if Statsample.has_gsl? - pca=Statsample::Factor::PCA.new(cov_matrix,:use_gsl=>true) - pca_set(pca,"gsl") - else - skip("Eigenvalues could be calculated with GSL (requires gsl)") - end - pca=Statsample::Factor::PCA.new(cov_matrix,:use_gsl=>false) - pca_set(pca,"ruby") + a = [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_scale + b = [2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9].to_scale + a.recode! { |c| c - a.mean } + b.recode! { |c| c - b.mean } + ds = { 'a' => a, 'b' => b }.to_dataset + cov_matrix = Statsample::Bivariate.covariance_matrix(ds) + if Statsample.has_gsl? + pca = Statsample::Factor::PCA.new(cov_matrix, use_gsl: true) + pca_set(pca, 'gsl') + else + skip('Eigenvalues could be calculated with GSL (requires gsl)') + end + pca = Statsample::Factor::PCA.new(cov_matrix, use_gsl: false) + pca_set(pca, 'ruby') end - def pca_set(pca,type) - expected_eigenvalues=[1.284, 0.0490] - expected_eigenvalues.each_with_index{|ev,i| - assert_in_delta(ev,pca.eigenvalues[i],0.001) - } - expected_communality=[0.590, 0.694] - expected_communality.each_with_index{|ev,i| - assert_in_delta(ev,pca.communalities[i],0.001) - } - expected_cm=[0.768, 0.833] - obs=pca.component_matrix_correlation(1).column(0).to_a - expected_cm.each_with_index{|ev,i| - assert_in_delta(ev,obs[i],0.001) - } - assert(pca.summary) + def pca_set(pca, _type) + expected_eigenvalues = [1.284, 0.0490] + expected_eigenvalues.each_with_index{|ev, i| + assert_in_delta(ev, pca.eigenvalues[i], 0.001) + } + expected_communality = [0.590, 0.694] + expected_communality.each_with_index{|ev, i| + assert_in_delta(ev, pca.communalities[i], 0.001) + } + expected_cm = [0.768, 0.833] + obs = pca.component_matrix_correlation(1).column(0).to_a + expected_cm.each_with_index{|ev, i| + assert_in_delta(ev, obs[i], 0.001) + } + + assert(pca.summary) end # Tested with R def test_principalaxis - matrix=::Matrix[ - [1.0, 0.709501601093587, 0.877596585880047, 0.272219316266807], [0.709501601093587, 1.0, 0.291633797330304, 0.871141831433844], [0.877596585880047, 0.291633797330304, 1.0, -0.213373722977167], [0.272219316266807, 0.871141831433844, -0.213373722977167, 1.0]] - - - fa=Statsample::Factor::PrincipalAxis.new(matrix,:m=>1, :max_iterations=>50) + matrix = ::Matrix[ + [1.0, 0.709501601093587, 0.877596585880047, 0.272219316266807], [0.709501601093587, 1.0, 0.291633797330304, 0.871141831433844], [0.877596585880047, 0.291633797330304, 1.0, -0.213373722977167], [0.272219316266807, 0.871141831433844, -0.213373722977167, 1.0]] - cm=::Matrix[[0.923],[0.912],[0.507],[0.483]] + fa = Statsample::Factor::PrincipalAxis.new(matrix, m: 1, max_iterations: 50) - assert_equal_matrix(cm,fa.component_matrix,0.001) + cm = ::Matrix[[0.923], [0.912], [0.507], [0.483]] - h2=[0.852,0.832,0.257,0.233] - h2.each_with_index{|ev,i| - assert_in_delta(ev,fa.communalities[i],0.001) - } - eigen1=2.175 - assert_in_delta(eigen1, fa.eigenvalues[0],0.001) - assert(fa.summary.size>0) - fa=Statsample::Factor::PrincipalAxis.new(matrix,:smc=>false) - - assert_raise RuntimeError do - fa.iterate - end + assert_equal_matrix(cm, fa.component_matrix, 0.001) + h2 = [0.852, 0.832, 0.257, 0.233] + h2.each_with_index{|ev, i| + assert_in_delta(ev, fa.communalities[i], 0.001) + } + eigen1 = 2.175 + assert_in_delta(eigen1, fa.eigenvalues[0], 0.001) + assert(fa.summary.size > 0) + fa = Statsample::Factor::PrincipalAxis.new(matrix, smc: false) + + assert_raise RuntimeError do + fa.iterate + end end - def test_rotation_varimax - a = Matrix[ [ 0.4320, 0.8129, 0.3872] , - [0.7950, -0.5416, 0.2565] , - [0.5944, 0.7234, -0.3441], - [0.8945, -0.3921, -0.1863] ] - - expected= Matrix[[-0.0204423, 0.938674, -0.340334], - [0.983662, 0.0730206, 0.134997], - [0.0826106, 0.435975, -0.893379], - [0.939901, -0.0965213, -0.309596]] - varimax=Statsample::Factor::Varimax.new(a) + a = Matrix[[0.4320, 0.8129, 0.3872], + [0.7950, -0.5416, 0.2565], + [0.5944, 0.7234, -0.3441], + [0.8945, -0.3921, -0.1863]] + + expected = Matrix[[-0.0204423, 0.938674, -0.340334], + [0.983662, 0.0730206, 0.134997], + [0.0826106, 0.435975, -0.893379], + [0.939901, -0.0965213, -0.309596]] + varimax = Statsample::Factor::Varimax.new(a) assert(!varimax.rotated.nil?, "Rotated shouldn't be empty") assert(!varimax.component_transformation_matrix.nil?, "Component matrix shouldn't be empty") assert(!varimax.h2.nil?, "H2 shouldn't be empty") - assert_equal_matrix(expected,varimax.rotated,1e-6) - assert(varimax.summary.size>0) + assert_equal_matrix(expected, varimax.rotated, 1e-6) + assert(varimax.summary.size > 0) end - - end diff --git a/test/test_factor_map.rb b/test/test_factor_map.rb index 7cb0e2f..69610bc 100644 --- a/test/test_factor_map.rb +++ b/test/test_factor_map.rb @@ -1,43 +1,38 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -#require 'rserve' -#require 'statsample/rserve_extension' +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) +# require 'rserve' +# require 'statsample/rserve_extension' class StatsampleFactorMpaTestCase < Minitest::Test context Statsample::Factor::MAP do setup do - m=Matrix[ - [ 1, 0.846, 0.805, 0.859, 0.473, 0.398, 0.301, 0.382], - [ 0.846, 1, 0.881, 0.826, 0.376, 0.326, 0.277, 0.415], - [ 0.805, 0.881, 1, 0.801, 0.38, 0.319, 0.237, 0.345], - [ 0.859, 0.826, 0.801, 1, 0.436, 0.329, 0.327, 0.365], - [ 0.473, 0.376, 0.38, 0.436, 1, 0.762, 0.73, 0.629], - [ 0.398, 0.326, 0.319, 0.329, 0.762, 1, 0.583, 0.577], - [ 0.301, 0.277, 0.237, 0.327, 0.73, 0.583, 1, 0.539], - [ 0.382, 0.415, 0.345, 0.365, 0.629, 0.577, 0.539, 1] + m = Matrix[ + [1, 0.846, 0.805, 0.859, 0.473, 0.398, 0.301, 0.382], + [0.846, 1, 0.881, 0.826, 0.376, 0.326, 0.277, 0.415], + [0.805, 0.881, 1, 0.801, 0.38, 0.319, 0.237, 0.345], + [0.859, 0.826, 0.801, 1, 0.436, 0.329, 0.327, 0.365], + [0.473, 0.376, 0.38, 0.436, 1, 0.762, 0.73, 0.629], + [0.398, 0.326, 0.319, 0.329, 0.762, 1, 0.583, 0.577], + [0.301, 0.277, 0.237, 0.327, 0.73, 0.583, 1, 0.539], + [0.382, 0.415, 0.345, 0.365, 0.629, 0.577, 0.539, 1] ] - @map=Statsample::Factor::MAP.new(m) + @map = Statsample::Factor::MAP.new(m) end - should "return correct values with pure ruby" do - @map.use_gsl=false + should 'return correct values with pure ruby' do + @map.use_gsl = false map_assertions(@map) end - should_with_gsl "return correct values with gsl" do - #require 'ruby-prof' + should_with_gsl 'return correct values with gsl' do + # require 'ruby-prof' - @map.use_gsl=true + @map.use_gsl = true map_assertions(@map) end - - end def map_assertions(map) - assert_in_delta(map.minfm, 0.066445,0.00001) - assert_equal(map.number_of_factors, 2) - assert_in_delta(map.fm[0], 0.312475,0.00001) - assert_in_delta(map.fm[1], 0.245121,0.00001) + assert_in_delta(map.minfm, 0.066445, 0.00001) + assert_equal(map.number_of_factors, 2) + assert_in_delta(map.fm[0], 0.312475, 0.00001) + assert_in_delta(map.fm[1], 0.245121, 0.00001) end - - end - diff --git a/test/test_factor_pa.rb b/test/test_factor_pa.rb index c8d6a36..bc16ea2 100644 --- a/test/test_factor_pa.rb +++ b/test/test_factor_pa.rb @@ -1,52 +1,52 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) -#require 'rserve' -#require 'statsample/rserve_extension' +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) +# require 'rserve' +# require 'statsample/rserve_extension' class StatsampleFactorTestCase < Minitest::Test include Statsample::Fixtures # Based on Hardle and Simar def setup - @fixtures_dir=File.expand_path(File.dirname(__FILE__)+"/fixtures") + @fixtures_dir = File.expand_path(File.dirname(__FILE__) + '/fixtures') end + def test_parallelanalysis_with_data if Statsample.has_gsl? - samples=100 - variables=10 - iterations=50 + samples = 100 + variables = 10 + iterations = 50 rng = Distribution::Normal.rng - f1=samples.times.collect {rng.call}.to_scale - f2=samples.times.collect {rng.call}.to_scale - vectors={} + f1 = samples.times.collect { rng.call }.to_scale + f2 = samples.times.collect { rng.call }.to_scale + vectors = {} variables.times do |i| - if i<5 - vectors["v#{i}"]=samples.times.collect {|nv| - f1[nv]*5+f2[nv]*2+rng.call + if i < 5 + vectors["v#{i}"] = samples.times.collect {|nv| + f1[nv] * 5 + f2[nv] * 2 + rng.call }.to_scale else - vectors["v#{i}"]=samples.times.collect {|nv| - f2[nv]*5+f1[nv]*2+rng.call + vectors["v#{i}"] = samples.times.collect {|nv| + f2[nv] * 5 + f1[nv] * 2 + rng.call }.to_scale end - end - ds=vectors.to_dataset + ds = vectors.to_dataset - pa1=Statsample::Factor::ParallelAnalysis.new(ds, :bootstrap_method=>:data, :iterations=>iterations) - pa2=Statsample::Factor::ParallelAnalysis.with_random_data(samples,variables,:iterations=>iterations,:percentil=>95) + pa1 = Statsample::Factor::ParallelAnalysis.new(ds, bootstrap_method: :data, iterations: iterations) + pa2 = Statsample::Factor::ParallelAnalysis.with_random_data(samples, variables, iterations: iterations, percentil: 95) 3.times do |n| - var="ev_0000#{n+1}" - assert_in_delta(pa1.ds_eigenvalues[var].mean, pa2.ds_eigenvalues[var].mean,0.05) + var = "ev_0000#{n + 1}" + assert_in_delta(pa1.ds_eigenvalues[var].mean, pa2.ds_eigenvalues[var].mean, 0.05) end else - skip("Too slow without GSL") + skip('Too slow without GSL') end - end + def test_parallelanalysis - pa=Statsample::Factor::ParallelAnalysis.with_random_data(305,8,:iterations=>100,:percentil=>95) + pa = Statsample::Factor::ParallelAnalysis.with_random_data(305, 8, iterations: 100, percentil: 95) assert_in_delta(1.2454, pa.ds_eigenvalues['ev_00001'].mean, 0.01) assert_in_delta(1.1542, pa.ds_eigenvalues['ev_00002'].mean, 0.01) assert_in_delta(1.0836, pa.ds_eigenvalues['ev_00003'].mean, 0.01) - assert(pa.summary.size>0) + assert(pa.summary.size > 0) end end diff --git a/test/test_ggobi.rb b/test/test_ggobi.rb index bbe37db..88bcd96 100644 --- a/test/test_ggobi.rb +++ b/test/test_ggobi.rb @@ -1,24 +1,25 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) require 'ostruct' class StatsampleGGobiTestCase < Minitest::Test - def setup - v1=([10.2,20.3,10,20,30,40,30,20,30,40]*10).to_vector(:scale) - @v2=(%w{a b c a a a b b c d}*10).to_vector(:nominal) - @v2.labels={"a"=>"letter a","d"=>"letter d"} - v3=([1,2,3,4,5,4,3,2,1,2]*10).to_vector(:ordinal) - @ds={'v1'=>v1,'v2'=>@v2,'v3'=>v3}.to_dataset + v1 = ([10.2, 20.3, 10, 20, 30, 40, 30, 20, 30, 40] * 10).to_vector(:scale) + @v2 = (%w(a b c a a a b b c d) * 10).to_vector(:nominal) + @v2.labels = { 'a' => 'letter a', 'd' => 'letter d' } + v3 = ([1, 2, 3, 4, 5, 4, 3, 2, 1, 2] * 10).to_vector(:ordinal) + @ds = { 'v1' => v1, 'v2' => @v2, 'v3' => v3 }.to_dataset end + def test_values_definition - a=[1.0,2,"a",nil] - assert_equal("1.0 2 a NA", Statsample::GGobi.values_definition(a,"NA")) + a = [1.0, 2, 'a', nil] + assert_equal('1.0 2 a NA', Statsample::GGobi.values_definition(a, 'NA')) end + def test_variable_definition - carrier=OpenStruct.new - carrier.categorials=[] - carrier.conversions={} - real_var_definition=Statsample::GGobi.variable_definition(carrier,@v2,'variable 2',"v2") - expected=<<-EOS + carrier = OpenStruct.new + carrier.categorials = [] + carrier.conversions = {} + real_var_definition = Statsample::GGobi.variable_definition(carrier, @v2, 'variable 2', 'v2') + expected = <<-EOS letter a @@ -27,8 +28,8 @@ def test_variable_definition letter d EOS - assert_equal(expected.gsub(/\s/," "),real_var_definition.gsub(/\s/," ")) - assert_equal({'variable 2'=>{'a'=>1,'b'=>2,'c'=>3,'d'=>4}},carrier.conversions) - assert_equal(['variable 2'],carrier.categorials) + assert_equal(expected.gsub(/\s/, ' '), real_var_definition.gsub(/\s/, ' ')) + assert_equal({ 'variable 2' => { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 } }, carrier.conversions) + assert_equal(['variable 2'], carrier.categorials) end end diff --git a/test/test_gsl.rb b/test/test_gsl.rb index cff4b24..800c484 100644 --- a/test/test_gsl.rb +++ b/test/test_gsl.rb @@ -1,17 +1,15 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleGSLTestCase < Minitest::Test - should_with_gsl "matrix with gsl" do - a=[1,2,3,4,20].to_vector(:scale) - b=[3,2,3,4,50].to_vector(:scale) - c=[6,2,3,4,3].to_vector(:scale) - ds={'a'=>a,'b'=>b,'c'=>c}.to_dataset - gsl=ds.to_matrix.to_gsl - assert_equal(5,gsl.size1) - assert_equal(3,gsl.size2) - matrix=gsl.to_matrix - assert_equal(5,matrix.row_size) - assert_equal(3,matrix.column_size) + should_with_gsl 'matrix with gsl' do + a = [1, 2, 3, 4, 20].to_vector(:scale) + b = [3, 2, 3, 4, 50].to_vector(:scale) + c = [6, 2, 3, 4, 3].to_vector(:scale) + ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset + gsl = ds.to_matrix.to_gsl + assert_equal(5, gsl.size1) + assert_equal(3, gsl.size2) + matrix = gsl.to_matrix + assert_equal(5, matrix.row_size) + assert_equal(3, matrix.column_size) end end - - diff --git a/test/test_histogram.rb b/test/test_histogram.rb index c9e51b6..b68809f 100644 --- a/test/test_histogram.rb +++ b/test/test_histogram.rb @@ -1,112 +1,109 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) - +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleHistogramTestCase < Minitest::Test context Statsample::Histogram do - should "alloc correctly with integer" do + should 'alloc correctly with integer' do h = Statsample::Histogram.alloc(4) - assert_equal([0.0]*4, h.bin) - assert_equal([0.0]*5, h.range) + assert_equal([0.0] * 4, h.bin) + assert_equal([0.0] * 5, h.range) end - should "alloc correctly with array" do + should 'alloc correctly with array' do h = Statsample::Histogram.alloc([1, 3, 7, 9, 20]) - assert_equal([0.0]*4, h.bin) - assert_equal([1,3,7,9,20], h.range) + assert_equal([0.0] * 4, h.bin) + assert_equal([1, 3, 7, 9, 20], h.range) end - should "alloc correctly with integer and min, max array" do + should 'alloc correctly with integer and min, max array' do h = Statsample::Histogram.alloc(5, [0, 5]) - assert_equal([0.0,1.0,2.0,3.0,4.0,5.0], h.range) - assert_equal([0.0]*5,h.bin) + assert_equal([0.0, 1.0, 2.0, 3.0, 4.0, 5.0], h.range) + assert_equal([0.0] * 5, h.bin) end - should "bin() method return correct number of bins" do + should 'bin() method return correct number of bins' do h = Statsample::Histogram.alloc(4) - assert_equal(4,h.bins) + assert_equal(4, h.bins) end - should "increment correctly" do + should 'increment correctly' do h = Statsample::Histogram.alloc(5, [0, 5]) h.increment 2.5 - assert_equal([0.0,0.0,1.0,0.0,0.0], h.bin) - h.increment [0.5,0.5,3.5,3.5] - assert_equal([2.0,0.0,1.0,2.0,0.0], h.bin) + assert_equal([0.0, 0.0, 1.0, 0.0, 0.0], h.bin) + h.increment [0.5, 0.5, 3.5, 3.5] + assert_equal([2.0, 0.0, 1.0, 2.0, 0.0], h.bin) h.increment 0 - assert_equal([3.0,0.0,1.0,2.0,0.0], h.bin) + assert_equal([3.0, 0.0, 1.0, 2.0, 0.0], h.bin) h.increment 5 - assert_equal([3.0,0.0,1.0,2.0,0.0], h.bin) + assert_equal([3.0, 0.0, 1.0, 2.0, 0.0], h.bin) end - should "alloc_uniform correctly with n, min,max" do - h = Statsample::Histogram.alloc_uniform(5,0,10) - assert_equal(5,h.bins) - assert_equal([0.0]*5,h.bin) - assert_equal([0.0,2.0,4.0,6.0,8.0,10.0], h.range) + should 'alloc_uniform correctly with n, min,max' do + h = Statsample::Histogram.alloc_uniform(5, 0, 10) + assert_equal(5, h.bins) + assert_equal([0.0] * 5, h.bin) + assert_equal([0.0, 2.0, 4.0, 6.0, 8.0, 10.0], h.range) end - should "alloc_uniform correctly with n, [min,max]" do + should 'alloc_uniform correctly with n, [min,max]' do h = Statsample::Histogram.alloc_uniform(5, [0, 10]) - assert_equal(5,h.bins) - assert_equal([0.0]*5,h.bin) - assert_equal([0.0,2.0,4.0,6.0,8.0,10.0], h.range) + assert_equal(5, h.bins) + assert_equal([0.0] * 5, h.bin) + assert_equal([0.0, 2.0, 4.0, 6.0, 8.0, 10.0], h.range) end - should "get_range()" do - h = Statsample::Histogram.alloc_uniform(5,2,12) + should 'get_range()' do + h = Statsample::Histogram.alloc_uniform(5, 2, 12) 5.times {|i| - assert_equal([2+i*2, 4+i*2], h.get_range(i)) + assert_equal([2 + i * 2, 4 + i * 2], h.get_range(i)) + } + end + should 'min() and max()' do + h = Statsample::Histogram.alloc_uniform(5, 2, 12) + assert_equal(2, h.min) + assert_equal(12, h.max) + end + should 'max_val()' do + h = Statsample::Histogram.alloc(5, [0, 5]) + 100.times { h.increment(rand * 5) } + max = h.bin[0] + (1..4).each {|i| + max = h.bin[i] if h.bin[i] > max + } + assert_equal(max, h.max_val) + end + should 'min_val()' do + h = Statsample::Histogram.alloc(5, [0, 5]) + 100.times { h.increment(rand * 5) } + min = h.bin[0] + (1..4).each {|i| + min = h.bin[i] if h.bin[i] < min } + assert_equal(min, h.min_val) end - should "min() and max()" do - h=Statsample::Histogram.alloc_uniform(5,2,12) - assert_equal(2,h.min) - assert_equal(12,h.max) - end - should "max_val()" do - h = Statsample::Histogram.alloc(5, [0, 5]) - 100.times {h.increment(rand*5)} - max=h.bin[0] - (1..4).each {|i| - max = h.bin[i] if h.bin[i] > max - } - assert_equal(max,h.max_val) - end - should "min_val()" do - h = Statsample::Histogram.alloc(5, [0, 5]) - 100.times {h.increment(rand*5)} - min=h.bin[0] - (1..4).each {|i| - min = h.bin[i] if h.bin[i]x1,'x2'=>x2}.to_dataset - ds.name="test" - obs=m.to_dataset - assert_equal(ds['x1'],obs['x1']) - assert_equal(ds['x2'],obs['x2']) - assert_equal(ds['x1'].mean,obs['x1'].mean) - - + m.fields_y = %w(x1 x2) + m.name = 'test' + samples = 100 + x1 = [1, 2, 3].to_scale + x2 = [4, 5, 6].to_scale + ds = { 'x1' => x1, 'x2' => x2 }.to_dataset + ds.name = 'test' + obs = m.to_dataset + assert_equal(ds['x1'], obs['x1']) + assert_equal(ds['x2'], obs['x2']) + assert_equal(ds['x1'].mean, obs['x1'].mean) end + def test_covariate - a=Matrix[[1.0, 0.3, 0.2], [0.3, 1.0, 0.5], [0.2, 0.5, 1.0]] + a = Matrix[[1.0, 0.3, 0.2], [0.3, 1.0, 0.5], [0.2, 0.5, 1.0]] a.extend Statsample::CovariateMatrix - a.fields=%w{a b c} + a.fields = %w(a b c) assert_equal(:correlation, a._type) - assert_equal(Matrix[[0.5],[0.3]], a.submatrix(%w{c a}, %w{b})) - assert_equal(Matrix[[1.0, 0.2] , [0.2, 1.0]], a.submatrix(%w{c a})) - assert_equal(:correlation, a.submatrix(%w{c a})._type) + assert_equal(Matrix[[0.5], [0.3]], a.submatrix(%w(c a), %w(b))) + assert_equal(Matrix[[1.0, 0.2], [0.2, 1.0]], a.submatrix(%w(c a))) + assert_equal(:correlation, a.submatrix(%w(c a))._type) - a=Matrix[[20,30,10], [30,60,50], [10,50,50]] + a = Matrix[[20, 30, 10], [30, 60, 50], [10, 50, 50]] a.extend Statsample::CovariateMatrix assert_equal(:covariance, a._type) - a=50.times.collect {rand()}.to_scale - b=50.times.collect {rand()}.to_scale - c=50.times.collect {rand()}.to_scale - ds={'a'=>a,'b'=>b,'c'=>c}.to_dataset - corr=Statsample::Bivariate.correlation_matrix(ds) - real=Statsample::Bivariate.covariance_matrix(ds).correlation + a = 50.times.collect { rand }.to_scale + b = 50.times.collect { rand }.to_scale + c = 50.times.collect { rand }.to_scale + ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset + corr = Statsample::Bivariate.correlation_matrix(ds) + real = Statsample::Bivariate.covariance_matrix(ds).correlation corr.row_size.times do |i| corr.column_size.times do |j| - assert_in_delta(corr[i,j], real[i,j],1e-15) + assert_in_delta(corr[i, j], real[i, j], 1e-15) end end end diff --git a/test/test_multiset.rb b/test/test_multiset.rb index bd676f2..d443cff 100644 --- a/test/test_multiset.rb +++ b/test/test_multiset.rb @@ -1,158 +1,164 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) - +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleMultisetTestCase < Minitest::Test def setup - @x=%w{a a a a b b b b}.to_vector - @y=[1,2,3,4,5,6,7,8].to_scale - @z=[10,11,12,13,14,15,16,17].to_scale - @ds={'x'=>@x,'y'=>@y,'z'=>@z}.to_dataset - @ms=@ds.to_multiset_by_split('x') + @x = %w(a a a a b b b b).to_vector + @y = [1, 2, 3, 4, 5, 6, 7, 8].to_scale + @z = [10, 11, 12, 13, 14, 15, 16, 17].to_scale + @ds = { 'x' => @x, 'y' => @y, 'z' => @z }.to_dataset + @ms = @ds.to_multiset_by_split('x') end + def test_creation - v1a=[1,2,3,4,5].to_vector - v2b=[11,21,31,41,51].to_vector - v3c=[21,23,34,45,56].to_vector - ds1={'v1'=>v1a,'v2'=>v2b,'v3'=>v3c}.to_dataset - v1b=[15,25,35,45,55].to_vector - v2b=[11,21,31,41,51].to_vector - v3b=[21,23,34,45,56].to_vector - ds2={'v1'=>v1b,'v2'=>v2b,'v3'=>v3b}.to_dataset - ms=Statsample::Multiset.new(['v1','v2','v3']) - ms.add_dataset('ds1',ds1) - ms.add_dataset('ds2',ds2) - assert_equal(ds1,ms['ds1']) - assert_equal(ds2,ms['ds2']) - assert_equal(v1a,ms['ds1']['v1']) - assert_not_equal(v1b,ms['ds1']['v1']) - ds3={'v1'=>v1b,'v2'=>v2b}.to_dataset + v1a = [1, 2, 3, 4, 5].to_vector + v2b = [11, 21, 31, 41, 51].to_vector + v3c = [21, 23, 34, 45, 56].to_vector + ds1 = { 'v1' => v1a, 'v2' => v2b, 'v3' => v3c }.to_dataset + v1b = [15, 25, 35, 45, 55].to_vector + v2b = [11, 21, 31, 41, 51].to_vector + v3b = [21, 23, 34, 45, 56].to_vector + ds2 = { 'v1' => v1b, 'v2' => v2b, 'v3' => v3b }.to_dataset + ms = Statsample::Multiset.new(%w(v1 v2 v3)) + ms.add_dataset('ds1', ds1) + ms.add_dataset('ds2', ds2) + assert_equal(ds1, ms['ds1']) + assert_equal(ds2, ms['ds2']) + assert_equal(v1a, ms['ds1']['v1']) + assert_not_equal(v1b, ms['ds1']['v1']) + ds3 = { 'v1' => v1b, 'v2' => v2b }.to_dataset assert_raise ArgumentError do ms.add_dataset(ds3) end end + def test_creation_empty - ms=Statsample::Multiset.new_empty_vectors(%w{id age name},%w{male female}) - ds_male={'id'=>[].to_vector,'age'=>[].to_vector, 'name'=>[].to_vector}.to_dataset(%w{id age name}) - ds_female={'id'=>[].to_vector,'age'=>[].to_vector, 'name'=>[].to_vector}.to_dataset(%w{id age name}) - ms2=Statsample::Multiset.new(%w{id age name}) - ms2.add_dataset('male',ds_male) - ms2.add_dataset('female',ds_female) - assert_equal(ms2.fields,ms.fields) - assert_equal(ms2['male'],ms['male']) - assert_equal(ms2['female'],ms['female']) + ms = Statsample::Multiset.new_empty_vectors(%w(id age name), %w(male female)) + ds_male = { 'id' => [].to_vector, 'age' => [].to_vector, 'name' => [].to_vector }.to_dataset(%w(id age name)) + ds_female = { 'id' => [].to_vector, 'age' => [].to_vector, 'name' => [].to_vector }.to_dataset(%w(id age name)) + ms2 = Statsample::Multiset.new(%w(id age name)) + ms2.add_dataset('male', ds_male) + ms2.add_dataset('female', ds_female) + assert_equal(ms2.fields, ms.fields) + assert_equal(ms2['male'], ms['male']) + assert_equal(ms2['female'], ms['female']) end + def test_to_multiset_by_split_one - sex=%w{m m m m m f f f f m}.to_vector(:nominal) - city=%w{London Paris NY London Paris NY London Paris NY Tome}.to_vector(:nominal) - age=[10,10,20,30,34,34,33,35,36,40].to_vector(:scale) - ds={'sex'=>sex,'city'=>city,'age'=>age}.to_dataset - ms=ds.to_multiset_by_split('sex') - assert_equal(2,ms.n_datasets) - assert_equal(%w{f m},ms.datasets.keys.sort) - assert_equal(6,ms['m'].cases) - assert_equal(4,ms['f'].cases) - assert_equal(%w{London Paris NY London Paris Tome},ms['m']['city'].to_a) - assert_equal([34,33,35,36],ms['f']['age'].to_a) + sex = %w(m m m m m f f f f m).to_vector(:nominal) + city = %w(London Paris NY London Paris NY London Paris NY Tome).to_vector(:nominal) + age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:scale) + ds = { 'sex' => sex, 'city' => city, 'age' => age }.to_dataset + ms = ds.to_multiset_by_split('sex') + assert_equal(2, ms.n_datasets) + assert_equal(%w(f m), ms.datasets.keys.sort) + assert_equal(6, ms['m'].cases) + assert_equal(4, ms['f'].cases) + assert_equal(%w(London Paris NY London Paris Tome), ms['m']['city'].to_a) + assert_equal([34, 33, 35, 36], ms['f']['age'].to_a) end + def test_to_multiset_by_split_multiple - sex=%w{m m m m m m m m m m f f f f f f f f f f}.to_vector(:nominal) - city=%w{London London London Paris Paris London London London Paris Paris London London London Paris Paris London London London Paris Paris}.to_vector(:nominal) - hair=%w{blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black}.to_vector(:nominal) - age=[10,10,20,30,34,34,33,35,36,40, 10,10,20,30,34,34,33,35,36,40].to_vector(:scale) - ds={'sex'=>sex,'city'=>city,'hair'=>hair,'age'=>age}.to_dataset(%w{sex city hair age}) - ms=ds.to_multiset_by_split('sex','city','hair') - assert_equal(8,ms.n_datasets) - assert_equal(3,ms[%w{m London blonde}].cases) - assert_equal(3,ms[%w{m London blonde}].cases) - assert_equal(1,ms[%w{m Paris black}].cases) + sex = %w(m m m m m m m m m m f f f f f f f f f f).to_vector(:nominal) + city = %w(London London London Paris Paris London London London Paris Paris London London London Paris Paris London London London Paris Paris).to_vector(:nominal) + hair = %w(blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black).to_vector(:nominal) + age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40, 10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:scale) + ds = { 'sex' => sex, 'city' => city, 'hair' => hair, 'age' => age }.to_dataset(%w(sex city hair age)) + ms = ds.to_multiset_by_split('sex', 'city', 'hair') + assert_equal(8, ms.n_datasets) + assert_equal(3, ms[%w(m London blonde)].cases) + assert_equal(3, ms[%w(m London blonde)].cases) + assert_equal(1, ms[%w(m Paris black)].cases) end def test_stratum_proportion - ds1={'q1'=>[1,1,1,1,1,0,0,0,0,0,0,0].to_vector}.to_dataset - ds2={'q1'=>[1,1,1,1,1,1,1,0,0].to_vector}.to_dataset - assert_equal(5.0/12, ds1['q1'].proportion ) - assert_equal(7.0/9, ds2['q1'].proportion ) - ms=Statsample::Multiset.new(['q1']) - ms.add_dataset('d1',ds1) - ms.add_dataset('d2',ds2) - ss=Statsample::StratifiedSample.new(ms,{'d1'=>50,'d2'=>100}) - assert_in_delta(0.655, ss.proportion('q1'),0.01) - assert_in_delta(0.345, ss.proportion('q1',0),0.01) - + ds1 = { 'q1' => [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0].to_vector }.to_dataset + ds2 = { 'q1' => [1, 1, 1, 1, 1, 1, 1, 0, 0].to_vector }.to_dataset + assert_equal(5.0 / 12, ds1['q1'].proportion) + assert_equal(7.0 / 9, ds2['q1'].proportion) + ms = Statsample::Multiset.new(['q1']) + ms.add_dataset('d1', ds1) + ms.add_dataset('d2', ds2) + ss = Statsample::StratifiedSample.new(ms, 'd1' => 50, 'd2' => 100) + assert_in_delta(0.655, ss.proportion('q1'), 0.01) + assert_in_delta(0.345, ss.proportion('q1', 0), 0.01) end + def test_stratum_scale - boys={'test'=>[50, 55, 60, 62, 62, 65, 67, 67, 70, 70, 73, 73, 75, 78, 78, 80, 85, 90].to_vector(:scale)}.to_dataset - girls={'test'=>[70, 70, 72, 72, 75, 75, 78, 78, 80, 80, 82, 82, 85, 85, 88, 88, 90, 90].to_vector(:scale)}.to_dataset - ms=Statsample::Multiset.new(['test']) - ms.add_dataset('boys',boys) - ms.add_dataset('girls',girls) - ss=Statsample::StratifiedSample.new(ms,{'boys'=>10000,'girls'=>10000}) - assert_equal(2,ss.strata_number) - assert_equal(20000,ss.population_size) - assert_equal(10000,ss.stratum_size('boys')) - assert_equal(10000,ss.stratum_size('girls')) - assert_equal(36,ss.sample_size) - assert_equal(75,ss.mean('test')) - assert_in_delta(1.45,ss.standard_error_wor('test'),0.01) - assert_in_delta(ss.standard_error_wor('test'), ss.standard_error_wor_2('test'),0.00001) + boys = { 'test' => [50, 55, 60, 62, 62, 65, 67, 67, 70, 70, 73, 73, 75, 78, 78, 80, 85, 90].to_vector(:scale) }.to_dataset + girls = { 'test' => [70, 70, 72, 72, 75, 75, 78, 78, 80, 80, 82, 82, 85, 85, 88, 88, 90, 90].to_vector(:scale) }.to_dataset + ms = Statsample::Multiset.new(['test']) + ms.add_dataset('boys', boys) + ms.add_dataset('girls', girls) + ss = Statsample::StratifiedSample.new(ms, 'boys' => 10_000, 'girls' => 10_000) + assert_equal(2, ss.strata_number) + assert_equal(20_000, ss.population_size) + assert_equal(10_000, ss.stratum_size('boys')) + assert_equal(10_000, ss.stratum_size('girls')) + assert_equal(36, ss.sample_size) + assert_equal(75, ss.mean('test')) + assert_in_delta(1.45, ss.standard_error_wor('test'), 0.01) + assert_in_delta(ss.standard_error_wor('test'), ss.standard_error_wor_2('test'), 0.00001) end + def test_each - xpe={ - 'a'=>%w{a a a a}.to_vector, - 'b'=>%w{b b b b}.to_vector + xpe = { + 'a' => %w(a a a a).to_vector, + 'b' => %w(b b b b).to_vector } - ype={ - 'a'=>[1,2,3,4].to_scale, - 'b'=>[5,6,7,8].to_scale, + ype = { + 'a' => [1, 2, 3, 4].to_scale, + 'b' => [5, 6, 7, 8].to_scale } - zpe={ - 'a'=>[10,11,12,13].to_scale, - 'b'=>[14,15,16,17].to_scale, + zpe = { + 'a' => [10, 11, 12, 13].to_scale, + 'b' => [14, 15, 16, 17].to_scale } - xp,yp,zp=Hash.new(),Hash.new(),Hash.new() - @ms.each {|k,ds| - xp[k]=ds['x'] - yp[k]=ds['y'] - zp[k]=ds['z'] + xp, yp, zp = {}, {}, {} + @ms.each {|k, ds| + xp[k] = ds['x'] + yp[k] = ds['y'] + zp[k] = ds['z'] } - assert_equal(xpe,xp) - assert_equal(ype,yp) - assert_equal(zpe,zp) - + assert_equal(xpe, xp) + assert_equal(ype, yp) + assert_equal(zpe, zp) end - def test_multiset_union_with_block - r1=rand() - r2=rand() - ye=[1*r1,2*r1,3*r1,4*r1,5*r2,6*r2,7*r2,8*r2].to_scale + def test_multiset_union_with_block + r1 = rand + r2 = rand + ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_scale - ze=[10*r1,11*r1,12*r1,13*r1, 14*r2,15*r2,16*r2,17*r2].to_scale + ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_scale - ds2=@ms.union {|k,ds| + ds2 = @ms.union {|k, ds| ds['y'].recode!{|v| - k=='a' ? v*r1 : v*r2} + k == 'a' ? v * r1 : v * r2 + } ds['z'].recode!{|v| - k=='a' ? v*r1 : v*r2} + k == 'a' ? v * r1 : v * r2 + } } - assert_equal(ye,ds2['y']) - assert_equal(ze,ds2['z']) + assert_equal(ye, ds2['y']) + assert_equal(ze, ds2['z']) end + def test_multiset_union - r1=rand() - r2=rand() - ye=[1*r1,2*r1,3*r1,4*r1,5*r2,6*r2,7*r2,8*r2].to_scale + r1 = rand + r2 = rand + ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_scale - ze=[10*r1,11*r1,12*r1,13*r1, 14*r2,15*r2,16*r2,17*r2].to_scale - @ms.each {|k,ds| + ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_scale + @ms.each {|k, ds| ds['y'].recode!{|v| - k=='a' ? v*r1 : v*r2} + k == 'a' ? v * r1 : v * r2 + } ds['z'].recode!{|v| - k=='a' ? v*r1 : v*r2} - + k == 'a' ? v * r1 : v * r2 + } } - ds2=@ms.union - assert_equal(ye,ds2['y']) - assert_equal(ze,ds2['z']) - + ds2 = @ms.union + assert_equal(ye, ds2['y']) + assert_equal(ze, ds2['z']) end end diff --git a/test/test_regression.rb b/test/test_regression.rb index 03aa430..db70fba 100644 --- a/test/test_regression.rb +++ b/test/test_regression.rb @@ -1,215 +1,210 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleRegressionTestCase < Minitest::Test - context "Example with missing data" do + context 'Example with missing data' do setup do - @x=[0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857].to_scale + @x = [0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857].to_scale - @y=[nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_scale - @ds={'x'=>@x,'y'=>@y}.to_dataset - @lr=Statsample::Regression::Multiple::RubyEngine.new(@ds,'y') + @y = [nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_scale + @ds = { 'x' => @x, 'y' => @y }.to_dataset + @lr = Statsample::Regression::Multiple::RubyEngine.new(@ds, 'y') + end + should 'have correct values' do + assert_in_delta(0.455, @lr.r2, 0.001) + assert_in_delta(0.427, @lr.r2_adjusted, 0.001) + assert_in_delta(0.1165, @lr.se_estimate, 0.001) + assert_in_delta(15.925, @lr.f, 0.0001) + assert_in_delta(0.675, @lr.standarized_coeffs['x'], 0.001) + assert_in_delta(0.778, @lr.coeffs['x'], 0.001, 'coeff x') + assert_in_delta(0.132, @lr.constant, 0.001, 'constant') + assert_in_delta(0.195, @lr.coeffs_se['x'], 0.001, 'coeff x se') + assert_in_delta(0.064, @lr.constant_se, 0.001, 'constant se') end - should "have correct values" do - assert_in_delta(0.455,@lr.r2,0.001) - assert_in_delta(0.427,@lr.r2_adjusted, 0.001) - assert_in_delta(0.1165,@lr.se_estimate,0.001) - assert_in_delta(15.925,@lr.f,0.0001) - assert_in_delta(0.675, @lr.standarized_coeffs['x'],0.001) - assert_in_delta(0.778, @lr.coeffs['x'],0.001, "coeff x") - assert_in_delta(0.132, @lr.constant,0.001,"constant") - assert_in_delta(0.195, @lr.coeffs_se['x'],0.001,"coeff x se") - assert_in_delta(0.064, @lr.constant_se,0.001,"constant se") - end end - should "return an error if data is linearly dependent" do - samples=100 + should 'return an error if data is linearly dependent' do + samples = 100 - a,b=rand,rand + a, b = rand, rand - x1=samples.times.map { rand}.to_scale - x2=samples.times.map {rand}.to_scale - x3=samples.times.map {|i| x1[i]*(1+a)+x2[i]*(1+b)}.to_scale - y=samples.times.map {|i| x1[i]+x2[i]+x3[i]+rand}.to_scale + x1 = samples.times.map { rand }.to_scale + x2 = samples.times.map { rand }.to_scale + x3 = samples.times.map { |i| x1[i] * (1 + a) + x2[i] * (1 + b) }.to_scale + y = samples.times.map { |i| x1[i] + x2[i] + x3[i] + rand }.to_scale - ds={'x1'=>x1,'x2'=>x2,'x3'=>x3,'y'=>y}.to_dataset + ds = { 'x1' => x1, 'x2' => x2, 'x3' => x3, 'y' => y }.to_dataset assert_raise(Statsample::Regression::LinearDependency) { - Statsample::Regression::Multiple::RubyEngine.new(ds,'y') + Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') } end def test_parameters - @x=[13,20,10,33,15].to_vector(:scale) - @y=[23,18,35,10,27 ].to_vector(:scale) - reg=Statsample::Regression::Simple.new_from_vectors(@x,@y) + @x = [13, 20, 10, 33, 15].to_vector(:scale) + @y = [23, 18, 35, 10, 27].to_vector(:scale) + reg = Statsample::Regression::Simple.new_from_vectors(@x, @y) _test_simple_regression(reg) - ds={'x'=>@x,'y'=>@y}.to_dataset - reg=Statsample::Regression::Simple.new_from_dataset(ds,'x','y') + ds = { 'x' => @x, 'y' => @y }.to_dataset + reg = Statsample::Regression::Simple.new_from_dataset(ds, 'x', 'y') _test_simple_regression(reg) - reg=Statsample::Regression.simple(@x,@y) + reg = Statsample::Regression.simple(@x, @y) _test_simple_regression(reg) - end - def _test_simple_regression(reg) - assert_in_delta(40.009, reg.a,0.001) - assert_in_delta(-0.957, reg.b,0.001) - assert_in_delta(4.248,reg.standard_error,0.002) + def _test_simple_regression(reg) + assert_in_delta(40.009, reg.a, 0.001) + assert_in_delta(-0.957, reg.b, 0.001) + assert_in_delta(4.248, reg.standard_error, 0.002) assert(reg.summary) end def test_summaries - a=10.times.map{rand(100)}.to_scale - b=10.times.map{rand(100)}.to_scale - y=10.times.map{rand(100)}.to_scale - ds={'a'=>a,'b'=>b,'y'=>y}.to_dataset - lr=Statsample::Regression::Multiple::RubyEngine.new(ds,'y') - assert(lr.summary.size>0) + a = 10.times.map { rand(100) }.to_scale + b = 10.times.map { rand(100) }.to_scale + y = 10.times.map { rand(100) }.to_scale + ds = { 'a' => a, 'b' => b, 'y' => y }.to_dataset + lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') + assert(lr.summary.size > 0) end + def test_multiple_dependent - complete=Matrix[ - [1,0.53,0.62,0.19,-0.09,0.08,0.02,-0.12,0.08], - [0.53,1,0.61,0.23,0.1,0.18,0.02,-0.1,0.15], - [0.62,0.61,1,0.03,0.1,0.12,0.03,-0.06,0.12], - [0.19,0.23,0.03,1,-0.02,0.02,0,-0.02,-0.02], - [-0.09,0.1,0.1,-0.02,1,0.05,0.06,0.18,0.02], - [0.08,0.18,0.12,0.02,0.05,1,0.22,-0.07,0.36], - [0.02,0.02,0.03,0,0.06,0.22,1,-0.01,-0.05], - [-0.12,-0.1,-0.06,-0.02,0.18,-0.07,-0.01,1,-0.03], - [0.08,0.15,0.12,-0.02,0.02,0.36,-0.05,-0.03,1]] + complete = Matrix[ + [1, 0.53, 0.62, 0.19, -0.09, 0.08, 0.02, -0.12, 0.08], + [0.53, 1, 0.61, 0.23, 0.1, 0.18, 0.02, -0.1, 0.15], + [0.62, 0.61, 1, 0.03, 0.1, 0.12, 0.03, -0.06, 0.12], + [0.19, 0.23, 0.03, 1, -0.02, 0.02, 0, -0.02, -0.02], + [-0.09, 0.1, 0.1, -0.02, 1, 0.05, 0.06, 0.18, 0.02], + [0.08, 0.18, 0.12, 0.02, 0.05, 1, 0.22, -0.07, 0.36], + [0.02, 0.02, 0.03, 0, 0.06, 0.22, 1, -0.01, -0.05], + [-0.12, -0.1, -0.06, -0.02, 0.18, -0.07, -0.01, 1, -0.03], + [0.08, 0.15, 0.12, -0.02, 0.02, 0.36, -0.05, -0.03, 1]] complete.extend Statsample::CovariateMatrix - complete.fields=%w{adhd cd odd sex age monly mwork mage poverty} - - lr=Statsample::Regression::Multiple::MultipleDependent.new(complete, %w{adhd cd odd}) - + complete.fields = %w(adhd cd odd sex age monly mwork mage poverty) - assert_in_delta(0.197, lr.r2yx,0.001) - assert_in_delta(0.197, lr.r2yx_covariance,0.001) - assert_in_delta(0.07, lr.p2yx,0.001) + lr = Statsample::Regression::Multiple::MultipleDependent.new(complete, %w(adhd cd odd)) + assert_in_delta(0.197, lr.r2yx, 0.001) + assert_in_delta(0.197, lr.r2yx_covariance, 0.001) + assert_in_delta(0.07, lr.p2yx, 0.001) end def test_multiple_regression_pairwise_2 - @a=[1,3,2,4,3,5,4,6,5,7,3,nil,3,nil,3].to_vector(:scale) - @b=[3,3,4,4,5,5,6,6,4,4,2,2,nil,6,2].to_vector(:scale) - @c=[11,22,30,40,50,65,78,79,99,100,nil,3,7,nil,7].to_vector(:scale) - @y=[3,4,5,6,7,8,9,10,20,30,30,40,nil,50,nil].to_vector(:scale) - ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset - lr=Statsample::Regression::Multiple::RubyEngine.new(ds,'y') - assert_in_delta(2407.436,lr.sst,0.001) - assert_in_delta(0.752,lr.r,0.001, "pairwise r") - assert_in_delta(0.565,lr.r2,0.001) - assert_in_delta(1361.130,lr.ssr,0.001) - assert_in_delta(1046.306,lr.sse,0.001) - assert_in_delta(3.035,lr.f,0.001) + @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 3, nil, 3, nil, 3].to_vector(:scale) + @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4, 2, 2, nil, 6, 2].to_vector(:scale) + @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100, nil, 3, 7, nil, 7].to_vector(:scale) + @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 30, 40, nil, 50, nil].to_vector(:scale) + ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset + lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') + assert_in_delta(2407.436, lr.sst, 0.001) + assert_in_delta(0.752, lr.r, 0.001, 'pairwise r') + assert_in_delta(0.565, lr.r2, 0.001) + assert_in_delta(1361.130, lr.ssr, 0.001) + assert_in_delta(1046.306, lr.sse, 0.001) + assert_in_delta(3.035, lr.f, 0.001) end - def test_multiple_regression_gsl if Statsample.has_gsl? - @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:scale) - @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:scale) - @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:scale) - @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:scale) - ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset - lr=Statsample::Regression::Multiple::GslEngine.new(ds,'y') - assert(lr.summary.size>0) - model_test(lr,'gsl') - predicted=[1.7857, 6.0989, 3.2433, 7.2908, 4.9667, 10.3428, 8.8158, 10.4717, 23.6639, 25.3198] - c_predicted=lr.predicted + @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:scale) + @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:scale) + @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:scale) + @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:scale) + ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset + lr = Statsample::Regression::Multiple::GslEngine.new(ds, 'y') + assert(lr.summary.size > 0) + model_test(lr, 'gsl') + predicted = [1.7857, 6.0989, 3.2433, 7.2908, 4.9667, 10.3428, 8.8158, 10.4717, 23.6639, 25.3198] + c_predicted = lr.predicted predicted.each_index{|i| - assert_in_delta(predicted[i],c_predicted[i],0.001) + assert_in_delta(predicted[i], c_predicted[i], 0.001) } - residuals=[1.2142, -2.0989, 1.7566, -1.29085, 2.033, -2.3428, 0.18414, -0.47177, -3.66395, 4.6801] - c_residuals=lr.residuals + residuals = [1.2142, -2.0989, 1.7566, -1.29085, 2.033, -2.3428, 0.18414, -0.47177, -3.66395, 4.6801] + c_residuals = lr.residuals residuals.each_index{|i| - assert_in_delta(residuals[i],c_residuals[i],0.001) + assert_in_delta(residuals[i], c_residuals[i], 0.001) } else - skip "Regression::Multiple::GslEngine not tested (no Gsl)" + skip 'Regression::Multiple::GslEngine not tested (no Gsl)' end end - - - def model_test_matrix(lr,name='undefined') - - stan_coeffs={'a'=>0.151,'b'=>-0.547,'c'=>0.997} - unstan_coeffs={'a'=>0.695, 'b'=>-4.286, 'c'=>0.266} + def model_test_matrix(lr, name = 'undefined') + stan_coeffs = { 'a' => 0.151, 'b' => -0.547, 'c' => 0.997 } + unstan_coeffs = { 'a' => 0.695, 'b' => -4.286, 'c' => 0.266 } unstan_coeffs.each_key{|k| - assert_in_delta(unstan_coeffs[k], lr.coeffs[k],0.001,"b coeffs - #{name}") + assert_in_delta(unstan_coeffs[k], lr.coeffs[k], 0.001, "b coeffs - #{name}") } stan_coeffs.each_key{|k| - assert_in_delta(stan_coeffs[k], lr.standarized_coeffs[k],0.001, "beta coeffs - #{name}") + assert_in_delta(stan_coeffs[k], lr.standarized_coeffs[k], 0.001, "beta coeffs - #{name}") } - assert_in_delta(11.027,lr.constant,0.001) + assert_in_delta(11.027, lr.constant, 0.001) - assert_in_delta(0.955,lr.r,0.001) - assert_in_delta(0.913,lr.r2,0.001) + assert_in_delta(0.955, lr.r, 0.001) + assert_in_delta(0.913, lr.r2, 0.001) - assert_in_delta(20.908, lr.f,0.001) + assert_in_delta(20.908, lr.f, 0.001) assert_in_delta(0.001, lr.probability, 0.001) - assert_in_delta(0.226,lr.tolerance("a"),0.001) - - coeffs_se={"a"=>1.171,"b"=>1.129,"c"=>0.072} - + assert_in_delta(0.226, lr.tolerance('a'), 0.001) + coeffs_se = { 'a' => 1.171, 'b' => 1.129, 'c' => 0.072 } - ccoeffs_se=lr.coeffs_se + ccoeffs_se = lr.coeffs_se coeffs_se.each_key{|k| - assert_in_delta(coeffs_se[k],ccoeffs_se[k],0.001) + assert_in_delta(coeffs_se[k], ccoeffs_se[k], 0.001) } - coeffs_t={"a"=>0.594,"b"=>-3.796,"c"=>3.703} - ccoeffs_t=lr.coeffs_t + coeffs_t = { 'a' => 0.594, 'b' => -3.796, 'c' => 3.703 } + ccoeffs_t = lr.coeffs_t coeffs_t.each_key{|k| - assert_in_delta(coeffs_t[k], ccoeffs_t[k],0.001) + assert_in_delta(coeffs_t[k], ccoeffs_t[k], 0.001) } - assert_in_delta(639.6,lr.sst,0.001) - assert_in_delta(583.76,lr.ssr,0.001) - assert_in_delta(55.840,lr.sse,0.001) - assert(lr.summary.size>0, "#{name} without summary") + assert_in_delta(639.6, lr.sst, 0.001) + assert_in_delta(583.76, lr.ssr, 0.001) + assert_in_delta(55.840, lr.sse, 0.001) + assert(lr.summary.size > 0, "#{name} without summary") end - def model_test(lr,name='undefined') - model_test_matrix(lr,name) - assert_in_delta(4.559, lr.constant_se,0.001) - assert_in_delta(2.419, lr.constant_t,0.001) - assert_in_delta(1.785,lr.process([1,3,11]),0.001) + def model_test(lr, name = 'undefined') + model_test_matrix(lr, name) + assert_in_delta(4.559, lr.constant_se, 0.001) + assert_in_delta(2.419, lr.constant_t, 0.001) + + assert_in_delta(1.785, lr.process([1, 3, 11]), 0.001) end + def test_regression_matrix - @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:scale) - @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:scale) - @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:scale) - @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:scale) - ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset - cor=Statsample::Bivariate.correlation_matrix(ds) - - lr=Statsample::Regression::Multiple::MatrixEngine.new(cor,'y', :y_mean=>@y.mean, :x_mean=>{'a'=>ds['a'].mean, 'b'=>ds['b'].mean, 'c'=>ds['c'].mean}, :cases=>@a.size, :y_sd=>@y.sd , :x_sd=>{'a' => @a.sd, 'b' => @b.sd, 'c' => @c.sd}) + @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:scale) + @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:scale) + @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:scale) + @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:scale) + ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset + cor = Statsample::Bivariate.correlation_matrix(ds) + + lr = Statsample::Regression::Multiple::MatrixEngine.new(cor, 'y', y_mean: @y.mean, x_mean: { 'a' => ds['a'].mean, 'b' => ds['b'].mean, 'c' => ds['c'].mean }, cases: @a.size, y_sd: @y.sd, x_sd: { 'a' => @a.sd, 'b' => @b.sd, 'c' => @c.sd }) assert_nil(lr.constant_se) assert_nil(lr.constant_t) - model_test_matrix(lr, "correlation matrix") + model_test_matrix(lr, 'correlation matrix') - covariance=Statsample::Bivariate.covariance_matrix(ds) - lr=Statsample::Regression::Multiple::MatrixEngine.new(covariance,'y', :y_mean=>@y.mean, :x_mean=>{'a'=>ds['a'].mean, 'b'=>ds['b'].mean, 'c'=>ds['c'].mean}, :cases=>@a.size) - assert(lr.summary.size>0) + covariance = Statsample::Bivariate.covariance_matrix(ds) + lr = Statsample::Regression::Multiple::MatrixEngine.new(covariance, 'y', y_mean: @y.mean, x_mean: { 'a' => ds['a'].mean, 'b' => ds['b'].mean, 'c' => ds['c'].mean }, cases: @a.size) + assert(lr.summary.size > 0) - model_test(lr , "covariance matrix") + model_test(lr, 'covariance matrix') end + def test_regression_rubyengine - @a=[nil,1,3,2,4,3,5,4,6,5,7].to_vector(:scale) - @b=[nil,3,3,4,4,5,5,6,6,4,4].to_vector(:scale) - @c=[nil,11,22,30,40,50,65,78,79,99,100].to_vector(:scale) - @y=[nil,3,4,5,6,7,8,9,10,20,30].to_vector(:scale) - ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset - lr=Statsample::Regression::Multiple::RubyEngine.new(ds,'y') + @a = [nil, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:scale) + @b = [nil, 3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:scale) + @c = [nil, 11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:scale) + @y = [nil, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:scale) + ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset + lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') assert_equal(11, lr.total_cases) assert_equal(10, lr.valid_cases) model_test(lr, 'rubyengine with missing data') - predicted=[nil,1.7857, 6.0989, 3.2433, 7.2908, 4.9667, 10.3428, 8.8158, 10.4717, 23.6639, 25.3198] + predicted = [nil, 1.7857, 6.0989, 3.2433, 7.2908, 4.9667, 10.3428, 8.8158, 10.4717, 23.6639, 25.3198] c_predicted = lr.predicted predicted.each_index do |i| if c_predicted[i].nil? @@ -218,15 +213,14 @@ def test_regression_rubyengine assert_in_delta(predicted[i], c_predicted[i], 0.001) end end - residuals=[nil,1.2142, -2.0989, 1.7566, -1.29085, 2.033, -2.3428, 0.18414, -0.47177, -3.66395, 4.6801] - c_residuals=lr.residuals + residuals = [nil, 1.2142, -2.0989, 1.7566, -1.29085, 2.033, -2.3428, 0.18414, -0.47177, -3.66395, 4.6801] + c_residuals = lr.residuals residuals.each_index do |i| if c_residuals[i].nil? assert(residuals[i].nil?) else - assert_in_delta(residuals[i],c_residuals[i],0.001) + assert_in_delta(residuals[i], c_residuals[i], 0.001) end end - end end diff --git a/test/test_reliability.rb b/test/test_reliability.rb index be7c750..b3b3bdb 100644 --- a/test/test_reliability.rb +++ b/test/test_reliability.rb @@ -1,229 +1,223 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleReliabilityTestCase < Minitest::Test context Statsample::Reliability do - should "return correct r according to Spearman-Brown prophecy" do - r=0.6849 - n=62.quo(15) - assert_in_delta(0.9, Statsample::Reliability.sbp(r,n), 0.001) + should 'return correct r according to Spearman-Brown prophecy' do + r = 0.6849 + n = 62.quo(15) + assert_in_delta(0.9, Statsample::Reliability.sbp(r, n), 0.001) + end + should 'return correct n for desired realiability' do + r = 0.6849 + r_d = 0.9 + assert_in_delta(62, Statsample::Reliability.n_for_desired_reliability(r, r_d, 15), 0.5) end - should "return correct n for desired realiability" do - r=0.6849 - r_d=0.9 - assert_in_delta(62, Statsample::Reliability.n_for_desired_reliability(r, r_d, 15),0.5) - end context "Cronbach's alpha" do setup do - @samples=40 - @n_variables=rand(10)+2 - @ds=Statsample::Dataset.new() - base=@samples.times.collect {|a| rand()}.to_scale + @samples = 40 + @n_variables = rand(10) + 2 + @ds = Statsample::Dataset.new + base = @samples.times.collect { |_a| rand }.to_scale @n_variables.times do |i| - @ds[i]=base.collect {|v| v+rand()}.to_scale + @ds[i] = base.collect { |v| v + rand }.to_scale end @ds.update_valid_data - @k=@ds.fields.size - @cm=Statsample::Bivariate.covariance_matrix(@ds) - @dse=@ds.dup + @k = @ds.fields.size + @cm = Statsample::Bivariate.covariance_matrix(@ds) + @dse = @ds.dup @dse.fields.each do |f| - @dse[f]=@dse[f].standarized + @dse[f] = @dse[f].standarized end - @cme=Statsample::Bivariate.covariance_matrix(@dse) - @a=Statsample::Reliability.cronbach_alpha(@ds) - @as=Statsample::Reliability.cronbach_alpha_standarized(@ds) - end - should "alpha will be equal to sum of matrix covariance less the individual variances" do - total_sum=@cm.total_sum - ind_var=@ds.fields.inject(0) {|ac,v| ac+@ds[v].variance} - expected = @k.quo(@k-1) * (1-(ind_var.quo(total_sum))) - assert_in_delta(expected, @a,1e-10) - end - should "method cronbach_alpha_from_n_s2_cov return correct values" do - sa=Statsample::Reliability::ScaleAnalysis.new(@ds) + @cme = Statsample::Bivariate.covariance_matrix(@dse) + @a = Statsample::Reliability.cronbach_alpha(@ds) + @as = Statsample::Reliability.cronbach_alpha_standarized(@ds) + end + should 'alpha will be equal to sum of matrix covariance less the individual variances' do + total_sum = @cm.total_sum + ind_var = @ds.fields.inject(0) { |ac, v| ac + @ds[v].variance } + expected = @k.quo(@k - 1) * (1 - (ind_var.quo(total_sum))) + assert_in_delta(expected, @a, 1e-10) + end + should 'method cronbach_alpha_from_n_s2_cov return correct values' do + sa = Statsample::Reliability::ScaleAnalysis.new(@ds) vm, cm = sa.variances_mean, sa.covariances_mean - assert_in_delta(sa.alpha, Statsample::Reliability.cronbach_alpha_from_n_s2_cov(@n_variables, vm,cm), 1e-10) + assert_in_delta(sa.alpha, Statsample::Reliability.cronbach_alpha_from_n_s2_cov(@n_variables, vm, cm), 1e-10) end - should "method cronbach_alpha_from_covariance_matrix returns correct value" do - cov=Statsample::Bivariate.covariance_matrix(@ds) - assert_in_delta(@a, Statsample::Reliability.cronbach_alpha_from_covariance_matrix(cov),0.0000001) + should 'method cronbach_alpha_from_covariance_matrix returns correct value' do + cov = Statsample::Bivariate.covariance_matrix(@ds) + assert_in_delta(@a, Statsample::Reliability.cronbach_alpha_from_covariance_matrix(cov), 0.0000001) end - should "return correct n for desired alpha, covariance and variance" do - sa=Statsample::Reliability::ScaleAnalysis.new(@ds) + should 'return correct n for desired alpha, covariance and variance' do + sa = Statsample::Reliability::ScaleAnalysis.new(@ds) vm, cm = sa.variances_mean, sa.covariances_mean - n_obtained=Statsample::Reliability.n_for_desired_alpha(@a, vm,cm) - #p n_obtained - assert_in_delta(Statsample::Reliability.cronbach_alpha_from_n_s2_cov(n_obtained, vm,cm) ,@a,0.001) + n_obtained = Statsample::Reliability.n_for_desired_alpha(@a, vm, cm) + # p n_obtained + assert_in_delta(Statsample::Reliability.cronbach_alpha_from_n_s2_cov(n_obtained, vm, cm), @a, 0.001) end - should "standarized alpha will be equal to sum of matrix covariance less the individual variances on standarized values" do - total_sum=@cme.total_sum - ind_var=@dse.fields.inject(0) {|ac,v| ac+@dse[v].variance} - expected = @k.quo(@k-1) * (1-(ind_var.quo(total_sum))) + should 'standarized alpha will be equal to sum of matrix covariance less the individual variances on standarized values' do + total_sum = @cme.total_sum + ind_var = @dse.fields.inject(0) { |ac, v| ac + @dse[v].variance } + expected = @k.quo(@k - 1) * (1 - (ind_var.quo(total_sum))) assert_in_delta(expected, @as, 1e-10) end end context Statsample::Reliability::ItemCharacteristicCurve do setup do - @samples=100 - @points=rand(10)+3 - @max_point=(@points-1)*3 - @x1=@samples.times.map{rand(@points)}.to_scale - @x2=@samples.times.map{rand(@points)}.to_scale - @x3=@samples.times.map{rand(@points)}.to_scale - @ds={'a'=>@x1,'b'=>@x2,'c'=>@x3}.to_dataset - @icc=Statsample::Reliability::ItemCharacteristicCurve.new(@ds) - end - should "have a correct automatic vector_total" do + @samples = 100 + @points = rand(10) + 3 + @max_point = (@points - 1) * 3 + @x1 = @samples.times.map { rand(@points) }.to_scale + @x2 = @samples.times.map { rand(@points) }.to_scale + @x3 = @samples.times.map { rand(@points) }.to_scale + @ds = { 'a' => @x1, 'b' => @x2, 'c' => @x3 }.to_dataset + @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds) + end + should 'have a correct automatic vector_total' do assert_equal(@ds.vector_sum, @icc.vector_total) end - should "have a correct different vector_total" do - x2=@samples.times.map{rand(10)}.to_scale - @icc=Statsample::Reliability::ItemCharacteristicCurve.new(@ds,x2) + should 'have a correct different vector_total' do + x2 = @samples.times.map { rand(10) }.to_scale + @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds, x2) assert_equal(x2, @icc.vector_total) assert_raises(ArgumentError) do - inc=(@samples+10).times.map{rand(10)}.to_scale - @icc=Statsample::Reliability::ItemCharacteristicCurve.new(@ds,inc) + inc = (@samples + 10).times.map { rand(10) }.to_scale + @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds, inc) end end - should "have 0% for 0 points on maximum value values" do - max=@icc.curve_field('a',0)[@max_point.to_f] - max||=0 + should 'have 0% for 0 points on maximum value values' do + max = @icc.curve_field('a', 0)[@max_point.to_f] + max ||= 0 assert_in_delta(0, max) end - should "have 0 for max value on minimum value" do - max=@icc.curve_field('a',@max_point)[0.0] - max||=0 + should 'have 0 for max value on minimum value' do + max = @icc.curve_field('a', @max_point)[0.0] + max ||= 0 assert_in_delta(0, max) end - should "have correct values of % for any value" do - sum=@icc.vector_total - total={} - total_g=sum.frequencies - index=rand(@points) - @x1.each_with_index do |v,i| - total[sum[i]]||=0 - total[sum[i]]+=1 if v==index + should 'have correct values of % for any value' do + sum = @icc.vector_total + total = {} + total_g = sum.frequencies + index = rand(@points) + @x1.each_with_index do |v, i| + total[sum[i]] ||= 0 + total[sum[i]] += 1 if v == index end - expected=total.each {|k,v| - total[k]=v.quo(total_g[k]) + expected = total.each {|k, v| + total[k] = v.quo(total_g[k]) } - assert_equal(expected, @icc.curve_field('a',index)) - + assert_equal(expected, @icc.curve_field('a', index)) end - end context Statsample::Reliability::MultiScaleAnalysis do - setup do - size=100 - @scales=3 - @items_per_scale=10 - h={} + size = 100 + @scales = 3 + @items_per_scale = 10 + h = {} @scales.times {|s| @items_per_scale.times {|i| - h["#{s}_#{i}"] = (size.times.map {(s*2)+rand}).to_scale + h["#{s}_#{i}"] = (size.times.map { (s * 2) + rand }).to_scale } } - @ds=h.to_dataset - @msa=Statsample::Reliability::MultiScaleAnalysis.new(:name=>'Multiple Analysis') do |m| - m.scale "complete", @ds + @ds = h.to_dataset + @msa = Statsample::Reliability::MultiScaleAnalysis.new(name: 'Multiple Analysis') do |m| + m.scale 'complete', @ds @scales.times {|s| - m.scale "scale_#{s}", @ds.clone(@items_per_scale.times.map {|i| "#{s}_#{i}"}), {:name=>"Scale #{s}"} + m.scale "scale_#{s}", @ds.clone(@items_per_scale.times.map { |i| "#{s}_#{i}" }), name: "Scale #{s}" } end end - should "Retrieve correct ScaleAnalysis for whole scale" do - sa=Statsample::Reliability::ScaleAnalysis.new(@ds, :name=>"Scale complete") - assert_equal(sa.variances_mean, @msa.scale("complete").variances_mean) + should 'Retrieve correct ScaleAnalysis for whole scale' do + sa = Statsample::Reliability::ScaleAnalysis.new(@ds, name: 'Scale complete') + assert_equal(sa.variances_mean, @msa.scale('complete').variances_mean) end - should "Retrieve correct ScaleAnalysis for each scale" do + should 'Retrieve correct ScaleAnalysis for each scale' do @scales.times {|s| - sa=Statsample::Reliability::ScaleAnalysis.new(@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}), :name=>"Scale #{s}") - assert_equal(sa.variances_mean,@msa.scale("scale_#{s}").variances_mean) + sa = Statsample::Reliability::ScaleAnalysis.new(@ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}" }), name: "Scale #{s}") + assert_equal(sa.variances_mean, @msa.scale("scale_#{s}").variances_mean) } end - should "retrieve correct correlation matrix for each scale" do - vectors={'complete' => @ds.vector_sum} + should 'retrieve correct correlation matrix for each scale' do + vectors = { 'complete' => @ds.vector_sum } @scales.times {|s| - vectors["scale_#{s}"]=@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}).vector_sum + vectors["scale_#{s}"] = @ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}" }).vector_sum } - ds2=vectors.to_dataset + ds2 = vectors.to_dataset assert_equal(Statsample::Bivariate.correlation_matrix(ds2), @msa.correlation_matrix) end - should "delete scale using delete_scale" do - @msa.delete_scale("complete") - assert_equal(@msa.scales.keys.sort, @scales.times.map {|s| "scale_#{s}"}) + should 'delete scale using delete_scale' do + @msa.delete_scale('complete') + assert_equal(@msa.scales.keys.sort, @scales.times.map { |s| "scale_#{s}" }) end - should "retrieve pca for scales" do - @msa.delete_scale("complete") - vectors=Hash.new + should 'retrieve pca for scales' do + @msa.delete_scale('complete') + vectors = {} @scales.times {|s| - vectors["scale_#{s}"]=@ds.dup(@items_per_scale.times.map {|i| "#{s}_#{i}"}).vector_sum + vectors["scale_#{s}"] = @ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}" }).vector_sum } - ds2=vectors.to_dataset - cor_matrix=Statsample::Bivariate.correlation_matrix(ds2) - m=3 - pca=Statsample::Factor::PCA.new(cor_matrix, :m=>m) - assert_equal(pca.component_matrix, @msa.pca(:m=>m).component_matrix) - end - should "retrieve acceptable summary" do - @msa.delete_scale("scale_0") - @msa.delete_scale("scale_1") - @msa.delete_scale("scale_2") - + ds2 = vectors.to_dataset + cor_matrix = Statsample::Bivariate.correlation_matrix(ds2) + m = 3 + pca = Statsample::Factor::PCA.new(cor_matrix, m: m) + assert_equal(pca.component_matrix, @msa.pca(m: m).component_matrix) + end + should 'retrieve acceptable summary' do + @msa.delete_scale('scale_0') + @msa.delete_scale('scale_1') + @msa.delete_scale('scale_2') - #@msa.summary_correlation_matrix=true - #@msa.summary_pca=true + # @msa.summary_correlation_matrix=true + # @msa.summary_pca=true - - assert(@msa.summary.size>0) + assert(@msa.summary.size > 0) end end context Statsample::Reliability::ScaleAnalysis do setup do - @x1=[1,1,1,1,2,2,2,2,3,3,3,30].to_scale - @x2=[1,1,1,2,2,3,3,3,3,4,4,50].to_scale - @x3=[2,2,1,1,1,2,2,2,3,4,5,40].to_scale - @x4=[1,2,3,4,4,4,4,3,4,4,5,30].to_scale - @ds={'x1'=>@x1,'x2'=>@x2,'x3'=>@x3,'x4'=>@x4}.to_dataset - @ia=Statsample::Reliability::ScaleAnalysis.new(@ds) - @cov_matrix=@ia.cov_m - end - should "return correct values for item analysis" do - assert_in_delta(0.980,@ia.alpha,0.001) - assert_in_delta(0.999,@ia.alpha_standarized,0.001) - var_mean=4.times.map{|m| @cov_matrix[m,m]}.to_scale.mean + @x1 = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 30].to_scale + @x2 = [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 50].to_scale + @x3 = [2, 2, 1, 1, 1, 2, 2, 2, 3, 4, 5, 40].to_scale + @x4 = [1, 2, 3, 4, 4, 4, 4, 3, 4, 4, 5, 30].to_scale + @ds = { 'x1' => @x1, 'x2' => @x2, 'x3' => @x3, 'x4' => @x4 }.to_dataset + @ia = Statsample::Reliability::ScaleAnalysis.new(@ds) + @cov_matrix = @ia.cov_m + end + should 'return correct values for item analysis' do + assert_in_delta(0.980, @ia.alpha, 0.001) + assert_in_delta(0.999, @ia.alpha_standarized, 0.001) + var_mean = 4.times.map { |m| @cov_matrix[m, m] }.to_scale.mean assert_in_delta(var_mean, @ia.variances_mean) assert_equal(@x1.mean, @ia.item_statistics['x1'][:mean]) assert_equal(@x4.mean, @ia.item_statistics['x4'][:mean]) - assert_in_delta(@x1.sds, @ia.item_statistics['x1'][:sds],1e-14) - assert_in_delta(@x4.sds, @ia.item_statistics['x4'][:sds],1e-14) - ds2=@ds.clone + assert_in_delta(@x1.sds, @ia.item_statistics['x1'][:sds], 1e-14) + assert_in_delta(@x4.sds, @ia.item_statistics['x4'][:sds], 1e-14) + ds2 = @ds.clone ds2.delete_vector('x1') - vector_sum=ds2.vector_sum + vector_sum = ds2.vector_sum assert_equal(vector_sum.mean, @ia.stats_if_deleted['x1'][:mean]) assert_equal(vector_sum.sds, @ia.stats_if_deleted['x1'][:sds]) - assert_in_delta(vector_sum.variance, @ia.stats_if_deleted['x1'][:variance_sample],1e-10) + assert_in_delta(vector_sum.variance, @ia.stats_if_deleted['x1'][:variance_sample], 1e-10) assert_equal(Statsample::Reliability.cronbach_alpha(ds2), @ia.stats_if_deleted['x1'][:alpha]) - covariances=[] + covariances = [] 4.times.each {|i| 4.times.each {|j| - if i!=j - covariances.push(@cov_matrix[i,j]) + if i != j + covariances.push(@cov_matrix[i, j]) end } } assert_in_delta(covariances.to_scale.mean, @ia.covariances_mean) - assert_in_delta(0.999,@ia.item_total_correlation()['x1'],0.001) - assert_in_delta(1050.455,@ia.stats_if_deleted()['x1'][:variance_sample],0.001) + assert_in_delta(0.999, @ia.item_total_correlation['x1'], 0.001) + assert_in_delta(1050.455, @ia.stats_if_deleted['x1'][:variance_sample], 0.001) end - should "return a summary" do - assert(@ia.summary.size>0) + should 'return a summary' do + assert(@ia.summary.size > 0) end - end end end diff --git a/test/test_reliability_icc.rb b/test/test_reliability_icc.rb index bfdf9bd..7148534 100644 --- a/test/test_reliability_icc.rb +++ b/test/test_reliability_icc.rb @@ -1,115 +1,113 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) -$reliability_icc=nil +$reliability_icc = nil class StatsampleReliabilityIccTestCase < Minitest::Test context Statsample::Reliability::ICC do setup do - a=[9,6,8,7,10,6].to_scale - b=[2,1,4,1,5,2].to_scale - c=[5,3,6,2,6,4].to_scale - d=[8,2,8,6,9,7].to_scale - @ds={'a'=>a,'b'=>b,'c'=>c,'d'=>d}.to_dataset - @icc=Statsample::Reliability::ICC.new(@ds) + a = [9, 6, 8, 7, 10, 6].to_scale + b = [2, 1, 4, 1, 5, 2].to_scale + c = [5, 3, 6, 2, 6, 4].to_scale + d = [8, 2, 8, 6, 9, 7].to_scale + @ds = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset + @icc = Statsample::Reliability::ICC.new(@ds) end - should "basic method be correct" do - assert_equal(6,@icc.n) - assert_equal(4,@icc.k) + should 'basic method be correct' do + assert_equal(6, @icc.n) + assert_equal(4, @icc.k) end - should "total mean be correct" do + should 'total mean be correct' do assert_in_delta(5.291, @icc.total_mean, 0.001) end - should "df methods be correct" do + should 'df methods be correct' do assert_equal(5, @icc.df_bt) assert_equal(18, @icc.df_wt) assert_equal(3, @icc.df_bj) assert_equal(15, @icc.df_residual) end - should "ms between targets be correct" do + should 'ms between targets be correct' do assert_in_delta(11.24, @icc.ms_bt, 0.01) end - should "ms within targets be correct" do + should 'ms within targets be correct' do assert_in_delta(6.26, @icc.ms_wt, 0.01) end - should "ms between judges be correct" do + should 'ms between judges be correct' do assert_in_delta(32.49, @icc.ms_bj, 0.01) end - should "ms residual be correct" do + should 'ms residual be correct' do assert_in_delta(1.02, @icc.ms_residual, 0.01) end - context "with McGraw and Wong denominations," do - + context 'with McGraw and Wong denominations,' do end - context "with Shrout & Fleiss denominations, " do - should "icc(1,1) method be correct" do + context 'with Shrout & Fleiss denominations, ' do + should 'icc(1,1) method be correct' do assert_in_delta(0.17, @icc.icc_1_1, 0.01) end # Verified on SPSS and R - should "icc(2,1) method be correct" do + should 'icc(2,1) method be correct' do assert_in_delta(0.29, @icc.icc_2_1, 0.01) end - should "icc(3,1) method be correct" do + should 'icc(3,1) method be correct' do assert_in_delta(0.71, @icc.icc_3_1, 0.01) end - should "icc(1,k) method be correct" do + should 'icc(1,k) method be correct' do assert_in_delta(0.44, @icc.icc_1_k, 0.01) end # Verified on SPSS and R - should "icc(2,k) method be correct" do + should 'icc(2,k) method be correct' do assert_in_delta(0.62, @icc.icc_2_k, 0.01) end - should "icc(3,k) method be correct" do + should 'icc(3,k) method be correct' do assert_in_delta(0.91, @icc.icc_3_k, 0.01) end - should "icc(1,1) F be correct" do + should 'icc(1,1) F be correct' do assert_in_delta(1.795, @icc.icc_1_f.f) end - should "icc(1,1) confidence interval should be correct" do + should 'icc(1,1) confidence interval should be correct' do assert_in_delta(-0.133, @icc.icc_1_1_ci[0], 0.001) assert_in_delta(0.723, @icc.icc_1_1_ci[1], 0.001) end - should "icc(1,k) confidence interval should be correct" do + should 'icc(1,k) confidence interval should be correct' do assert_in_delta(-0.884, @icc.icc_1_k_ci[0], 0.001) assert_in_delta(0.912, @icc.icc_1_k_ci[1], 0.001) end - should "icc(2,1) F be correct" do + should 'icc(2,1) F be correct' do assert_in_delta(11.027, @icc.icc_2_f.f) end - should "icc(2,1) confidence interval should be correct" do - #skip("Not yet operational") + should 'icc(2,1) confidence interval should be correct' do + # skip("Not yet operational") assert_in_delta(0.019, @icc.icc_2_1_ci[0], 0.001) assert_in_delta(0.761, @icc.icc_2_1_ci[1], 0.001) end # Verified on SPSS and R - should "icc(2,k) confidence interval should be correct" do - #skip("Not yet operational") - #p @icc.icc_2_k_ci + should 'icc(2,k) confidence interval should be correct' do + # skip("Not yet operational") + # p @icc.icc_2_k_ci assert_in_delta(0.039, @icc.icc_2_k_ci[0], 0.001) assert_in_delta(0.929, @icc.icc_2_k_ci[1], 0.001) - end - #should "Shrout icc(2,k) and McGraw icc(a,k) ci be equal" do + # should "Shrout icc(2,k) and McGraw icc(a,k) ci be equal" do # assert_in_delta(@icc.icc_2_k_ci_shrout[0], @icc.icc_2_k_ci_mcgraw[0], 10e-5) - #end + # end - should "icc(3,1) F be correct" do + should 'icc(3,1) F be correct' do assert_in_delta(11.027, @icc.icc_3_f.f) end - should "icc(3,1) confidence interval should be correct" do + should 'icc(3,1) confidence interval should be correct' do assert_in_delta(0.342, @icc.icc_3_1_ci[0], 0.001) assert_in_delta(0.946, @icc.icc_3_1_ci[1], 0.001) end - should "icc(3,k) confidence interval should be correct" do + should 'icc(3,k) confidence interval should be correct' do assert_in_delta(0.676, @icc.icc_3_k_ci[0], 0.001) assert_in_delta(0.986, @icc.icc_3_k_ci[1], 0.001) end - should "incorrect type raises an error" do + should 'incorrect type raises an error' do assert_raise(::RuntimeError) do - @icc.type=:nonexistant_type + @icc.type = :nonexistant_type end end end @@ -117,23 +115,23 @@ class StatsampleReliabilityIccTestCase < Minitest::Test begin require 'rserve' require 'statsample/rserve_extension' - context "McGraw and Wong" do + context 'McGraw and Wong' do teardown do - @r=$reliability_icc[:r].close unless $reliability_icc[:r].nil? + @r = $reliability_icc[:r].close unless $reliability_icc[:r].nil? end setup do - if($reliability_icc.nil?) - size=100 - a=size.times.map {rand(10)}.to_scale - b=a.recode{|i|i+rand(4)-2} - c=a.recode{|i|i+rand(4)-2} - d=a.recode{|i|i+rand(4)-2} - @ds={'a'=>a,'b'=>b,'c'=>c,'d'=>d}.to_dataset + if $reliability_icc.nil? + size = 100 + a = size.times.map { rand(10) }.to_scale + b = a.recode { |i| i + rand(4) - 2 } + c = a.recode { |i| i + rand(4) - 2 } + d = a.recode { |i| i + rand(4) - 2 } + @ds = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset - @icc=Statsample::Reliability::ICC.new(@ds) - @r=Rserve::Connection.new + @icc = Statsample::Reliability::ICC.new(@ds) + @r = Rserve::Connection.new - @r.assign('ds',@ds) + @r.assign('ds', @ds) @r.void_eval("library(irr); iccs=list( @@ -144,59 +142,57 @@ class StatsampleReliabilityIccTestCase < Minitest::Test icc_a_1=icc(ds,'t','a','s'), icc_a_k=icc(ds,'t','a','a')) ") - @iccs=@r.eval('iccs').to_ruby - $reliability_icc={ :icc=>@icc, :iccs=>@iccs, :r=>@r + @iccs = @r.eval('iccs').to_ruby + $reliability_icc = { icc: @icc, iccs: @iccs, r: @r } end - @icc=$reliability_icc[:icc] - @iccs=$reliability_icc[:iccs] - @r=$reliability_icc[:r] - + @icc = $reliability_icc[:icc] + @iccs = $reliability_icc[:iccs] + @r = $reliability_icc[:r] end [:icc_1, :icc_k, :icc_c_1, :icc_c_k, :icc_a_1, :icc_a_k].each do |t| context "ICC Type #{t} " do - should "value be correct" do - @icc.type=t - @r_icc=@iccs[t.to_s] - assert_in_delta(@r_icc['value'],@icc.r) + should 'value be correct' do + @icc.type = t + @r_icc = @iccs[t.to_s] + assert_in_delta(@r_icc['value'], @icc.r) end - should "fvalue be correct" do - @icc.type=t - @r_icc=@iccs[t.to_s] - assert_in_delta(@r_icc['Fvalue'],@icc.f.f) + should 'fvalue be correct' do + @icc.type = t + @r_icc = @iccs[t.to_s] + assert_in_delta(@r_icc['Fvalue'], @icc.f.f) end - should "num df be correct" do - @icc.type=t - @r_icc=@iccs[t.to_s] - assert_in_delta(@r_icc['df1'],@icc.f.df_num) + should 'num df be correct' do + @icc.type = t + @r_icc = @iccs[t.to_s] + assert_in_delta(@r_icc['df1'], @icc.f.df_num) end - should "den df be correct" do - @icc.type=t - @r_icc=@iccs[t.to_s] - assert_in_delta(@r_icc['df2'],@icc.f.df_den) + should 'den df be correct' do + @icc.type = t + @r_icc = @iccs[t.to_s] + assert_in_delta(@r_icc['df2'], @icc.f.df_den) end - should "f probability be correct" do - @icc.type=t - @r_icc=@iccs[t.to_s] - assert_in_delta(@r_icc['p.value'],@icc.f.probability) + should 'f probability be correct' do + @icc.type = t + @r_icc = @iccs[t.to_s] + assert_in_delta(@r_icc['p.value'], @icc.f.probability) end - should "bounds be equal" do - @icc.type=t - @r_icc=@iccs[t.to_s] - assert_in_delta(@r_icc['lbound'],@icc.lbound) - assert_in_delta(@r_icc['ubound'],@icc.ubound) + should 'bounds be equal' do + @icc.type = t + @r_icc = @iccs[t.to_s] + assert_in_delta(@r_icc['lbound'], @icc.lbound) + assert_in_delta(@r_icc['ubound'], @icc.ubound) end - should "summary generated" do - assert(@icc.summary.size>0) + should 'summary generated' do + assert(@icc.summary.size > 0) end end end end rescue - puts "requires rserve" + puts 'requires rserve' end - end end diff --git a/test/test_reliability_skillscale.rb b/test/test_reliability_skillscale.rb index 9e78b2c..ea7f15c 100644 --- a/test/test_reliability_skillscale.rb +++ b/test/test_reliability_skillscale.rb @@ -1,57 +1,55 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) - +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleReliabilitySkillScaleTestCase < Minitest::Test context Statsample::Reliability::SkillScaleAnalysis do setup do - options=%w{a b c d e} - cases=20 - @id=cases.times.map {|v| v}.to_scale - @a=cases.times.map {options[rand(5)]}.to_vector - @b=cases.times.map {options[rand(5)]}.to_vector - @c=cases.times.map {options[rand(5)]}.to_vector - @d=cases.times.map {options[rand(5)]}.to_vector - @e=cases.times.map {|i| - i==0 ? options[rand(0)] : - rand()>0.8 ? nil : options[rand(5)] + options = %w(a b c d e) + cases = 20 + @id = cases.times.map { |v| v }.to_scale + @a = cases.times.map { options[rand(5)] }.to_vector + @b = cases.times.map { options[rand(5)] }.to_vector + @c = cases.times.map { options[rand(5)] }.to_vector + @d = cases.times.map { options[rand(5)] }.to_vector + @e = cases.times.map {|i| + i == 0 ? options[rand(0)] : + rand > 0.8 ? nil : options[rand(5)] }.to_vector - @ds={'id'=>@id,'a'=>@a,'b'=>@b,'c'=>@c,'d'=>@d,'e'=>@e}.to_dataset - @key={'a'=>"a", 'b'=>options[rand(5)], 'c'=>options[rand(5)], 'd'=>options[rand(5)],'e'=>options[rand(5)]} - @ssa=Statsample::Reliability::SkillScaleAnalysis.new(@ds, @key) - @ac=@a.map {|v| v==@key['a'] ? 1 : 0}.to_scale - @bc=@b.map {|v| v==@key['b'] ? 1 : 0}.to_scale - @cc=@c.map {|v| v==@key['c'] ? 1 : 0}.to_scale - @dc=@d.map {|v| v==@key['d'] ? 1 : 0}.to_scale - @ec=@e.map {|v| v.nil? ? nil : (v==@key['e'] ? 1 : 0)}.to_scale - + @ds = { 'id' => @id, 'a' => @a, 'b' => @b, 'c' => @c, 'd' => @d, 'e' => @e }.to_dataset + @key = { 'a' => 'a', 'b' => options[rand(5)], 'c' => options[rand(5)], 'd' => options[rand(5)], 'e' => options[rand(5)] } + @ssa = Statsample::Reliability::SkillScaleAnalysis.new(@ds, @key) + @ac = @a.map { |v| v == @key['a'] ? 1 : 0 }.to_scale + @bc = @b.map { |v| v == @key['b'] ? 1 : 0 }.to_scale + @cc = @c.map { |v| v == @key['c'] ? 1 : 0 }.to_scale + @dc = @d.map { |v| v == @key['d'] ? 1 : 0 }.to_scale + @ec = @e.map { |v| v.nil? ? nil : (v == @key['e'] ? 1 : 0) }.to_scale end - should "return proper corrected dataset" do - cds={'id'=>@id, 'a'=>@ac,'b'=>@bc,'c'=>@cc,'d'=>@dc, 'e'=>@ec}.to_dataset + should 'return proper corrected dataset' do + cds = { 'id' => @id, 'a' => @ac, 'b' => @bc, 'c' => @cc, 'd' => @dc, 'e' => @ec }.to_dataset assert_equal(cds, @ssa.corrected_dataset) end - should "return proper corrected minimal dataset" do - cdsm={'a'=>@ac,'b'=>@bc,'c'=>@cc,'d'=>@dc, 'e'=>@ec}.to_dataset + should 'return proper corrected minimal dataset' do + cdsm = { 'a' => @ac, 'b' => @bc, 'c' => @cc, 'd' => @dc, 'e' => @ec }.to_dataset assert_equal(cdsm, @ssa.corrected_dataset_minimal) end - should "return correct vector_sum and vector_sum" do - cdsm=@ssa.corrected_dataset_minimal + should 'return correct vector_sum and vector_sum' do + cdsm = @ssa.corrected_dataset_minimal assert_equal(cdsm.vector_sum, @ssa.vector_sum) assert_equal(cdsm.vector_mean, @ssa.vector_mean) end - should "not crash on rare case" do - a=Statsample::Vector["c","c","a","a","c","a","b","c","c","b","a","d","a","d","a","a","d","e","c","d"] - b=Statsample::Vector["e","b","e","b","c","d","a","e","e","c","b","e","e","b","d","c","e","b","b","d"] - c=Statsample::Vector["e","b","e","c","e","c","b","d","e","c","a","a","b","d","e","c","b","a","a","e"] - d=Statsample::Vector["a","b","d","d","e","b","e","b","d","c","e","a","c","d","c","c","e","d","d","b"] - e=Statsample::Vector["a","b",nil,"d","c","c","d",nil,"d","d","e","e",nil,nil,nil,"d","c",nil,"e","d"] - key={"a"=>"a", "b"=>"e", "c"=>"d", "d"=>"c", "e"=>"d"} - ds=Statsample::Dataset.new("a"=>a,"b"=>b,"c"=>c,"d"=>d,"e"=>e) - ssa=Statsample::Reliability::SkillScaleAnalysis.new(ds, key) + should 'not crash on rare case' do + a = Statsample::Vector['c', 'c', 'a', 'a', 'c', 'a', 'b', 'c', 'c', 'b', 'a', 'd', 'a', 'd', 'a', 'a', 'd', 'e', 'c', 'd'] + b = Statsample::Vector['e', 'b', 'e', 'b', 'c', 'd', 'a', 'e', 'e', 'c', 'b', 'e', 'e', 'b', 'd', 'c', 'e', 'b', 'b', 'd'] + c = Statsample::Vector['e', 'b', 'e', 'c', 'e', 'c', 'b', 'd', 'e', 'c', 'a', 'a', 'b', 'd', 'e', 'c', 'b', 'a', 'a', 'e'] + d = Statsample::Vector['a', 'b', 'd', 'd', 'e', 'b', 'e', 'b', 'd', 'c', 'e', 'a', 'c', 'd', 'c', 'c', 'e', 'd', 'd', 'b'] + e = Statsample::Vector['a', 'b', nil, 'd', 'c', 'c', 'd', nil, 'd', 'd', 'e', 'e', nil, nil, nil, 'd', 'c', nil, 'e', 'd'] + key = { 'a' => 'a', 'b' => 'e', 'c' => 'd', 'd' => 'c', 'e' => 'd' } + ds = Statsample::Dataset.new('a' => a, 'b' => b, 'c' => c, 'd' => d, 'e' => e) + ssa = Statsample::Reliability::SkillScaleAnalysis.new(ds, key) assert(ssa.summary) end - should "return valid summary" do - assert(@ssa.summary.size>0) + should 'return valid summary' do + assert(@ssa.summary.size > 0) end end end diff --git a/test/test_resample.rb b/test/test_resample.rb index 3612bdc..4d353ff 100644 --- a/test/test_resample.rb +++ b/test/test_resample.rb @@ -1,22 +1,24 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleResampleTestCase < Minitest::Test def initialize(*args) super end + def test_basic - r=Statsample::Resample.generate(20,1,10) - assert_equal(20,r.size) - assert(r.min>=1) - assert(r.max<=10) + r = Statsample::Resample.generate(20, 1, 10) + assert_equal(20, r.size) + assert(r.min >= 1) + assert(r.max <= 10) end + def test_repeat_and_save - r=Statsample::Resample.repeat_and_save(400) { - Statsample::Resample.generate(20,1,10).count(1) + r = Statsample::Resample.repeat_and_save(400) { + Statsample::Resample.generate(20, 1, 10).count(1) } - assert_equal(400,r.size) - v=Statsample::Vector.new(r,:scale) - a=v.count {|x| x > 3} - assert(a>=30 && a<=70) + assert_equal(400, r.size) + v = Statsample::Vector.new(r, :scale) + a = v.count { |x| x > 3 } + assert(a >= 30 && a <= 70) end end diff --git a/test/test_rserve_extension.rb b/test/test_rserve_extension.rb index 9466cff..ce7ef4e 100644 --- a/test/test_rserve_extension.rb +++ b/test/test_rserve_extension.rb @@ -1,42 +1,42 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) begin require 'rserve' require 'statsample/rserve_extension' -class StatsampleRserveExtensionTestCase < Minitest::Test - context "Statsample Rserve extensions" do - setup do - @r=Rserve::Connection.new - end - teardown do - @r.close - end - should "return a valid rexp for numeric vector" do - a=100.times.map {|i| rand()>0.9 ? nil : i+rand() }.to_scale - rexp=a.to_REXP - assert(rexp.is_a? Rserve::REXP::Double) - assert_equal(rexp.to_ruby,a.data_with_nils) - @r.assign 'a',rexp - assert_equal(a.data_with_nils, @r.eval('a').to_ruby) - end - should "return a valid rserve dataframe for statsample datasets" do - a=100.times.map {|i| rand()>0.9 ? nil : i+rand() }.to_scale - b=100.times.map {|i| rand()>0.9 ? nil : i+rand() }.to_scale - c=100.times.map {|i| rand()>0.9 ? nil : i+rand() }.to_scale - ds={'a'=>a,'b'=>b,'c'=>c}.to_dataset - rexp=ds.to_REXP - assert(rexp.is_a? Rserve::REXP::GenericVector) - ret=rexp.to_ruby - assert_equal(a.data_with_nils, ret['a']) - @r.assign 'df', rexp - out_df=@r.eval('df').to_ruby - assert_equal('data.frame', out_df.attributes['class']) - assert_equal(['a','b','c'], out_df.attributes['names']) - assert_equal(a.data_with_nils, out_df['a']) + class StatsampleRserveExtensionTestCase < Minitest::Test + context 'Statsample Rserve extensions' do + setup do + @r = Rserve::Connection.new + end + teardown do + @r.close + end + should 'return a valid rexp for numeric vector' do + a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale + rexp = a.to_REXP + assert(rexp.is_a? Rserve::REXP::Double) + assert_equal(rexp.to_ruby, a.data_with_nils) + @r.assign 'a', rexp + assert_equal(a.data_with_nils, @r.eval('a').to_ruby) + end + should 'return a valid rserve dataframe for statsample datasets' do + a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale + b = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale + c = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale + ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset + rexp = ds.to_REXP + assert(rexp.is_a? Rserve::REXP::GenericVector) + ret = rexp.to_ruby + assert_equal(a.data_with_nils, ret['a']) + @r.assign 'df', rexp + out_df = @r.eval('df').to_ruby + assert_equal('data.frame', out_df.attributes['class']) + assert_equal(%w(a b c), out_df.attributes['names']) + assert_equal(a.data_with_nils, out_df['a']) + end end end -end rescue LoadError - puts "Require rserve extension" + puts 'Require rserve extension' end diff --git a/test/test_srs.rb b/test/test_srs.rb index 7b70abb..c9d5abd 100644 --- a/test/test_srs.rb +++ b/test/test_srs.rb @@ -1,9 +1,9 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleSrsTestCase < Minitest::Test def test_std_error - assert_equal(384,Statsample::SRS.estimation_n0(0.05,0.5,0.95).to_i) - assert_equal(108,Statsample::SRS.estimation_n(0.05,0.5,150,0.95).to_i) - assert_in_delta(0.0289,Statsample::SRS.proportion_sd_kp_wor(0.5,100,150),0.001) + assert_equal(384, Statsample::SRS.estimation_n0(0.05, 0.5, 0.95).to_i) + assert_equal(108, Statsample::SRS.estimation_n(0.05, 0.5, 150, 0.95).to_i) + assert_in_delta(0.0289, Statsample::SRS.proportion_sd_kp_wor(0.5, 100, 150), 0.001) end end diff --git a/test/test_statistics.rb b/test/test_statistics.rb index 44eaf99..4e17370 100644 --- a/test/test_statistics.rb +++ b/test/test_statistics.rb @@ -1,77 +1,79 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleStatisicsTestCase < Minitest::Test - def initialize(*args) super end - def test_p_using_cdf - assert_equal(0.25, Statsample::Test.p_using_cdf(0.25, tails=:left)) - assert_equal(0.75, Statsample::Test.p_using_cdf(0.25, tails=:right)) - assert_equal(0.50, Statsample::Test.p_using_cdf(0.25, tails=:both)) - assert_equal(1, Statsample::Test.p_using_cdf(0.50, tails=:both)) - assert_equal(0.05, Statsample::Test.p_using_cdf(0.025, tails=:both)) - assert_in_delta(0.05, Statsample::Test.p_using_cdf(0.975, tails=:both),0.0001) + def test_p_using_cdf + assert_equal(0.25, Statsample::Test.p_using_cdf(0.25, tails = :left)) + assert_equal(0.75, Statsample::Test.p_using_cdf(0.25, tails = :right)) + assert_equal(0.50, Statsample::Test.p_using_cdf(0.25, tails = :both)) + assert_equal(1, Statsample::Test.p_using_cdf(0.50, tails = :both)) + assert_equal(0.05, Statsample::Test.p_using_cdf(0.025, tails = :both)) + assert_in_delta(0.05, Statsample::Test.p_using_cdf(0.975, tails = :both), 0.0001) end + def test_recode_repeated - a=%w{a b c c d d d e} - exp=["a","b","c_1","c_2","d_1","d_2","d_3","e"] - assert_equal(exp,a.recode_repeated) + a = %w(a b c c d d d e) + exp = %w(a b c_1 c_2 d_1 d_2 d_3 e) + assert_equal(exp, a.recode_repeated) end - def test_is_number - assert("10".is_number?) - assert("-10".is_number?) - assert("0.1".is_number?) - assert("-0.1".is_number?) - assert("10e3".is_number?) - assert("10e-3".is_number?) - assert(!"1212-1212-1".is_number?) - assert(!"a10".is_number?) - assert(!"".is_number?) + def test_is_number + assert('10'.is_number?) + assert('-10'.is_number?) + assert('0.1'.is_number?) + assert('-0.1'.is_number?) + assert('10e3'.is_number?) + assert('10e-3'.is_number?) + assert(!'1212-1212-1'.is_number?) + assert(!'a10'.is_number?) + assert(!''.is_number?) end + def test_estimation_mean - v=([42]*23+[41]*4+[36]*1+[32]*1+[29]*1+[27]*2+[23]*1+[19]*1+[16]*2+[15]*2+[14,11,10,9,7]+ [6]*3+[5]*2+[4,3]).to_vector(:scale) - assert_equal(50,v.size) - assert_equal(1471,v.sum()) - #limits=Statsample::SRS.mean_confidence_interval_z(v.mean(), v.sds(), v.size,676,0.80) + v = ([42] * 23 + [41] * 4 + [36] * 1 + [32] * 1 + [29] * 1 + [27] * 2 + [23] * 1 + [19] * 1 + [16] * 2 + [15] * 2 + [14, 11, 10, 9, 7] + [6] * 3 + [5] * 2 + [4, 3]).to_vector(:scale) + assert_equal(50, v.size) + assert_equal(1471, v.sum) + # limits=Statsample::SRS.mean_confidence_interval_z(v.mean(), v.sds(), v.size,676,0.80) end + def test_estimation_proportion # total - pop=3042 - sam=200 - prop=0.19 + pop = 3042 + sam = 200 + prop = 0.19 assert_in_delta(81.8, Statsample::SRS.proportion_total_sd_ep_wor(prop, sam, pop), 0.1) # confidence limits - pop=500 - sam=100 - prop=0.37 - a=0.95 - l= Statsample::SRS.proportion_confidence_interval_z(prop, sam, pop, a) - assert_in_delta(0.28,l[0],0.01) - assert_in_delta(0.46,l[1],0.01) + pop = 500 + sam = 100 + prop = 0.37 + a = 0.95 + l = Statsample::SRS.proportion_confidence_interval_z(prop, sam, pop, a) + assert_in_delta(0.28, l[0], 0.01) + assert_in_delta(0.46, l[1], 0.01) end + def test_ml - if(true) - #real=[1,1,1,1].to_vector(:scale) + if true + # real=[1,1,1,1].to_vector(:scale) - #pred=[0.0001,0.0001,0.0001,0.0001].to_vector(:scale) + # pred=[0.0001,0.0001,0.0001,0.0001].to_vector(:scale) # puts Statsample::Bivariate.maximum_likehood_dichotomic(pred,real) end end - def test_simple_linear_regression - a=[1,2,3,4,5,6].to_vector(:scale) - b=[6,2,4,10,12,8].to_vector(:scale) - reg = Statsample::Regression::Simple.new_from_vectors(a,b) - assert_in_delta((reg.ssr+reg.sse).to_f,reg.sst,0.001) - assert_in_delta(Statsample::Bivariate.pearson(a,b),reg.r,0.001) - assert_in_delta(2.4,reg.a,0.01) - assert_in_delta(1.314,reg.b,0.001) - assert_in_delta(0.657,reg.r,0.001) - assert_in_delta(0.432,reg.r2,0.001) + a = [1, 2, 3, 4, 5, 6].to_vector(:scale) + b = [6, 2, 4, 10, 12, 8].to_vector(:scale) + reg = Statsample::Regression::Simple.new_from_vectors(a, b) + assert_in_delta((reg.ssr + reg.sse).to_f, reg.sst, 0.001) + assert_in_delta(Statsample::Bivariate.pearson(a, b), reg.r, 0.001) + assert_in_delta(2.4, reg.a, 0.01) + assert_in_delta(1.314, reg.b, 0.001) + assert_in_delta(0.657, reg.r, 0.001) + assert_in_delta(0.432, reg.r2, 0.001) end end diff --git a/test/test_stest.rb b/test/test_stest.rb index 7f5b1fd..1ce8244 100644 --- a/test/test_stest.rb +++ b/test/test_stest.rb @@ -1,56 +1,55 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleTestTestCase < Minitest::Test def test_chi_square_matrix_with_expected - real=Matrix[[95,95],[45,155]] - expected=Matrix[[68,122],[72,128]] + real = Matrix[[95, 95], [45, 155]] + expected = Matrix[[68, 122], [72, 128]] assert_nothing_raised do - Statsample::Test.chi_square(real,expected) + Statsample::Test.chi_square(real, expected) end - chi=Statsample::Test.chi_square(real,expected).chi_square - assert_in_delta(32.53,chi,0.1) - + chi = Statsample::Test.chi_square(real, expected).chi_square + assert_in_delta(32.53, chi, 0.1) end + def test_chi_square_matrix_only_observed - observed=Matrix[[20,30,40],[30,40,50],[60,70,80],[10,20,40]] + observed = Matrix[[20, 30, 40], [30, 40, 50], [60, 70, 80], [10, 20, 40]] assert_nothing_raised do Statsample::Test.chi_square(observed) end - chi=Statsample::Test.chi_square(observed) + chi = Statsample::Test.chi_square(observed) assert_in_delta(9.5602, chi.chi_square, 0.0001) assert_in_delta(0.1444, chi.probability, 0.0001) assert_equal(6, chi.df) - end def test_u_mannwhitney - a=[1,2,3,4,5,6].to_scale - b=[0,5,7,9,10,11].to_scale - assert_equal(7.5, Statsample::Test.u_mannwhitney(a,b).u) - assert_equal(7.5, Statsample::Test.u_mannwhitney(b,a).u) - a=[1, 7,8,9,10,11].to_scale - b=[2,3,4,5,6,12].to_scale - assert_equal(11, Statsample::Test.u_mannwhitney(a,b).u) + a = [1, 2, 3, 4, 5, 6].to_scale + b = [0, 5, 7, 9, 10, 11].to_scale + assert_equal(7.5, Statsample::Test.u_mannwhitney(a, b).u) + assert_equal(7.5, Statsample::Test.u_mannwhitney(b, a).u) + a = [1, 7, 8, 9, 10, 11].to_scale + b = [2, 3, 4, 5, 6, 12].to_scale + assert_equal(11, Statsample::Test.u_mannwhitney(a, b).u) end - def test_levene - a=[1,2,3,4,5,6,7,8,100,10].to_scale - b=[30,40,50,60,70,80,90,100,110,120].to_scale - levene=Statsample::Test::Levene.new([a,b]) + a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_scale + b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_scale + levene = Statsample::Test::Levene.new([a, b]) assert_levene(levene) end + def test_levene_dataset - a=[1,2,3,4,5,6,7,8,100,10].to_scale - b=[30,40,50,60,70,80,90,100,110,120].to_scale - ds={'a'=>a,'b'=>b}.to_dataset - levene=Statsample::Test::Levene.new(ds) + a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_scale + b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_scale + ds = { 'a' => a, 'b' => b }.to_dataset + levene = Statsample::Test::Levene.new(ds) assert_levene(levene) end + def assert_levene(levene) assert_in_delta(0.778, levene.f, 0.001) assert_in_delta(0.389, levene.probability, 0.001) end - end diff --git a/test/test_stratified.rb b/test/test_stratified.rb index cb8d451..9ab0f8f 100644 --- a/test/test_stratified.rb +++ b/test/test_stratified.rb @@ -1,17 +1,17 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleStratifiedTestCase < Minitest::Test - def initialize(*args) super end + def test_mean - a=[10,20,30,40,50] - b=[110,120,130,140] - pop=a+b - av=a.to_vector(:scale) - bv=b.to_vector(:scale) - popv=pop.to_vector(:scale) - assert_equal(popv.mean,Statsample::StratifiedSample.mean(av,bv)) + a = [10, 20, 30, 40, 50] + b = [110, 120, 130, 140] + pop = a + b + av = a.to_vector(:scale) + bv = b.to_vector(:scale) + popv = pop.to_vector(:scale) + assert_equal(popv.mean, Statsample::StratifiedSample.mean(av, bv)) end end diff --git a/test/test_test_f.rb b/test/test_test_f.rb index 182b087..0ef0650 100644 --- a/test/test_test_f.rb +++ b/test/test_test_f.rb @@ -1,32 +1,32 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleTestFTestCase < Minitest::Test context(Statsample::Test::F) do setup do - @ssb=84 - @ssw=68 - @df_num=2 - @df_den=15 - @f=Statsample::Test::F.new(@ssb.quo(@df_num),@ssw.quo(@df_den), @df_num, @df_den) + @ssb = 84 + @ssw = 68 + @df_num = 2 + @df_den = 15 + @f = Statsample::Test::F.new(@ssb.quo(@df_num), @ssw.quo(@df_den), @df_num, @df_den) end - should "have #f equal to msb/msw" do + should 'have #f equal to msb/msw' do assert_equal((@ssb.quo(@df_num)).quo(@ssw.quo(@df_den)), @f.f) end - should "have df total equal to df_num+df_den" do + should 'have df total equal to df_num+df_den' do assert_equal(@df_num + @df_den, @f.df_total) end - should "have probability near 0.002" do + should 'have probability near 0.002' do assert_in_delta(0.002, @f.probability, 0.0005) end - should "be coerced into float" do + should 'be coerced into float' do assert_equal(@f.to_f, @f.f) end - context("method summary") do + context('method summary') do setup do - @summary=@f.summary + @summary = @f.summary end - should "have size > 0" do - assert(@summary.size>0) + should 'have size > 0' do + assert(@summary.size > 0) end end end diff --git a/test/test_test_kolmogorovsmirnov.rb b/test/test_test_kolmogorovsmirnov.rb index 5e7ad2c..7b698a1 100644 --- a/test/test_test_kolmogorovsmirnov.rb +++ b/test/test_test_kolmogorovsmirnov.rb @@ -1,28 +1,28 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleTestKolmogorovSmirnovTestCase < Minitest::Test context(Statsample::Test::KolmogorovSmirnov) do - should "calculate correctly D for two given samples" do - a=[1.1,2.5,5.6,9] - b=[1,2.3,5.8,10] - ks=Statsample::Test::KolmogorovSmirnov.new(a,b) - assert_equal(0.25,ks.d) + should 'calculate correctly D for two given samples' do + a = [1.1, 2.5, 5.6, 9] + b = [1, 2.3, 5.8, 10] + ks = Statsample::Test::KolmogorovSmirnov.new(a, b) + assert_equal(0.25, ks.d) end - should "calculate correctly D for a normal sample and Normal Distribution" do - a=[0.30022510,-0.36664035,0.08593404,1.29881130,-0.49878633,-0.63056010, 0.28397638, -0.04913700,0.03566644,-1.33414346] - ks=Statsample::Test::KolmogorovSmirnov.new(a,Distribution::Normal) - assert_in_delta(0.282, ks.d,0.001) + should 'calculate correctly D for a normal sample and Normal Distribution' do + a = [0.30022510, -0.36664035, 0.08593404, 1.29881130, -0.49878633, -0.63056010, 0.28397638, -0.04913700, 0.03566644, -1.33414346] + ks = Statsample::Test::KolmogorovSmirnov.new(a, Distribution::Normal) + assert_in_delta(0.282, ks.d, 0.001) end - should "calculate correctly D for a variable normal and Normal Distribution" do - rng=Distribution::Normal.rng - a=100.times.map {rng.call} - ks=Statsample::Test::KolmogorovSmirnov.new(a,Distribution::Normal) - assert(ks.d<0.15) + should 'calculate correctly D for a variable normal and Normal Distribution' do + rng = Distribution::Normal.rng + a = 100.times.map { rng.call } + ks = Statsample::Test::KolmogorovSmirnov.new(a, Distribution::Normal) + assert(ks.d < 0.15) end context(Statsample::Test::KolmogorovSmirnov::EmpiricDistribution) do - should "Create a correct empirical distribution for an array" do - a=[10,9,8,7,6,5,4,3,2,1] - ed=Statsample::Test::KolmogorovSmirnov::EmpiricDistribution.new(a) + should 'Create a correct empirical distribution for an array' do + a = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] + ed = Statsample::Test::KolmogorovSmirnov::EmpiricDistribution.new(a) assert_equal(0, ed.cdf(-2)) assert_equal(0.5, ed.cdf(5)) assert_equal(0.5, ed.cdf(5.5)) diff --git a/test/test_test_t.rb b/test/test_test_t.rb index b2a1836..26ce98e 100644 --- a/test/test_test_t.rb +++ b/test/test_test_t.rb @@ -1,62 +1,62 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleTestTTestCase < Minitest::Test include Statsample::Test include Math context T do setup do - @a=[30.02, 29.99, 30.11, 29.97, 30.01, 29.99].to_scale - @b=[29.89, 29.93, 29.72, 29.98, 30.02, 29.98].to_scale - @x1=@a.mean - @x2=@b.mean - @s1=@a.sd - @s2=@b.sd - @n1=@a.n - @n2=@b.n - end - should "calculate correctly standard t" do - t=Statsample::Test::T.new(@x1, @s1.quo(Math.sqrt(@a.n)), @a.n-1) + @a = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99].to_scale + @b = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98].to_scale + @x1 = @a.mean + @x2 = @b.mean + @s1 = @a.sd + @s2 = @b.sd + @n1 = @a.n + @n2 = @b.n + end + should 'calculate correctly standard t' do + t = Statsample::Test::T.new(@x1, @s1.quo(Math.sqrt(@a.n)), @a.n - 1) assert_equal((@x1).quo(@s1.quo(Math.sqrt(@a.n))), t.t) - assert_equal(@a.n-1, t.df) - assert(t.summary.size>0) - end - should "calculate correctly t for one sample" do - t1=[6, 4, 6, 7, 4,5,5,12,6,1].to_scale - t2=[9, 6, 5,10,10,8,7,10,6,5].to_scale - d=t1-t2 - t=Statsample::Test::T::OneSample.new(d) + assert_equal(@a.n - 1, t.df) + assert(t.summary.size > 0) + end + should 'calculate correctly t for one sample' do + t1 = [6, 4, 6, 7, 4, 5, 5, 12, 6, 1].to_scale + t2 = [9, 6, 5, 10, 10, 8, 7, 10, 6, 5].to_scale + d = t1 - t2 + t = Statsample::Test::T::OneSample.new(d) assert_in_delta(-2.631, t.t, 0.001) - assert_in_delta( 0.027, t.probability, 0.001) - assert_in_delta( 0.76012, t.se, 0.0001) - assert(t.summary.size>0) - end - should "calculate correctly t for two samples" do - assert_in_delta(1.959, T.two_sample_independent(@x1, @x2, @s1, @s2, @n1, @n2),0.001) - assert_in_delta(1.959, T.two_sample_independent(@x1, @x2, @s1, @s2, @n1, @n2,true),0.001) - end - should "calculate correctly df for equal and unequal variance" do - assert_equal(10, T.df_equal_variance(@n1,@n2)) - assert_in_delta(7.03, T.df_not_equal_variance(@s1,@s2,@n1,@n2),0.001) - end - should "calculate all values for T object" do - t=Statsample::Test.t_two_samples_independent(@a,@b) - assert(t.summary.size>0) - assert_in_delta(1.959, t.t_equal_variance,0.001) - assert_in_delta(1.959, t.t_not_equal_variance,0.001) - assert_in_delta(10, t.df_equal_variance,0.001) - assert_in_delta(7.03, t.df_not_equal_variance,0.001) - assert_in_delta(0.07856, t.probability_equal_variance,0.001) - assert_in_delta(0.09095, t.probability_not_equal_variance,0.001) - end - should "be the same using shorthand" do - v=100.times.map {rand(100)}.to_scale + assert_in_delta(0.027, t.probability, 0.001) + assert_in_delta(0.76012, t.se, 0.0001) + assert(t.summary.size > 0) + end + should 'calculate correctly t for two samples' do + assert_in_delta(1.959, T.two_sample_independent(@x1, @x2, @s1, @s2, @n1, @n2), 0.001) + assert_in_delta(1.959, T.two_sample_independent(@x1, @x2, @s1, @s2, @n1, @n2, true), 0.001) + end + should 'calculate correctly df for equal and unequal variance' do + assert_equal(10, T.df_equal_variance(@n1, @n2)) + assert_in_delta(7.03, T.df_not_equal_variance(@s1, @s2, @n1, @n2), 0.001) + end + should 'calculate all values for T object' do + t = Statsample::Test.t_two_samples_independent(@a, @b) + assert(t.summary.size > 0) + assert_in_delta(1.959, t.t_equal_variance, 0.001) + assert_in_delta(1.959, t.t_not_equal_variance, 0.001) + assert_in_delta(10, t.df_equal_variance, 0.001) + assert_in_delta(7.03, t.df_not_equal_variance, 0.001) + assert_in_delta(0.07856, t.probability_equal_variance, 0.001) + assert_in_delta(0.09095, t.probability_not_equal_variance, 0.001) + end + should 'be the same using shorthand' do + v = 100.times.map { rand(100) }.to_scale assert_equal(Statsample::Test.t_one_sample(v).t, T::OneSample.new(v).t) end - should "calculate all values for one sample T test" do - u=@a.mean+(1-rand*2) - tos=T::OneSample.new(@a,{:u=>u}) - assert_equal((@a.mean-u).quo(@a.sd.quo(sqrt(@a.n))), tos.t) - assert_equal(@a.n-1, tos.df) - assert(tos.summary.size>0) + should 'calculate all values for one sample T test' do + u = @a.mean + (1 - rand * 2) + tos = T::OneSample.new(@a, u: u) + assert_equal((@a.mean - u).quo(@a.sd.quo(sqrt(@a.n))), tos.t) + assert_equal(@a.n - 1, tos.df) + assert(tos.summary.size > 0) end end end diff --git a/test/test_umannwhitney.rb b/test/test_umannwhitney.rb index a010a87..1f09562 100644 --- a/test/test_umannwhitney.rb +++ b/test/test_umannwhitney.rb @@ -1,27 +1,27 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleUMannWhitneyTestCase < Minitest::Test include Statsample::Test context Statsample::Test::UMannWhitney do setup do - @v1=[1,2,3,4,7,8,9,10,14,15].to_scale - @v2=[5,6,11,12,13,16,17,18,19].to_scale - @u=Statsample::Test::UMannWhitney.new(@v1,@v2) + @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15].to_scale + @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19].to_scale + @u = Statsample::Test::UMannWhitney.new(@v1, @v2) end - should "have same result using class or Test#u_mannwhitney" do - assert_equal(Statsample::Test.u_mannwhitney(@v1,@v2).u, @u.u) + should 'have same result using class or Test#u_mannwhitney' do + assert_equal(Statsample::Test.u_mannwhitney(@v1, @v2).u, @u.u) end - should "have correct U values" do - assert_equal(73,@u.r1) - assert_equal(117,@u.r2) - assert_equal(18,@u.u) + should 'have correct U values' do + assert_equal(73, @u.r1) + assert_equal(117, @u.r2) + assert_equal(18, @u.u) end - should "have correct value for z" do - assert_in_delta(-2.205,@u.z,0.001) + should 'have correct value for z' do + assert_in_delta(-2.205, @u.z, 0.001) end - should "have correct value for z and exact probability" do - assert_in_delta(0.027,@u.probability_z,0.001) - assert_in_delta(0.028,@u.probability_exact,0.001) + should 'have correct value for z and exact probability' do + assert_in_delta(0.027, @u.probability_z, 0.001) + assert_in_delta(0.028, @u.probability_exact, 0.001) end end end diff --git a/test/test_vector.rb b/test/test_vector.rb index 03d3674..b73af37 100644 --- a/test/test_vector.rb +++ b/test/test_vector.rb @@ -1,383 +1,376 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleTestVector < Minitest::Test include Statsample::Shorthand def setup - @c = Statsample::Vector.new([5,5,5,5,5,6,6,7,8,9,10,1,2,3,4,nil,-99,-99], :nominal) - @c.name="Test Vector" - @c.missing_values=[-99] + @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :nominal) + @c.name = 'Test Vector' + @c.missing_values = [-99] end + def assert_counting_tokens(b) - assert_equal([1,1,0,1,0,nil],b['a'].to_a) - assert_equal([0,1,0,0,0,nil],b['b'].to_a) - assert_equal([0,0,1,0,0,nil],b['c'].to_a) - assert_equal([0,0,1,1,0,nil],b['d'].to_a) - assert_equal([0,0,0,0,1,nil],b[10].to_a) + assert_equal([1, 1, 0, 1, 0, nil], b['a'].to_a) + assert_equal([0, 1, 0, 0, 0, nil], b['b'].to_a) + assert_equal([0, 0, 1, 0, 0, nil], b['c'].to_a) + assert_equal([0, 0, 1, 1, 0, nil], b['d'].to_a) + assert_equal([0, 0, 0, 0, 1, nil], b[10].to_a) end context Statsample do setup do - @sample=100 - @a=@sample.times.map{|i| (i+rand(10)) %10 ==0 ? nil : rand(100)}.to_scale - @b=@sample.times.map{|i| (i+rand(10)) %10 ==0 ? nil : rand(100)}.to_scale - @correct_a=Array.new - @correct_b=Array.new - @a.each_with_index do |v,i| + @sample = 100 + @a = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_scale + @b = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_scale + @correct_a = [] + @correct_b = [] + @a.each_with_index do |_v, i| if !@a[i].nil? and !@b[i].nil? @correct_a.push(@a[i]) @correct_b.push(@b[i]) end end - @correct_a=@correct_a.to_scale - @correct_b=@correct_b.to_scale - - @common=lambda do |av,bv| - assert_equal(@correct_a, av, "A no es esperado") - assert_equal(@correct_b, bv, "B no es esperado") - assert(!av.has_missing_data?, "A tiene datos faltantes") - assert(!bv.has_missing_data?, "b tiene datos faltantes") + @correct_a = @correct_a.to_scale + @correct_b = @correct_b.to_scale + + @common = lambda do |av, bv| + assert_equal(@correct_a, av, 'A no es esperado') + assert_equal(@correct_b, bv, 'B no es esperado') + assert(!av.has_missing_data?, 'A tiene datos faltantes') + assert(!bv.has_missing_data?, 'b tiene datos faltantes') end end - should "return correct only_valid" do - av,bv=Statsample.only_valid @a,@b - av2,bv2=Statsample.only_valid av,bv - @common.call(av,bv) - assert_equal(av,av2) - assert_not_same(av,av2) - assert_not_same(bv,bv2) - end - should "return correct only_valid_clone" do - av,bv=Statsample.only_valid_clone @a,@b - @common.call(av,bv) - av2,bv2=Statsample.only_valid_clone av,bv - assert_equal(av,av2) - assert_same(av,av2) - assert_same(bv,bv2) + should 'return correct only_valid' do + av, bv = Statsample.only_valid @a, @b + av2, bv2 = Statsample.only_valid av, bv + @common.call(av, bv) + assert_equal(av, av2) + assert_not_same(av, av2) + assert_not_same(bv, bv2) + end + should 'return correct only_valid_clone' do + av, bv = Statsample.only_valid_clone @a, @b + @common.call(av, bv) + av2, bv2 = Statsample.only_valid_clone av, bv + assert_equal(av, av2) + assert_same(av, av2) + assert_same(bv, bv2) end end context Statsample::Vector do setup do - @c = Statsample::Vector.new([5,5,5,5,5,6,6,7,8,9,10,1,2,3,4,nil,-99,-99], :nominal) - @c.name="Test Vector" - @c.missing_values=[-99] - end - should_with_gsl "be created with GSL::Vector" do - gsl=GSL::Vector[1,2,3,4,5] - v=Statsample::Vector.new(gsl) - assert_equal([1,2,3,4,5], v.to_a) + @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :nominal) + @c.name = 'Test Vector' + @c.missing_values = [-99] + end + should_with_gsl 'be created with GSL::Vector' do + gsl = GSL::Vector[1, 2, 3, 4, 5] + v = Statsample::Vector.new(gsl) + assert_equal([1, 2, 3, 4, 5], v.to_a) refute(v.flawed?) - end - context "using matrix operations" do + context 'using matrix operations' do setup do - @a=[1,2,3,4,5].to_scale + @a = [1, 2, 3, 4, 5].to_scale end - should "to_matrix returns a matrix with 1 row" do - mh=Matrix[[1,2,3,4,5]] - assert_equal(mh,@a.to_matrix) + should 'to_matrix returns a matrix with 1 row' do + mh = Matrix[[1, 2, 3, 4, 5]] + assert_equal(mh, @a.to_matrix) end - should "to_matrix(:vertical) returns a matrix with 1 column" do - mv=Matrix.columns([[1,2,3,4,5]]) - assert_equal(mv,@a.to_matrix(:vertical)) + should 'to_matrix(:vertical) returns a matrix with 1 column' do + mv = Matrix.columns([[1, 2, 3, 4, 5]]) + assert_equal(mv, @a.to_matrix(:vertical)) end - should "returns valid submatrixes" do + should 'returns valid submatrixes' do # 3*4 + 2*5 = 22 - a=[3,2].to_vector(:scale) - b=[4,5].to_vector(:scale) - assert_equal(22,(a.to_matrix*b.to_matrix(:vertical))[0,0]) + a = [3, 2].to_vector(:scale) + b = [4, 5].to_vector(:scale) + assert_equal(22, (a.to_matrix * b.to_matrix(:vertical))[0, 0]) end end - context "when initializing" do + context 'when initializing' do setup do - @data=(10.times.map{rand(100)})+[nil] - @original=Statsample::Vector.new(@data, :scale) + @data = (10.times.map { rand(100) }) + [nil] + @original = Statsample::Vector.new(@data, :scale) end - should "be the sample using []" do - second=Statsample::Vector[*@data] + should 'be the sample using []' do + second = Statsample::Vector[*@data] assert_equal(@original, second) end - should "[] returns same results as R-c()" do - reference=[0,4,5,6,10].to_scale - assert_equal(reference, Statsample::Vector[0,4,5,6,10]) - assert_equal(reference, Statsample::Vector[0,4..6,10]) - assert_equal(reference, Statsample::Vector[[0],[4,5,6],[10]]) - assert_equal(reference, Statsample::Vector[[0],[4,[5,[6]]],[10]]) - - assert_equal(reference, Statsample::Vector[[0],[4,5,6].to_vector,[10]]) - + should '[] returns same results as R-c()' do + reference = [0, 4, 5, 6, 10].to_scale + assert_equal(reference, Statsample::Vector[0, 4, 5, 6, 10]) + assert_equal(reference, Statsample::Vector[0, 4..6, 10]) + assert_equal(reference, Statsample::Vector[[0], [4, 5, 6], [10]]) + assert_equal(reference, Statsample::Vector[[0], [4, [5, [6]]], [10]]) + + assert_equal(reference, Statsample::Vector[[0], [4, 5, 6].to_vector, [10]]) end - should "be the same usign #to_vector" do - lazy1=@data.to_vector(:scale) - assert_equal(@original,lazy1) + should 'be the same usign #to_vector' do + lazy1 = @data.to_vector(:scale) + assert_equal(@original, lazy1) end - should "be the same using #to_scale" do - lazy2=@data.to_scale - assert_equal(@original,lazy2) - assert_equal(:scale,lazy2.type) - assert_equal(@data.find_all{|v| !v.nil?},lazy2.valid_data) + should 'be the same using #to_scale' do + lazy2 = @data.to_scale + assert_equal(@original, lazy2) + assert_equal(:scale, lazy2.type) + assert_equal(@data.find_all { |v| !v.nil? }, lazy2.valid_data) end - should "could use new_scale with size only" do - v1=10.times.map {nil}.to_scale - v2=Statsample::Vector.new_scale(10) - assert_equal(v1,v2) - + should 'could use new_scale with size only' do + v1 = 10.times.map { nil }.to_scale + v2 = Statsample::Vector.new_scale(10) + assert_equal(v1, v2) end - should "could use new_scale with size and value" do - a=rand - v1=10.times.map {a}.to_scale - v2=Statsample::Vector.new_scale(10,a) - assert_equal(v1,v2) + should 'could use new_scale with size and value' do + a = rand + v1 = 10.times.map { a }.to_scale + v2 = Statsample::Vector.new_scale(10, a) + assert_equal(v1, v2) end - should "could use new_scale with func" do - v1=10.times.map {|i| i*2}.to_scale - v2=Statsample::Vector.new_scale(10) {|i| i*2} - assert_equal(v1,v2) + should 'could use new_scale with func' do + v1 = 10.times.map { |i| i * 2 }.to_scale + v2 = Statsample::Vector.new_scale(10) { |i| i * 2 } + assert_equal(v1, v2) end - end - context "#split_by_separator" do - + context '#split_by_separator' do setup do - @a = Statsample::Vector.new(["a","a,b","c,d","a,d",10,nil],:nominal) - @b=@a.split_by_separator(",") + @a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 10, nil], :nominal) + @b = @a.split_by_separator(',') end - should "returns a Hash" do + should 'returns a Hash' do assert_kind_of(Hash, @b) end - should "return a Hash with keys with different values of @a" do - expected=['a','b','c','d',10] + should 'return a Hash with keys with different values of @a' do + expected = ['a', 'b', 'c', 'd', 10] assert_equal(expected, @b.keys) end - should "returns a Hash, which values are Statsample::Vector" do - @b.each_key {|k| assert_instance_of(Statsample::Vector, @b[k])} + should 'returns a Hash, which values are Statsample::Vector' do + @b.each_key { |k| assert_instance_of(Statsample::Vector, @b[k]) } end - should "hash values are n times the tokens appears" do + should 'hash values are n times the tokens appears' do assert_counting_tokens(@b) end - should "#split_by_separator_freq returns the number of ocurrences of tokens" do - assert_equal({'a'=>3,'b'=>1,'c'=>1,'d'=>2,10=>1}, @a.split_by_separator_freq()) + should '#split_by_separator_freq returns the number of ocurrences of tokens' do + assert_equal({ 'a' => 3, 'b' => 1, 'c' => 1, 'd' => 2, 10 => 1 }, @a.split_by_separator_freq) end - should "using a different separator give the same values" do - a = Statsample::Vector.new(["a","a*b","c*d","a*d",10,nil],:nominal) - b=a.split_by_separator("*") + should 'using a different separator give the same values' do + a = Statsample::Vector.new(['a', 'a*b', 'c*d', 'a*d', 10, nil], :nominal) + b = a.split_by_separator('*') assert_counting_tokens(b) end end - should "return correct median_absolute_deviation" do - a=[1, 1, 2, 2, 4, 6, 9].to_scale + should 'return correct median_absolute_deviation' do + a = [1, 1, 2, 2, 4, 6, 9].to_scale assert_equal(1, a.median_absolute_deviation) end - should "return correct histogram" do - a=10.times.map {|v| v}.to_scale - hist=a.histogram(2) - assert_equal([5,5], hist.bin) + should 'return correct histogram' do + a = 10.times.map { |v| v }.to_scale + hist = a.histogram(2) + assert_equal([5, 5], hist.bin) 3.times do |i| - assert_in_delta(i*4.5, hist.get_range(i)[0], 1e-9) + assert_in_delta(i * 4.5, hist.get_range(i)[0], 1e-9) end - end - should "have a name" do - @c.name=="Test Vector" + should 'have a name' do + @c.name == 'Test Vector' end - should "without explicit name, returns vector with succesive numbers" do - a=10.times.map{rand(100)}.to_scale - b=10.times.map{rand(100)}.to_scale + should 'without explicit name, returns vector with succesive numbers' do + a = 10.times.map { rand(100) }.to_scale + b = 10.times.map { rand(100) }.to_scale assert_match(/Vector \d+/, a.name) - a.name=~/Vector (\d+)/ - next_number=$1.to_i+1 - assert_equal("Vector #{next_number}",b.name) + a.name =~ /Vector (\d+)/ + next_number = Regexp.last_match(1).to_i + 1 + assert_equal("Vector #{next_number}", b.name) end - should "save to a file and load the same Vector" do - outfile=Tempfile.new("vector.vec") + should 'save to a file and load the same Vector' do + outfile = Tempfile.new('vector.vec') @c.save(outfile.path) - a=Statsample.load(outfile.path) - assert_equal(@c,a) - end - should "#collect returns an array" do - val=@c.collect {|v| v} - assert_equal(val,[5,5,5,5,5,6,6,7,8,9,10,1,2,3,4,nil,-99,-99]) - end - - should "#recode returns a recoded array" do - a=@c.recode{|v| @c.is_valid?(v) ? 0 : 1 } - exp=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1].to_vector - assert_equal(exp,a) - exp.recode!{|v| v==0 ? 1:0} - exp2=(([1]*15)+([0]*3)).to_vector - assert_equal(exp2,exp) - end - should "#product returns the * of all values" do - a=[1,2,3,4,5].to_vector(:scale) - assert_equal(120,a.product) - end - - should "missing values" do - @c.missing_values=[10] - assert_equal([-99,-99,1,2,3,4,5,5,5,5,5,6,6,7,8,9], @c.valid_data.sort) - assert_equal([5,5,5,5,5,6,6,7,8,9,nil,1,2,3,4,nil,-99,-99], @c.data_with_nils) - @c.missing_values=[-99] - assert_equal(@c.valid_data.sort,[1,2,3,4,5,5,5,5,5,6,6,7,8,9,10]) - assert_equal(@c.data_with_nils,[5,5,5,5,5,6,6,7,8,9,10,1,2,3,4,nil,nil,nil]) - @c.missing_values=[] - assert_equal(@c.valid_data.sort,[-99,-99,1,2,3,4,5,5,5,5,5,6,6,7,8,9,10]) - assert_equal(@c.data_with_nils,[5,5,5,5,5,6,6,7,8,9,10,1,2,3,4,nil,-99,-99]) - - end - should "correct has_missing_data? with missing data" do - a=[1,2,3,nil].to_vector + a = Statsample.load(outfile.path) + assert_equal(@c, a) + end + should '#collect returns an array' do + val = @c.collect { |v| v } + assert_equal(val, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]) + end + + should '#recode returns a recoded array' do + a = @c.recode { |v| @c.is_valid?(v) ? 0 : 1 } + exp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1].to_vector + assert_equal(exp, a) + exp.recode! { |v| v == 0 ? 1 : 0 } + exp2 = (([1] * 15) + ([0] * 3)).to_vector + assert_equal(exp2, exp) + end + should '#product returns the * of all values' do + a = [1, 2, 3, 4, 5].to_vector(:scale) + assert_equal(120, a.product) + end + + should 'missing values' do + @c.missing_values = [10] + assert_equal([-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9], @c.valid_data.sort) + assert_equal([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, nil, 1, 2, 3, 4, nil, -99, -99], @c.data_with_nils) + @c.missing_values = [-99] + assert_equal(@c.valid_data.sort, [1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]) + assert_equal(@c.data_with_nils, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, nil, nil]) + @c.missing_values = [] + assert_equal(@c.valid_data.sort, [-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]) + assert_equal(@c.data_with_nils, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]) + end + should 'correct has_missing_data? with missing data' do + a = [1, 2, 3, nil].to_vector assert(a.has_missing_data?) end - should "correct has_missing_data? without missing data" do - a=[1,2,3,4,10].to_vector + should 'correct has_missing_data? without missing data' do + a = [1, 2, 3, 4, 10].to_vector assert(!a.has_missing_data?) end - should "with explicit missing_values, should respond has_missing_data?" do - a=[1,2,3,4,10].to_vector - a.missing_values=[10] + should 'with explicit missing_values, should respond has_missing_data?' do + a = [1, 2, 3, 4, 10].to_vector + a.missing_values = [10] assert(a.has_missing_data?) end - should "label correctly fields" do - @c.labels={5=>'FIVE'} - assert_equal(["FIVE","FIVE","FIVE","FIVE","FIVE",6,6,7,8,9,10,1,2,3,4,nil,-99, -99],@c.vector_labeled.to_a) + should 'label correctly fields' do + @c.labels = { 5 => 'FIVE' } + assert_equal(['FIVE', 'FIVE', 'FIVE', 'FIVE', 'FIVE', 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], @c.vector_labeled.to_a) end - should "verify" do - h=@c.verify{|d| !d.nil? and d>0} - e={15=>nil,16=>-99,17=>-99} - assert_equal(e,h) + should 'verify' do + h = @c.verify { |d| !d.nil? and d > 0 } + e = { 15 => nil, 16 => -99, 17 => -99 } + assert_equal(e, h) end - should "have a summary with name on it" do + should 'have a summary with name on it' do assert_match(/#{@c.name}/, @c.summary) end - should "GSL::Vector based should push correcty" do + should 'GSL::Vector based should push correcty' do if Statsample.has_gsl? - v=GSL::Vector[1,2,3,4,5].to_scale + v = GSL::Vector[1, 2, 3, 4, 5].to_scale v.push(nil) - assert_equal([1,2,3,4,5,nil], v.to_a) + assert_equal([1, 2, 3, 4, 5, nil], v.to_a) assert(v.flawed?) else - skip("Requires GSL") + skip('Requires GSL') end end - - should "split correctly" do - a = Statsample::Vector.new(["a","a,b","c,d","a,d","d",10,nil],:nominal) - assert_equal([%w{a},%w{a b},%w{c d},%w{a d},%w{d},[10],nil], a.splitted) + should 'split correctly' do + a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 'd', 10, nil], :nominal) + assert_equal([%w(a), %w(a b), %w(c d), %w(a d), %w(d), [10], nil], a.splitted) end - should "multiply correct for scalar" do - a = [1,2,3].to_scale - assert_equal([5,10,15].to_scale, a*5) + should 'multiply correct for scalar' do + a = [1, 2, 3].to_scale + assert_equal([5, 10, 15].to_scale, a * 5) end - should "multiply correct with other vector" do - a = [1,2,3].to_scale - b = [2,4,6].to_scale + should 'multiply correct with other vector' do + a = [1, 2, 3].to_scale + b = [2, 4, 6].to_scale - assert_equal([2,8,18].to_scale, a*b) + assert_equal([2, 8, 18].to_scale, a * b) end - should "sum correct for scalar" do - a = [1,2,3].to_scale - assert_equal([11,12,13].to_scale, a+10) + should 'sum correct for scalar' do + a = [1, 2, 3].to_scale + assert_equal([11, 12, 13].to_scale, a + 10) end - should "raise NoMethodError when method requires ordinal and vector is nominal" do - @c.type=:nominal + should 'raise NoMethodError when method requires ordinal and vector is nominal' do + @c.type = :nominal assert_raise(::NoMethodError) { @c.median } end - should "raise NoMethodError when method requires scalar and vector is ordinal" do - @c.type=:ordinal + should 'raise NoMethodError when method requires scalar and vector is ordinal' do + @c.type = :ordinal assert_raise(::NoMethodError) { @c.mean } end - should "jacknife correctly with named method" do + should 'jacknife correctly with named method' do # First example - a=[1,2,3,4].to_scale - ds=a.jacknife(:mean) + a = [1, 2, 3, 4].to_scale + ds = a.jacknife(:mean) assert_equal(a.mean, ds[:mean].mean) - ds=a.jacknife([:mean,:sd]) + ds = a.jacknife([:mean, :sd]) assert_equal(a.mean, ds[:mean].mean) assert_equal(a.sd, ds[:mean].sd) end - should "jacknife correctly with custom method" do + should 'jacknife correctly with custom method' do # Second example - a=[17.23, 18.71,13.93,18.81,15.78,11.29,14.91,13.39, 18.21, 11.57, 14.28, 10.94, 18.83, 15.52,13.45,15.25].to_scale - ds=a.jacknife(:log_s2=>lambda {|v| Math.log(v.variance) }) - exp=[1.605, 2.972, 1.151, 3.097, 0.998, 3.308, 0.942, 1.393, 2.416, 2.951, 1.043, 3.806, 3.122, 0.958, 1.362, 0.937].to_scale + a = [17.23, 18.71, 13.93, 18.81, 15.78, 11.29, 14.91, 13.39, 18.21, 11.57, 14.28, 10.94, 18.83, 15.52, 13.45, 15.25].to_scale + ds = a.jacknife(log_s2: ->(v) { Math.log(v.variance) }) + exp = [1.605, 2.972, 1.151, 3.097, 0.998, 3.308, 0.942, 1.393, 2.416, 2.951, 1.043, 3.806, 3.122, 0.958, 1.362, 0.937].to_scale assert_similar_vector(exp, ds[:log_s2], 0.001) assert_in_delta(2.00389, ds[:log_s2].mean, 0.00001) assert_in_delta(1.091, ds[:log_s2].variance, 0.001) end - should "jacknife correctly with k>1" do - a=rnorm(6) - ds=a.jacknife(:mean,2) - mean=a.mean - exp=[3*mean-2*(a[2]+a[3]+a[4]+a[5]) / 4, 3*mean-2*(a[0]+a[1]+a[4]+a[5]) / 4, 3*mean-2*(a[0]+a[1]+a[2]+a[3]) / 4].to_scale + should 'jacknife correctly with k>1' do + a = rnorm(6) + ds = a.jacknife(:mean, 2) + mean = a.mean + exp = [3 * mean - 2 * (a[2] + a[3] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[2] + a[3]) / 4].to_scale assert_similar_vector(exp, ds[:mean], 1e-13) end - should "bootstrap should return a vector with mean=mu and sd=se" do - a=rnorm(100) - ds=a.bootstrap([:mean,:sd],200) - se=1/Math.sqrt(a.size) + should 'bootstrap should return a vector with mean=mu and sd=se' do + a = rnorm(100) + ds = a.bootstrap([:mean, :sd], 200) + se = 1 / Math.sqrt(a.size) assert_in_delta(0, ds[:mean].mean, 0.3) assert_in_delta(se, ds[:mean].sd, 0.02) end - - end - - def test_nominal - assert_equal(@c[1],5) - assert_equal({ 1=>1,2=>1,3=>1,4=>1,5=>5,6=>2,7=>1,8=>1, 9=>1,10=>1},@c.frequencies) - assert_equal({ 1=>1,2=>1,3=>1,4=>1,5=>5,6=>2,7=>1,8=>1, 9=>1,10=>1},@c._frequencies) - assert_equal({ 1 => 1.quo(15) ,2=>1.quo(15), 3=>1.quo(15),4=>1.quo(15),5=>5.quo(15),6=>2.quo(15),7=>1.quo(15), 8=>1.quo(15), 9=>1.quo(15),10=>1.quo(15)}, @c.proportions) + assert_equal(@c[1], 5) + assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c.frequencies) + assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c._frequencies) + assert_equal({ 1 => 1.quo(15), 2 => 1.quo(15), 3 => 1.quo(15), 4 => 1.quo(15), 5 => 5.quo(15), 6 => 2.quo(15), 7 => 1.quo(15), 8 => 1.quo(15), 9 => 1.quo(15), 10 => 1.quo(15) }, @c.proportions) assert_equal(@c.proportion, 1.quo(15)) assert_equal(@c.proportion(2), 1.quo(15)) - assert_equal([1,2,3,4,5,6,7,8,9,10], @c.factors.sort) - assert_equal(@c.mode,5) - assert_equal(@c.n_valid,15) + assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], @c.factors.sort) + assert_equal(@c.mode, 5) + assert_equal(@c.n_valid, 15) end + def test_equality - v1=[1,2,3].to_vector - v2=[1,2,3].to_vector - assert_equal(v1,v2) - v1=[1,2,3].to_vector(:nominal) - v2=[1,2,3].to_vector(:ordinal) - assert_not_equal(v1,v2) - v2=[1,2,3] - assert_not_equal(v1,v2) - v1=[1,2,3].to_vector() - v2=[1,2,3].to_vector() - assert_equal(v1,v2) + v1 = [1, 2, 3].to_vector + v2 = [1, 2, 3].to_vector + assert_equal(v1, v2) + v1 = [1, 2, 3].to_vector(:nominal) + v2 = [1, 2, 3].to_vector(:ordinal) + assert_not_equal(v1, v2) + v2 = [1, 2, 3] + assert_not_equal(v1, v2) + v1 = [1, 2, 3].to_vector + v2 = [1, 2, 3].to_vector + assert_equal(v1, v2) assert_equal(false, v1 == Object.new) end + def test_vector_percentil - a=[1,2,2,3,4,5,5,5,6,10].to_scale - expected=[10,25,25,40,50,70,70,70,90,100].to_scale + a = [1, 2, 2, 3, 4, 5, 5, 5, 6, 10].to_scale + expected = [10, 25, 25, 40, 50, 70, 70, 70, 90, 100].to_scale assert_equal(expected, a.vector_percentil) - a=[1,nil,nil,2,2,3,4,nil,nil,5,5,5,6,10].to_scale - expected=[10,nil,nil,25,25,40,50,nil,nil,70,70,70,90,100].to_scale + a = [1, nil, nil, 2, 2, 3, 4, nil, nil, 5, 5, 5, 6, 10].to_scale + expected = [10, nil, nil, 25, 25, 40, 50, nil, nil, 70, 70, 70, 90, 100].to_scale assert_equal(expected, a.vector_percentil) end + def test_ordinal - @c.type=:ordinal - assert_equal(5,@c.median) - assert_equal(4,@c.percentil(25)) - assert_equal(7,@c.percentil(75)) - - v=[200000, 200000, 210000, 220000, 230000, 250000, 250000, 250000, 270000, 300000, 450000, 130000, 140000, 140000, 140000, 145000, 148000, 165000, 170000, 180000, 180000, 180000, 180000, 180000, 180000 ].to_scale - assert_equal(180000,v.median) - a=[7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 9.0, 9.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 12.0, 13.0, 14.0, 14.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0].to_scale + @c.type = :ordinal + assert_equal(5, @c.median) + assert_equal(4, @c.percentil(25)) + assert_equal(7, @c.percentil(75)) + + v = [200_000, 200_000, 210_000, 220_000, 230_000, 250_000, 250_000, 250_000, 270_000, 300_000, 450_000, 130_000, 140_000, 140_000, 140_000, 145_000, 148_000, 165_000, 170_000, 180_000, 180_000, 180_000, 180_000, 180_000, 180_000].to_scale + assert_equal(180_000, v.median) + a = [7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 9.0, 9.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 12.0, 13.0, 14.0, 14.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0].to_scale assert_equal(4.5, a.percentil(25)) assert_equal(6.5, a.percentil(50)) assert_equal(9.5, a.percentil(75)) assert_equal(3.0, a.percentil(10)) end + def test_linear_percentil_strategy values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116].shuffle.to_scale assert_equal 102, values.percentil(0, :linear) @@ -393,252 +386,268 @@ def test_linear_percentil_strategy assert_equal 115, values.percentil(75, :linear) assert_equal 118, values.percentil(100, :linear) end + def test_ranked - v1=[0.8,1.2,1.2,2.3,18].to_vector(:ordinal) - expected=[1,2.5,2.5,4,5].to_vector(:ordinal) - assert_equal(expected,v1.ranked) - v1=[nil,0.8,1.2,1.2,2.3,18,nil].to_vector(:ordinal) - expected=[nil,1,2.5,2.5,4,5,nil].to_vector(:ordinal) - assert_equal(expected,v1.ranked) + v1 = [0.8, 1.2, 1.2, 2.3, 18].to_vector(:ordinal) + expected = [1, 2.5, 2.5, 4, 5].to_vector(:ordinal) + assert_equal(expected, v1.ranked) + v1 = [nil, 0.8, 1.2, 1.2, 2.3, 18, nil].to_vector(:ordinal) + expected = [nil, 1, 2.5, 2.5, 4, 5, nil].to_vector(:ordinal) + assert_equal(expected, v1.ranked) end + def test_scale - a=Statsample::Vector.new([1,2,3,4,"STRING"], :scale) + a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :scale) assert_equal(10, a.sum) - i=0 - factors=a.factors.sort - [0,1,2,3,4].each{|v| - assert(v==factors[i]) - assert(v.class==factors[i].class,"#{v} - #{v.class} != #{factors[i]} - #{factors[i].class}") - i+=1 + i = 0 + factors = a.factors.sort + [0, 1, 2, 3, 4].each{|v| + assert(v == factors[i]) + assert(v.class == factors[i].class, "#{v} - #{v.class} != #{factors[i]} - #{factors[i].class}") + i += 1 } end + def test_vector_centered - mean=rand() - samples=11 - centered=samples.times.map {|i| i-((samples/2).floor).to_i}.to_scale - not_centered=centered.recode {|v| v+mean} - obs=not_centered.centered - centered.each_with_index do |v,i| - assert_in_delta(v,obs[i],0.0001) + mean = rand + samples = 11 + centered = samples.times.map { |i| i - ((samples / 2).floor).to_i }.to_scale + not_centered = centered.recode { |v| v + mean } + obs = not_centered.centered + centered.each_with_index do |v, i| + assert_in_delta(v, obs[i], 0.0001) end end + def test_vector_standarized - v1=[1,2,3,4,nil].to_vector(:scale) - sds=v1.sds - expected=[((1-2.5).quo(sds)),((2-2.5).quo(sds)),((3-2.5).quo(sds)),((4-2.5).quo(sds)), nil].to_vector(:scale) - vs=v1.vector_standarized + v1 = [1, 2, 3, 4, nil].to_vector(:scale) + sds = v1.sds + expected = [((1 - 2.5).quo(sds)), ((2 - 2.5).quo(sds)), ((3 - 2.5).quo(sds)), ((4 - 2.5).quo(sds)), nil].to_vector(:scale) + vs = v1.vector_standarized assert_equal(expected, vs) - assert_equal(0,vs.mean) - assert_equal(1,vs.sds) + assert_equal(0, vs.mean) + assert_equal(1, vs.sds) end def test_vector_standarized_with_zero_variance - v1=100.times.map {|i| 1}.to_scale - exp=100.times.map {nil}.to_scale - assert_equal(exp,v1.standarized) + v1 = 100.times.map { |_i| 1 }.to_scale + exp = 100.times.map { nil }.to_scale + assert_equal(exp, v1.standarized) end - def test_check_type - v=Statsample::Vector.new - v.type=:nominal - assert_raise(NoMethodError) { v.check_type(:scale)} - assert_raise(NoMethodError) { v.check_type(:ordinal)} + def test_check_type + v = Statsample::Vector.new + v.type = :nominal + assert_raise(NoMethodError) { v.check_type(:scale) } + assert_raise(NoMethodError) { v.check_type(:ordinal) } assert(v.check_type(:nominal).nil?) - v.type=:ordinal + v.type = :ordinal - assert_raise(NoMethodError) { v.check_type(:scale)} + assert_raise(NoMethodError) { v.check_type(:scale) } assert(v.check_type(:ordinal).nil?) assert(v.check_type(:nominal).nil?) - - v.type=:scale + v.type = :scale assert(v.check_type(:scale).nil?) assert(v.check_type(:ordinal).nil?) assert(v.check_type(:nominal).nil?) - v.type=:date - assert_raise(NoMethodError) { v.check_type(:scale)} - assert_raise(NoMethodError) { v.check_type(:ordinal)} - assert_raise(NoMethodError) { v.check_type(:nominal)} - end + v.type = :date + assert_raise(NoMethodError) { v.check_type(:scale) } + assert_raise(NoMethodError) { v.check_type(:ordinal) } + assert_raise(NoMethodError) { v.check_type(:nominal) } +end def test_add - a=Statsample::Vector.new([1,2,3,4,5], :scale) - b=Statsample::Vector.new([11,12,13,14,15], :scale) - assert_equal([3,4,5,6,7], (a+2).to_a) - assert_equal([12,14,16,18,20], (a+b).to_a) - assert_raise ArgumentError do + a = Statsample::Vector.new([1, 2, 3, 4, 5], :scale) + b = Statsample::Vector.new([11, 12, 13, 14, 15], :scale) + assert_equal([3, 4, 5, 6, 7], (a + 2).to_a) + assert_equal([12, 14, 16, 18, 20], (a + b).to_a) + assert_raise ArgumentError do a + @c end - assert_raise TypeError do - a+"string" + assert_raise TypeError do + a + 'string' end - a=Statsample::Vector.new([nil,1, 2 ,3 ,4 ,5], :scale) - b=Statsample::Vector.new([11, 12,nil,13,14,15], :scale) - assert_equal([nil,13,nil,16,18,20], (a+b).to_a) - assert_equal([nil,13,nil,16,18,20], (a+b.to_a).to_a) + a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :scale) + b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :scale) + assert_equal([nil, 13, nil, 16, 18, 20], (a + b).to_a) + assert_equal([nil, 13, nil, 16, 18, 20], (a + b.to_a).to_a) end + def test_minus - a=Statsample::Vector.new([1,2,3,4,5], :scale) - b=Statsample::Vector.new([11,12,13,14,15], :scale) - assert_equal([-1,0,1,2,3], (a-2).to_a) - assert_equal([10,10,10,10,10], (b-a).to_a) - assert_raise ArgumentError do - a-@c - end - assert_raise TypeError do - a-"string" - end - a=Statsample::Vector.new([nil,1, 2 ,3 ,4 ,5], :scale) - b=Statsample::Vector.new([11, 12,nil,13,14,15], :scale) - assert_equal([nil,11,nil,10,10,10], (b-a).to_a) - assert_equal([nil,11,nil,10,10,10], (b-a.to_a).to_a) + a = Statsample::Vector.new([1, 2, 3, 4, 5], :scale) + b = Statsample::Vector.new([11, 12, 13, 14, 15], :scale) + assert_equal([-1, 0, 1, 2, 3], (a - 2).to_a) + assert_equal([10, 10, 10, 10, 10], (b - a).to_a) + assert_raise ArgumentError do + a - @c + end + assert_raise TypeError do + a - 'string' + end + a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :scale) + b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :scale) + assert_equal([nil, 11, nil, 10, 10, 10], (b - a).to_a) + assert_equal([nil, 11, nil, 10, 10, 10], (b - a.to_a).to_a) end + def test_sum_of_squares - a=[1,2,3,4,5,6].to_vector(:scale) + a = [1, 2, 3, 4, 5, 6].to_vector(:scale) assert_equal(17.5, a.sum_of_squared_deviation) end + def test_average_deviation - a=[1,2,3,4,5,6,7,8,9].to_scale + a = [1, 2, 3, 4, 5, 6, 7, 8, 9].to_scale assert_equal(20.quo(9), a.average_deviation_population) end + def test_samples srand(1) - assert_equal(100,@c.sample_with_replacement(100).size) + assert_equal(100, @c.sample_with_replacement(100).size) assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) - assert_raise ArgumentError do + assert_raise ArgumentError do @c.sample_without_replacement(20) end - @c.type=:scale + @c.type = :scale srand(1) assert_equal(100, @c.sample_with_replacement(100).size) assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) - end + def test_valid_data - a=Statsample::Vector.new([1,2,3,4,"STRING"]) - a.missing_values=[-99] - a.add(1,false) - a.add(2,false) - a.add(-99,false) + a = Statsample::Vector.new([1, 2, 3, 4, 'STRING']) + a.missing_values = [-99] + a.add(1, false) + a.add(2, false) + a.add(-99, false) a.set_valid_data - exp_valid_data=[1,2,3,4,"STRING",1,2] - assert_equal(exp_valid_data,a.valid_data) - a.add(20,false) - a.add(30,false) - assert_equal(exp_valid_data,a.valid_data) + exp_valid_data = [1, 2, 3, 4, 'STRING', 1, 2] + assert_equal(exp_valid_data, a.valid_data) + a.add(20, false) + a.add(30, false) + assert_equal(exp_valid_data, a.valid_data) a.set_valid_data - exp_valid_data_2=[1,2,3,4,"STRING",1,2,20,30] - assert_equal(exp_valid_data_2,a.valid_data) + exp_valid_data_2 = [1, 2, 3, 4, 'STRING', 1, 2, 20, 30] + assert_equal(exp_valid_data_2, a.valid_data) end + def test_set_value - @c[2]=10 - expected=[5,5,10,5,5,6,6,7,8,9,10,1,2,3,4,nil,-99,-99].to_vector - assert_equal(expected.data,@c.data) + @c[2] = 10 + expected = [5, 5, 10, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99].to_vector + assert_equal(expected.data, @c.data) end + def test_gsl if Statsample.has_gsl? - a=Statsample::Vector.new([1,2,3,4,"STRING"], :scale) + a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :scale) - assert_equal(2,a.mean) - assert_equal(a.variance_sample_ruby,a.variance_sample) - assert_equal(a.standard_deviation_sample_ruby,a.sds) - assert_equal(a.variance_population_ruby,a.variance_population) - assert_equal(a.standard_deviation_population_ruby,a.standard_deviation_population) + assert_equal(2, a.mean) + assert_equal(a.variance_sample_ruby, a.variance_sample) + assert_equal(a.standard_deviation_sample_ruby, a.sds) + assert_equal(a.variance_population_ruby, a.variance_population) + assert_equal(a.standard_deviation_population_ruby, a.standard_deviation_population) assert_nothing_raised do - a=[].to_vector(:scale) + a = [].to_vector(:scale) end - a.add(1,false) - a.add(2,false) + a.add(1, false) + a.add(2, false) a.set_valid_data - assert_equal(3,a.sum) - b=[1,2,nil,3,4,5,nil,6].to_vector(:scale) + assert_equal(3, a.sum) + b = [1, 2, nil, 3, 4, 5, nil, 6].to_vector(:scale) assert_equal(21, b.sum) assert_equal(3.5, b.mean) - assert_equal(6,b.gsl.size) - c=[10,20,30,40,50,100,1000,2000,5000].to_scale - assert_in_delta(c.skew, c.skew_ruby ,0.0001) - assert_in_delta(c.kurtosis, c.kurtosis_ruby ,0.0001) + assert_equal(6, b.gsl.size) + c = [10, 20, 30, 40, 50, 100, 1000, 2000, 5000].to_scale + assert_in_delta(c.skew, c.skew_ruby, 0.0001) + assert_in_delta(c.kurtosis, c.kurtosis_ruby, 0.0001) end end + def test_vector_matrix - v1=%w{a a a b b b c c}.to_vector - v2=%w{1 3 4 5 6 4 3 2}.to_vector - v3=%w{1 0 0 0 1 1 1 0}.to_vector - ex=Matrix.rows([["a", "1", "1"], ["a", "3", "0"], ["a", "4", "0"], ["b", "5", "0"], ["b", "6", "1"], ["b", "4", "1"], ["c", "3", "1"], ["c", "2", "0"]]) - assert_equal(ex,Statsample.vector_cols_matrix(v1,v2,v3)) + v1 = %w(a a a b b b c c).to_vector + v2 = %w(1 3 4 5 6 4 3 2).to_vector + v3 = %w(1 0 0 0 1 1 1 0).to_vector + ex = Matrix.rows([%w(a 1 1), %w(a 3 0), %w(a 4 0), %w(b 5 0), %w(b 6 1), %w(b 4 1), %w(c 3 1), %w(c 2 0)]) + assert_equal(ex, Statsample.vector_cols_matrix(v1, v2, v3)) end + def test_marshalling - v1=(0..100).to_a.collect{|n| rand(100)}.to_vector(:scale) - v2=Marshal.load(Marshal.dump(v1)) - assert_equal(v1,v2) + v1 = (0..100).to_a.collect { |_n| rand(100) }.to_vector(:scale) + v2 = Marshal.load(Marshal.dump(v1)) + assert_equal(v1, v2) end + def test_dup - v1=%w{a a a b b b c c}.to_vector - v2=v1.dup - assert_equal(v1.data,v2.data) - assert_not_same(v1.data,v2.data) - assert_equal(v1.type,v2.type) - - v1.type=:ordinal - assert_not_equal(v1.type,v2.type) - assert_equal(v1.missing_values,v2.missing_values) - assert_not_same(v1.missing_values,v2.missing_values) - assert_equal(v1.labels,v2.labels) - assert_not_same(v1.labels,v2.labels) - - v3=v1.dup_empty - assert_equal([],v3.data) - assert_not_equal(v1.data,v3.data) - assert_not_same(v1.data,v3.data) - assert_equal(v1.type,v3.type) - v1.type=:ordinal - v3.type=:nominal - assert_not_equal(v1.type,v3.type) - assert_equal(v1.missing_values,v3.missing_values) - assert_not_same(v1.missing_values,v3.missing_values) - assert_equal(v1.labels,v3.labels) - assert_not_same(v1.labels,v3.labels) + v1 = %w(a a a b b b c c).to_vector + v2 = v1.dup + assert_equal(v1.data, v2.data) + assert_not_same(v1.data, v2.data) + assert_equal(v1.type, v2.type) + + v1.type = :ordinal + assert_not_equal(v1.type, v2.type) + assert_equal(v1.missing_values, v2.missing_values) + assert_not_same(v1.missing_values, v2.missing_values) + assert_equal(v1.labels, v2.labels) + assert_not_same(v1.labels, v2.labels) + + v3 = v1.dup_empty + assert_equal([], v3.data) + assert_not_equal(v1.data, v3.data) + assert_not_same(v1.data, v3.data) + assert_equal(v1.type, v3.type) + v1.type = :ordinal + v3.type = :nominal + assert_not_equal(v1.type, v3.type) + assert_equal(v1.missing_values, v3.missing_values) + assert_not_same(v1.missing_values, v3.missing_values) + assert_equal(v1.labels, v3.labels) + assert_not_same(v1.labels, v3.labels) end + def test_paired_ties - a=[0,0,0,1,1,2,3,3,4,4,4].to_vector(:ordinal) - expected=[2,2,2,4.5,4.5,6,7.5,7.5,10,10,10].to_vector(:ordinal) - assert_equal(expected,a.ranked) + a = [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 4].to_vector(:ordinal) + expected = [2, 2, 2, 4.5, 4.5, 6, 7.5, 7.5, 10, 10, 10].to_vector(:ordinal) + assert_equal(expected, a.ranked) end + def test_dichotomize - a= [0,0,0,1,2,3,nil].to_vector - exp=[0,0,0,1,1,1,nil].to_scale - assert_equal(exp,a.dichotomize) - a= [1,1,1,2,2,2,3].to_vector - exp=[0,0,0,1,1,1,1].to_scale - assert_equal(exp,a.dichotomize) - a= [0,0,0,1,2,3,nil].to_vector - exp=[0,0,0,0,1,1,nil].to_scale - assert_equal(exp,a.dichotomize(1)) - a= %w{a a a b c d}.to_vector - exp=[0,0,0,1,1,1].to_scale + a = [0, 0, 0, 1, 2, 3, nil].to_vector + exp = [0, 0, 0, 1, 1, 1, nil].to_scale + assert_equal(exp, a.dichotomize) + a = [1, 1, 1, 2, 2, 2, 3].to_vector + exp = [0, 0, 0, 1, 1, 1, 1].to_scale + assert_equal(exp, a.dichotomize) + a = [0, 0, 0, 1, 2, 3, nil].to_vector + exp = [0, 0, 0, 0, 1, 1, nil].to_scale + assert_equal(exp, a.dichotomize(1)) + a = %w(a a a b c d).to_vector + exp = [0, 0, 0, 1, 1, 1].to_scale assert_equal(exp, a.dichotomize) end + def test_can_be_methods - a= [0,0,0,1,2,3,nil].to_vector + a = [0, 0, 0, 1, 2, 3, nil].to_vector assert(a.can_be_scale?) - a=[0,"s",0,1,2,3,nil].to_vector + a = [0, 's', 0, 1, 2, 3, nil].to_vector assert(!a.can_be_scale?) - a.missing_values=["s"] + a.missing_values = ['s'] assert(a.can_be_scale?) - a=[Date.new(2009,10,10), Date.today(), "2009-10-10", "2009-1-1", nil, "NOW"].to_vector + a = [Date.new(2009, 10, 10), Date.today, '2009-10-10', '2009-1-1', nil, 'NOW'].to_vector assert(a.can_be_date?) - a=[Date.new(2009,10,10), Date.today(),nil,"sss"].to_vector + a = [Date.new(2009, 10, 10), Date.today, nil, 'sss'].to_vector assert(!a.can_be_date?) end + def test_date_vector - a=[Date.new(2009,10,10), :NOW, "2009-10-10", "2009-1-1", nil, "NOW","MISSING"].to_vector(:date, :missing_values=>["MISSING"]) + a = [Date.new(2009, 10, 10), :NOW, '2009-10-10', '2009-1-1', nil, 'NOW', 'MISSING'].to_vector(:date, missing_values: ['MISSING']) - assert(a.type==:date) - expected=[Date.new(2009,10,10), Date.today(), Date.new(2009,10,10), Date.new(2009,1,1), nil, Date.today(), nil ] + assert(a.type == :date) + expected = [Date.new(2009, 10, 10), Date.today, Date.new(2009, 10, 10), Date.new(2009, 1, 1), nil, Date.today, nil] assert_equal(expected, a.date_data_with_nils) end end diff --git a/test/test_wilcoxonsignedrank.rb b/test/test_wilcoxonsignedrank.rb index faf115a..41dfa6c 100644 --- a/test/test_wilcoxonsignedrank.rb +++ b/test/test_wilcoxonsignedrank.rb @@ -1,67 +1,64 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleUMannWhitneyTestCase < Minitest::Test include Statsample::Test context Statsample::Test::WilcoxonSignedRank do - context "Example 1" do - setup do - @v1=[110,122,125,120,140,124,123,137,135,145].to_scale - @v2=[125,115,130,140,140,115,140,125,140,135].to_scale - @u=Statsample::Test::WilcoxonSignedRank.new(@v1,@v2) - end - should "have same result using class or Test#u_mannwhitney" do - assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1,@v2).w, @u.w) - end - should "have correct W values" do - assert_equal(9,@u.w) - end - should "have correct nr values" do - assert_equal(9,@u.nr) - end - should "have correct value for z" do - assert_in_delta(0.503,@u.z,0.001) - end - should "have correct value for probability_z" do - assert_in_delta(0.614,@u.probability_z,0.001) - end - should "have correct value for probability_exact" do - assert_in_delta(0.652,@u.probability_exact,0.001) - end - should "have summary" do - assert(@u.summary!="") - end - end - - context "Example 2" do - setup do - @v2=[78,24,64,45,64,52,30,50,64,50,78,22,84,40,90,72].to_scale - @v1=[78,24,62,48,68,56,25,44,56,40,68,36,68,20,58,32].to_scale - @u=Statsample::Test::WilcoxonSignedRank.new(@v1,@v2) - end - should "have same result using class or Test#u_mannwhitney" do - assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1,@v2).w, @u.w) - end - should "have correct W values" do - assert_equal(67,@u.w) - end - should "have correct nr values" do - assert_equal(14,@u.nr) - end - should "have correct value for z" do - assert_in_delta(2.087,@u.z,0.001) - end - should "have correct value for probability_z" do - assert_in_delta(0.036,@u.probability_z,0.001) - end - should "have correct value for probability_exact" do - assert_in_delta(0.036,@u.probability_exact,0.001) - end - should "have summary" do - assert(@u.summary!="") - end - end - - - end + context 'Example 1' do + setup do + @v1 = [110, 122, 125, 120, 140, 124, 123, 137, 135, 145].to_scale + @v2 = [125, 115, 130, 140, 140, 115, 140, 125, 140, 135].to_scale + @u = Statsample::Test::WilcoxonSignedRank.new(@v1, @v2) + end + should 'have same result using class or Test#u_mannwhitney' do + assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1, @v2).w, @u.w) + end + should 'have correct W values' do + assert_equal(9, @u.w) + end + should 'have correct nr values' do + assert_equal(9, @u.nr) + end + should 'have correct value for z' do + assert_in_delta(0.503, @u.z, 0.001) + end + should 'have correct value for probability_z' do + assert_in_delta(0.614, @u.probability_z, 0.001) + end + should 'have correct value for probability_exact' do + assert_in_delta(0.652, @u.probability_exact, 0.001) + end + should 'have summary' do + assert(@u.summary != '') + end + end + context 'Example 2' do + setup do + @v2 = [78, 24, 64, 45, 64, 52, 30, 50, 64, 50, 78, 22, 84, 40, 90, 72].to_scale + @v1 = [78, 24, 62, 48, 68, 56, 25, 44, 56, 40, 68, 36, 68, 20, 58, 32].to_scale + @u = Statsample::Test::WilcoxonSignedRank.new(@v1, @v2) + end + should 'have same result using class or Test#u_mannwhitney' do + assert_equal(Statsample::Test.wilcoxon_signed_rank(@v1, @v2).w, @u.w) + end + should 'have correct W values' do + assert_equal(67, @u.w) + end + should 'have correct nr values' do + assert_equal(14, @u.nr) + end + should 'have correct value for z' do + assert_in_delta(2.087, @u.z, 0.001) + end + should 'have correct value for probability_z' do + assert_in_delta(0.036, @u.probability_z, 0.001) + end + should 'have correct value for probability_exact' do + assert_in_delta(0.036, @u.probability_exact, 0.001) + end + should 'have summary' do + assert(@u.summary != '') + end + end + end end diff --git a/test/test_xls.rb b/test/test_xls.rb index bc6728e..4a5959d 100644 --- a/test/test_xls.rb +++ b/test/test_xls.rb @@ -1,51 +1,51 @@ -require(File.expand_path(File.dirname(__FILE__)+'/helpers_tests.rb')) +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleExcelTestCase < Minitest::Test - context "Excel reader" do + context 'Excel reader' do setup do - @ds=Statsample::Excel.read(File.dirname(__FILE__)+"/fixtures/test_xls.xls") + @ds = Statsample::Excel.read(File.dirname(__FILE__) + '/fixtures/test_xls.xls') end - should "set the number of cases" do - assert_equal(6,@ds.cases) + should 'set the number of cases' do + assert_equal(6, @ds.cases) end - should "set correct field names" do - assert_equal(%w{id name age city a1},@ds.fields) + should 'set correct field names' do + assert_equal(%w(id name age city a1), @ds.fields) end - should "set a dataset equal to expected" do - id=[1,2,3,4,5,6].to_vector(:scale) - name=["Alex","Claude","Peter","Franz","George","Fernand"].to_vector(:nominal) - age=[20,23,25,nil,5.5,nil].to_vector(:scale) - city=["New York","London","London","Paris","Tome",nil].to_vector(:nominal) - a1=["a,b","b,c","a",nil,"a,b,c",nil].to_vector(:nominal) - ds_exp=Statsample::Dataset.new({'id'=>id,'name'=>name,'age'=>age,'city'=>city,'a1'=>a1}, %w{id name age city a1}) + should 'set a dataset equal to expected' do + id = [1, 2, 3, 4, 5, 6].to_vector(:scale) + name = %w(Alex Claude Peter Franz George Fernand).to_vector(:nominal) + age = [20, 23, 25, nil, 5.5, nil].to_vector(:scale) + city = ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:nominal) + a1 = ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:nominal) + ds_exp = Statsample::Dataset.new({ 'id' => id, 'name' => name, 'age' => age, 'city' => city, 'a1' => a1 }, %w(id name age city a1)) ds_exp.fields.each{|f| - assert_equal(ds_exp[f],@ds[f]) + assert_equal(ds_exp[f], @ds[f]) } - assert_equal(ds_exp,@ds) + assert_equal(ds_exp, @ds) end - should "set to nil empty cells" do - assert_equal(nil,@ds['age'][5]) + should 'set to nil empty cells' do + assert_equal(nil, @ds['age'][5]) end end - context "Excel writer" do + context 'Excel writer' do setup do - a=100.times.map{rand(100)}.to_scale - b=(["b"]*100).to_vector - @ds={'b'=>b, 'a'=>a}.to_dataset(%w{b a}) - tempfile=Tempfile.new("test_write.xls") - Statsample::Excel.write(@ds,tempfile.path) - @ds2=Statsample::Excel.read(tempfile.path) + a = 100.times.map { rand(100) }.to_scale + b = (['b'] * 100).to_vector + @ds = { 'b' => b, 'a' => a }.to_dataset(%w(b a)) + tempfile = Tempfile.new('test_write.xls') + Statsample::Excel.write(@ds, tempfile.path) + @ds2 = Statsample::Excel.read(tempfile.path) end - should "return same fields as original" do - assert_equal(@ds.fields ,@ds2.fields) + should 'return same fields as original' do + assert_equal(@ds.fields, @ds2.fields) end - should "return same number of cases as original" do + should 'return same number of cases as original' do assert_equal(@ds.cases, @ds2.cases) end - should "return same cases as original" do - i=0 + should 'return same cases as original' do + i = 0 @ds2.each_array do |row| - assert_equal(@ds.case_as_array(i),row) - i+=1 + assert_equal(@ds.case_as_array(i), row) + i += 1 end end end From 61a0a677515d33d6076ae1b7094caa4567cc6dc1 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 11:57:43 -0300 Subject: [PATCH 017/119] Start working on 1.4.2 I also removed the web/ directory. I'll provide some means of updating examples & documentation asap. Change VERSION to 1.4.2 --- History.txt | 3 +++ lib/statsample/version.rb | 2 +- web/Rakefile | 39 --------------------------------------- 3 files changed, 4 insertions(+), 40 deletions(-) delete mode 100644 web/Rakefile diff --git a/History.txt b/History.txt index 6352f98..408c535 100644 --- a/History.txt +++ b/History.txt @@ -1,3 +1,6 @@ +=== 1.4.2 / 2015- + * + === 1.4.1 / 2015-03-26 * Removed Hoe gem in order to use `statsample.gemspec`. * Improved readability of some files by using rubocop. diff --git a/lib/statsample/version.rb b/lib/statsample/version.rb index ca7ab9a..902253e 100644 --- a/lib/statsample/version.rb +++ b/lib/statsample/version.rb @@ -1,3 +1,3 @@ module Statsample - VERSION = '1.4.1' + VERSION = '1.4.2' end diff --git a/web/Rakefile b/web/Rakefile deleted file mode 100644 index b2f4127..0000000 --- a/web/Rakefile +++ /dev/null @@ -1,39 +0,0 @@ -# -*- ruby -*- -require 'rake' -require 'fileutils' -directory "examples" - -def get_base(f) - f.sub(File.dirname(__FILE__)+"/../examples/","").gsub("/","_").gsub(".rb","") -end - - -EXAMPLES=Dir.glob(File.dirname(__FILE__)+"/../examples/**/*.rb").map {|v| [v, get_base(v)] -}.find_all{|v| !v[0].include?"_data"} - -EXAMPLES_BASE=EXAMPLES.map {|v| v[1]} - - -desc "Build all html, rtf and pdf files" -task :build_site do - ruby "build_site.rb" -end - - -task :clean do - Dir.glob(File.dirname(__FILE__)+"/examples/*.pdf").each do |t| - FileUtils.rm t - end - Dir.glob(File.dirname(__FILE__)+"/examples/*.html").each do |t| - FileUtils.rm t - end - Dir.glob(File.dirname(__FILE__)+"/examples/*.rtf").each do |t| - FileUtils.rm t - end - Dir.glob(File.dirname(__FILE__)+"/examples/images/*.*").each do |t| - FileUtils.rm t - end -end - - -load 'upload_task.rb' if File.exists? "upload_task.rb" From 25f8b3f79eba3014317c16d4d64b1187730c2d58 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Thu, 26 Mar 2015 12:05:42 -0300 Subject: [PATCH 018/119] Update link to documentation in rubydoc --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 935fc16..34a1b05 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ If you need to work on Structural Equation Modeling, you could see +statsample-s $ [sudo] gem install statsample-sem ``` +# Documentation + +You can see the latest documentation in [rubydoc.info](http://www.rubydoc.info/github/sciruby/statsample/master). + # Description A suite for basic and advanced statistics on Ruby. Tested on CRuby 1.9.3, 2.0.0 and 2.1.1. See `.travis.yml` for more information. From 2803ae9530c8a5e44490d02e110e3cba93a36fac Mon Sep 17 00:00:00 2001 From: Nicolas Leger Date: Fri, 27 Mar 2015 15:34:27 +0100 Subject: [PATCH 019/119] Test on Ruby 2.2 and auto test on patched versions --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4a0791..5f70a8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,10 @@ language: ruby rvm: - - '1.9.3' - - '2.0.0' - - '2.1.1' + - 1.9.3 + - 2.0 + - 2.1 + - 2.2 script: bundle exec rake test From 31753bd18019507523b8825d5ed4f5070f96009d Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 7 Apr 2015 11:09:49 -0300 Subject: [PATCH 020/119] Remove 1.9 check in converter/csv.rb Even Ruby 1.9 is EOL, thus no need to check for it. --- lib/statsample/converter/csv.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/statsample/converter/csv.rb b/lib/statsample/converter/csv.rb index e84442d..df16e0d 100644 --- a/lib/statsample/converter/csv.rb +++ b/lib/statsample/converter/csv.rb @@ -1,12 +1,7 @@ +require 'csv' + module Statsample class CSV < SpreadsheetBase - if RUBY_VERSION<"1.9" - require 'fastercsv' - CSV_klass=::FasterCSV - else - require 'csv' - CSV_klass=::CSV - end class << self def read19(filename,ignore_lines=0,csv_opts=Hash.new) @@ -31,7 +26,7 @@ def read19(filename,ignore_lines=0,csv_opts=Hash.new) # # USE: # ds=Statsample::CSV.read("test_csv.csv") - def read(filename, empty=[''],ignore_lines=0,csv_opts=Hash.new) + def read(filename, empty=[''],ignore_lines=0,csv_opts=Hash.new) first_row=true fields=[] #fields_data={} @@ -63,7 +58,7 @@ def read(filename, empty=[''],ignore_lines=0,csv_opts=Hash.new) # USE: # Statsample::CSV.write(ds,"test_csv.csv") def write(dataset,filename, convert_comma=false,*opts) - + writer=CSV_klass.open(filename,'w',*opts) writer << dataset.fields dataset.each_array do|row| From 426761f2f1c9ef86acfd2c6c08b4008cf03314cd Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 7 Apr 2015 11:53:04 -0300 Subject: [PATCH 021/119] Auto-correct stuff in converters & converters/csv.rb --- lib/statsample/converter/csv.rb | 65 ++++++++++++++++----------------- lib/statsample/converters.rb | 37 +++++++++---------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/lib/statsample/converter/csv.rb b/lib/statsample/converter/csv.rb index df16e0d..84bc6e5 100644 --- a/lib/statsample/converter/csv.rb +++ b/lib/statsample/converter/csv.rb @@ -3,68 +3,65 @@ module Statsample class CSV < SpreadsheetBase class << self - - def read19(filename,ignore_lines=0,csv_opts=Hash.new) - #default first line is header - csv_opts.merge!(:headers=>true, :header_converters => :symbol) - csv = CSV_klass::Table.new(CSV_klass::read(filename,'r',csv_opts)) + def read19(filename, ignore_lines = 0, csv_opts = {}) + # default first line is header + csv_opts.merge!(headers: true, header_converters: :symbol) + csv = CSV_klass::Table.new(CSV_klass.read(filename, 'r', csv_opts)) csv_headers = if csv_opts[:headers] - csv.headers - else - #as in R, if no header we name the headers as V1,V2,V3,V4,.. - 1.upto(csv.first.length).collect { |i| "V#{i}" } + csv.headers + else + # as in R, if no header we name the headers as V1,V2,V3,V4,.. + 1.upto(csv.first.length).collect { |i| "V#{i}" } end - #we invert row -> column. It means csv[0] is the first column and not row. Similar to R + # we invert row -> column. It means csv[0] is the first column and not row. Similar to R csv.by_col! thash = {} - csv_headers.each_with_index do |header,idx| + csv_headers.each_with_index do |header, idx| thash[header] = Statsample::Vector.new(csv[idx].drop(ignore_lines)) end Statsample::Dataset.new(thash) end + # Returns a Dataset based on a csv file # # USE: # ds=Statsample::CSV.read("test_csv.csv") - def read(filename, empty=[''],ignore_lines=0,csv_opts=Hash.new) - first_row=true - fields=[] - #fields_data={} - ds=nil - line_number=0 - csv=CSV_klass.open(filename,'rb', csv_opts) + def read(filename, empty = [''], ignore_lines = 0, csv_opts = {}) + first_row = true + fields = [] + ds = nil + line_number = 0 + csv = CSV_klass.open(filename, 'rb', csv_opts) csv.each do |row| - line_number+=1 - if(line_number<=ignore_lines) - #puts "Skip line" + line_number += 1 + if (line_number <= ignore_lines) + # puts "Skip line" next end - row.collect!{|c| c.to_s } + row.collect!(&:to_s) if first_row - fields=extract_fields(row) - ds=Statsample::Dataset.new(fields) - first_row=false + fields = extract_fields(row) + ds = Statsample::Dataset.new(fields) + first_row = false else - rowa=process_row(row,empty) - ds.add_case(rowa,false) + rowa = process_row(row, empty) + ds.add_case(rowa, false) end end - convert_to_scale_and_date(ds,fields) + convert_to_scale_and_date(ds, fields) ds.update_valid_data ds end + # Save a Dataset on a csv file # # USE: # Statsample::CSV.write(ds,"test_csv.csv") - def write(dataset,filename, convert_comma=false,*opts) - - writer=CSV_klass.open(filename,'w',*opts) + def write(dataset, filename, convert_comma = false, *opts) + writer = CSV_klass.open(filename, 'w', *opts) writer << dataset.fields dataset.each_array do|row| - if(convert_comma) - row.collect!{|v| v.to_s.gsub(".",",")} - end + row.collect! { |v| v.to_s.gsub('.', ',') } if convert_comma writer << row end writer.close diff --git a/lib/statsample/converters.rb b/lib/statsample/converters.rb index f5201ee..5f2bcd3 100644 --- a/lib/statsample/converters.rb +++ b/lib/statsample/converters.rb @@ -31,13 +31,13 @@ def read(dbh,query) # Insert each case of the Dataset on the selected table # # USE: - # + # # ds={'id'=>[1,2,3].to_vector, 'name'=>["a","b","c"].to_vector}.to_dataset # dbh = DBI.connect("DBI:Mysql:database:localhost", "user", "password") # Statsample::Database.insert(ds,dbh,"test") # def insert(ds, dbh, table) - require 'dbi' + require 'dbi' query="INSERT INTO #{table} ("+ds.fields.join(",")+") VALUES ("+((["?"]*ds.fields.size).join(","))+")" sth=dbh.prepare(query) ds.each_array{|c| sth.execute(*c) } @@ -46,11 +46,11 @@ def insert(ds, dbh, table) # Create a sql, basen on a given Dataset # # USE: - # + # # ds={'id'=>[1,2,3,4,5].to_vector,'name'=>%w{Alex Peter Susan Mary John}.to_vector}.to_dataset # Statsample::Database.create_sql(ds,'names') # ==>"CREATE TABLE names (id INTEGER,\n name VARCHAR (255)) CHARACTER SET=UTF8;" - # + # def create_sql(ds,table,charset="UTF8") sql="CREATE TABLE #{table} (" fields=ds.fields.collect{|f| @@ -81,14 +81,14 @@ def extract_fields(row) fields=row.to_a.collect{|c| if c.nil? i+=1 - "var%05d" % i + "var%05d" % i else c.to_s.downcase - end + end } fields.recode_repeated end - + def process_row(row,empty) row.to_a.map do |c| if empty.include?(c) @@ -115,7 +115,7 @@ def convert_to_scale_and_date(ds,fields) end end end - + end end class PlainText < SpreadsheetBase @@ -137,7 +137,7 @@ def read(filename, fields) end end end - class Excel < SpreadsheetBase + class Excel < SpreadsheetBase class << self # Write a Excel spreadsheet based on a dataset # * TODO: Format nicely date values @@ -177,7 +177,7 @@ def preprocess_row(row, dates) } end private :process_row, :preprocess_row - + # Returns a dataset based on a xls file # USE: # ds = Statsample::Excel.read("test.xls") @@ -186,20 +186,19 @@ def read(filename, opts=Hash.new) require 'spreadsheet' raise "options should be Hash" unless opts.is_a? Hash opts_default={ - :worksheet_id=>0, - :ignore_lines=>0, + :worksheet_id=>0, + :ignore_lines=>0, :empty=>[''] } - + opts=opts_default.merge opts - + worksheet_id=opts[:worksheet_id] ignore_lines=opts[:ignore_lines] empty=opts[:empty] - + first_row=true fields=[] - fields_data={} ds=nil line_number=0 book = Spreadsheet.open filename @@ -214,7 +213,7 @@ def read(filename, opts=Hash.new) } line_number+=1 next if(line_number<=ignore_lines) - + preprocess_row(row,dates) if first_row fields=extract_fields(row) @@ -296,7 +295,7 @@ def out(dataset,opt={}) variables_def=dataset.fields.collect{|k| variable_definition(carrier,dataset[k],k) }.join("\n") - + indexes=carrier.categorials.inject({}) {|s,c| s[dataset.fields.index(c)]=c s @@ -308,7 +307,7 @@ def out(dataset,opt={}) } records << "#{values_definition(c, default_opt[:missing])}\n" } - + out=< From 1270f3b5ebaf9c29ed559ddb0ed0213424184876 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 7 Apr 2015 12:30:29 -0300 Subject: [PATCH 022/119] Refactored Statsample::CSV.{read,write} methods - Removed the line converting everything to String (wtf). - Added a DEFAULT_OPTIONS constant. --- lib/statsample/converter/csv.rb | 55 +++++++++++++++------------------ 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/lib/statsample/converter/csv.rb b/lib/statsample/converter/csv.rb index 84bc6e5..b0ee2dd 100644 --- a/lib/statsample/converter/csv.rb +++ b/lib/statsample/converter/csv.rb @@ -2,43 +2,33 @@ module Statsample class CSV < SpreadsheetBase - class << self - def read19(filename, ignore_lines = 0, csv_opts = {}) - # default first line is header - csv_opts.merge!(headers: true, header_converters: :symbol) - csv = CSV_klass::Table.new(CSV_klass.read(filename, 'r', csv_opts)) - csv_headers = if csv_opts[:headers] - csv.headers - else - # as in R, if no header we name the headers as V1,V2,V3,V4,.. - 1.upto(csv.first.length).collect { |i| "V#{i}" } - end - # we invert row -> column. It means csv[0] is the first column and not row. Similar to R - csv.by_col! - thash = {} - csv_headers.each_with_index do |header, idx| - thash[header] = Statsample::Vector.new(csv[idx].drop(ignore_lines)) - end - Statsample::Dataset.new(thash) - end + # Default options for processing CSV files. Accept the same options as + # Ruby's `CSV#new`. + DEFAULT_OPTIONS = { + converters: [:numeric] + } - # Returns a Dataset based on a csv file + class << self + # Return a Dataset created from a csv file. # # USE: - # ds=Statsample::CSV.read("test_csv.csv") - def read(filename, empty = [''], ignore_lines = 0, csv_opts = {}) + # ds = Statsample::CSV.read('test_csv.csv') + def read(filename, empty = [''], ignore_lines = 0, opts = {}) first_row = true fields = [] ds = nil line_number = 0 - csv = CSV_klass.open(filename, 'rb', csv_opts) + options = DEFAULT_OPTIONS.merge(opts) + + csv = ::CSV.open(filename, 'rb', options) + csv.each do |row| line_number += 1 + if (line_number <= ignore_lines) - # puts "Skip line" next end - row.collect!(&:to_s) + if first_row fields = extract_fields(row) ds = Statsample::Dataset.new(fields) @@ -48,22 +38,27 @@ def read(filename, empty = [''], ignore_lines = 0, csv_opts = {}) ds.add_case(rowa, false) end end + convert_to_scale_and_date(ds, fields) ds.update_valid_data ds end - # Save a Dataset on a csv file + # Save a Dataset on a csv file. # # USE: - # Statsample::CSV.write(ds,"test_csv.csv") - def write(dataset, filename, convert_comma = false, *opts) - writer = CSV_klass.open(filename, 'w', *opts) + # Statsample::CSV.write(ds, 'test_csv.csv') + def write(dataset, filename, convert_comma = false, opts = {}) + options = DEFAULT_OPTIONS.merge(opts) + + writer = ::CSV.open(filename, 'w', options) writer << dataset.fields - dataset.each_array do|row| + + dataset.each_array do |row| row.collect! { |v| v.to_s.gsub('.', ',') } if convert_comma writer << row end + writer.close end end From 65beaac5d1186770c3a1fe8f45d54bbd76a45df3 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 7 Apr 2015 14:08:27 -0300 Subject: [PATCH 023/119] Add test for reading scientific notation in CSV files Fix #19 --- test/fixtures/scientific_notation.csv | 4 ++ test/helpers_tests.rb | 1 + test/test_csv.rb | 88 +++++++++++---------------- 3 files changed, 39 insertions(+), 54 deletions(-) create mode 100644 test/fixtures/scientific_notation.csv diff --git a/test/fixtures/scientific_notation.csv b/test/fixtures/scientific_notation.csv new file mode 100644 index 0000000..1c3bb09 --- /dev/null +++ b/test/fixtures/scientific_notation.csv @@ -0,0 +1,4 @@ +x,y +1,9.629587310436753e+127 +2,1.9341543147883677e+129 +3,3.88485279048245e+130 diff --git a/test/helpers_tests.rb b/test/helpers_tests.rb index 85cff59..83a40a0 100644 --- a/test/helpers_tests.rb +++ b/test/helpers_tests.rb @@ -1,5 +1,6 @@ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib/')) $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/')) + require 'minitest' require 'minitest/unit' require 'mocha/setup' diff --git a/test/test_csv.rb b/test/test_csv.rb index 9b2d832..86fd7cf 100644 --- a/test/test_csv.rb +++ b/test/test_csv.rb @@ -1,21 +1,29 @@ -require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) +require 'helpers_tests.rb' + class StatsampleCSVTestCase < Minitest::Test def setup - @ds = Statsample::CSV.read(File.dirname(__FILE__) + '/fixtures/test_csv.csv') + @ds = Statsample::CSV.read('test/fixtures/test_csv.csv') end def test_read + header = %w(id name age city a1) + data = { + 'id' => [1, 2, 3, 4, 5, 6].to_vector(:scale), + 'name' => %w(Alex Claude Peter Franz George Fernand).to_vector(:nominal), + 'age' => [20, 23, 25, 27, 5.5, nil].to_vector(:scale), + 'city' => ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:nominal), + 'a1' => ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:nominal) + } + + ds_exp = Statsample::Dataset.new(data, header) + assert_equal(6, @ds.cases) - assert_equal(%w(id name age city a1), @ds.fields) - id = [1, 2, 3, 4, 5, 6].to_vector(:scale) - name = %w(Alex Claude Peter Franz George Fernand).to_vector(:nominal) - age = [20, 23, 25, 27, 5.5, nil].to_vector(:scale) - city = ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:nominal) - a1 = ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:nominal) - ds_exp = Statsample::Dataset.new({ 'id' => id, 'name' => name, 'age' => age, 'city' => city, 'a1' => a1 }, %w(id name age city a1)) - ds_exp.fields.each{|f| + assert_equal(header, @ds.fields) + + ds_exp.fields.each do |f| assert_equal(ds_exp[f], @ds[f]) - } + end + assert_equal(ds_exp, @ds) end @@ -24,60 +32,32 @@ def test_nil end def test_repeated - ds = Statsample::CSV.read(File.dirname(__FILE__) + '/fixtures/repeated_fields.csv') + ds = Statsample::CSV.read('test/fixtures/repeated_fields.csv') assert_equal(%w(id name_1 age_1 city a1 name_2 age_2), ds.fields) age = [3, 4, 5, 6, nil, 8].to_vector(:scale) assert_equal(age, ds['age_2']) end + # Testing fix for SciRuby/statsample#19. + def test_accept_scientific_notation_as_float + ds = Statsample::CSV.read('test/fixtures/scientific_notation.csv') + assert_equal(%w(x y), ds.fields) + y = [9.629587310436753e+127, 1.9341543147883677e+129, 3.88485279048245e+130] + y.zip(ds['y']).each do |y_expected, y_ds| + assert_in_delta(y_expected, y_ds) + end + + end + def test_write filename = Tempfile.new('afile') - # filename=Dir::tmpdir+"/test_write.csv" Statsample::CSV.write(@ds, filename.path) ds2 = Statsample::CSV.read(filename.path) i = 0 - ds2.each_array{|row| + + ds2.each_array do |row| assert_equal(@ds.case_as_array(i), row) i += 1 - } + end end end -# class StatsampleCSVTestCase2 < Minitest::Test -# def setup -# @ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/test_csv.csv") -# end -# def test_read -# assert_equal(6,@ds.cases) -# assert_equal(%w{id name age city a1}, @ds.fields) -# id=[1,2,3,4,5,6].to_vector(:scale) -# name=["Alex","Claude","Peter","Franz","George","Fernand"].to_vector(:nominal) -# age=[20,23,25,27,5.5,nil].to_vector(:scale) -# city=["New York","London","London","Paris","Tome",nil].to_vector(:nominal) -# a1=["a,b","b,c","a",nil,"a,b,c",nil].to_vector(:nominal) -# ds_exp=Statsample::Dataset.new({'id'=>id,'name'=>name,'age'=>age,'city'=>city,'a1'=>a1}, %w{id name age city a1}) -# ds_exp.fields.each{|f| -# assert_equal(ds_exp[f],@ds[f]) -# } -# assert_equal(ds_exp,@ds) -# end -# def test_nil -# assert_equal(nil,@ds['age'][5]) -# end -# def test_repeated -# ds=Statsample::CSV.read19(File.dirname(__FILE__)+"/fixtures/repeated_fields.csv") -# assert_equal(%w{id name_1 age_1 city a1 name_2 age_2},ds.fields) -# age=[3,4,5,6,nil,8].to_vector(:scale) -# assert_equal(age,ds['age_2']) -# end -# def test_write -# filename=Tempfile.new("afile") -# # filename=Dir::tmpdir+"/test_write.csv" -# Statsample::CSV.write(@ds, filename.path) -# ds2=Statsample::CSV.read19(filename.path) -# i=0 -# ds2.each_array{|row| -# assert_equal(@ds.case_as_array(i),row) -# i+=1 -# } -# end -# end From f73e1570f93747052560438c85554fba68b03dc3 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Tue, 7 Apr 2015 14:23:47 -0300 Subject: [PATCH 024/119] Update History for 1.4.2 --- History.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/History.txt b/History.txt index 408c535..937b0c6 100644 --- a/History.txt +++ b/History.txt @@ -1,5 +1,6 @@ -=== 1.4.2 / 2015- - * +=== 1.4.2 / 2015-04-07 + * Statsample::CSV.read accepts numbers in scientific notation. + * Test on Ruby 2.2 via Travis CI. === 1.4.1 / 2015-03-26 * Removed Hoe gem in order to use `statsample.gemspec`. From 31bc86cd4539d20ddf304c794c67e0bd1d241a6b Mon Sep 17 00:00:00 2001 From: IsmailM Date: Mon, 13 Apr 2015 16:06:51 +0100 Subject: [PATCH 025/119] add versions to dependencies --- statsample.gemspec | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/statsample.gemspec b/statsample.gemspec index dd014e3..59d3b62 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -69,21 +69,21 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'spreadsheet', '~> 0.6.5' s.add_runtime_dependency 'reportbuilder', '~> 1.4' - s.add_runtime_dependency 'minimization' - s.add_runtime_dependency 'dirty-memoize' - s.add_runtime_dependency 'extendmatrix' - s.add_runtime_dependency 'rserve-client' + s.add_runtime_dependency 'minimization', '~> 0.2' + s.add_runtime_dependency 'dirty-memoize', '~> 0.0.4' + s.add_runtime_dependency 'extendmatrix', '~> 0.3' + s.add_runtime_dependency 'rserve-client', '~> 0.3' s.add_runtime_dependency 'rubyvis', '~> 0.5.0' - s.add_runtime_dependency 'distribution' - s.add_runtime_dependency 'rb-gsl' - s.add_runtime_dependency 'awesome_print' - - s.add_development_dependency 'bundler' - s.add_development_dependency 'rake' - s.add_development_dependency 'rdoc' - s.add_development_dependency 'shoulda' + s.add_runtime_dependency 'distribution', '~> 0.7' + s.add_runtime_dependency 'rb-gsl', '~> 1.16' + s.add_runtime_dependency 'awesome_print', '~> 1.6' + + s.add_development_dependency 'bundler', '~> 1.9' + s.add_development_dependency 'rake', '~> 10.4' + s.add_development_dependency 'rdoc', '~> 4.2' + s.add_development_dependency 'shoulda', '~> 3.5' s.add_development_dependency 'shoulda-matchers', '~> 2.2' - s.add_development_dependency 'minitest' - s.add_development_dependency 'gettext' - s.add_development_dependency 'mocha' + s.add_development_dependency 'minitest', '~> 5.5' + s.add_development_dependency 'gettext', '~> 3.1' + s.add_development_dependency 'mocha', '~> 1.1' end From 0fca9ffcb225e87a4b27e61c53b8e24de564b1e0 Mon Sep 17 00:00:00 2001 From: IsmailM Date: Mon, 13 Apr 2015 16:48:48 +0100 Subject: [PATCH 026/119] change bundler dependency version from 1.9 to 1.7 --- statsample.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statsample.gemspec b/statsample.gemspec index 59d3b62..fc3cf4e 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -78,7 +78,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rb-gsl', '~> 1.16' s.add_runtime_dependency 'awesome_print', '~> 1.6' - s.add_development_dependency 'bundler', '~> 1.9' + s.add_development_dependency 'bundler', '~> 1.7' s.add_development_dependency 'rake', '~> 10.4' s.add_development_dependency 'rdoc', '~> 4.2' s.add_development_dependency 'shoulda', '~> 3.5' From 63280a31849bb6320eecda04445cc43d34932ae8 Mon Sep 17 00:00:00 2001 From: Carlos Agarie Date: Mon, 27 Apr 2015 12:16:00 -0300 Subject: [PATCH 027/119] Remove rb-gsl dependency. Upgrade to 1.4.3. --- History.txt | 3 +++ lib/statsample/version.rb | 2 +- statsample.gemspec | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/History.txt b/History.txt index 937b0c6..76a2b7a 100644 --- a/History.txt +++ b/History.txt @@ -1,3 +1,6 @@ +=== 1.4.3 / 2015-04-27 + * Removed rb-gsl dependency. + === 1.4.2 / 2015-04-07 * Statsample::CSV.read accepts numbers in scientific notation. * Test on Ruby 2.2 via Travis CI. diff --git a/lib/statsample/version.rb b/lib/statsample/version.rb index 902253e..39db528 100644 --- a/lib/statsample/version.rb +++ b/lib/statsample/version.rb @@ -1,3 +1,3 @@ module Statsample - VERSION = '1.4.2' + VERSION = '1.4.3' end diff --git a/statsample.gemspec b/statsample.gemspec index fc3cf4e..91a50d4 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -75,7 +75,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rserve-client', '~> 0.3' s.add_runtime_dependency 'rubyvis', '~> 0.5.0' s.add_runtime_dependency 'distribution', '~> 0.7' - s.add_runtime_dependency 'rb-gsl', '~> 1.16' s.add_runtime_dependency 'awesome_print', '~> 1.6' s.add_development_dependency 'bundler', '~> 1.7' From d9860e3855a4c03062aba2bfdbc6d391e205e681 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 7 May 2015 19:15:07 +0530 Subject: [PATCH 028/119] changed stuff to ensure proper working with or without GSL. --- lib/statsample.rb | 2 +- lib/statsample/matrix.rb | 18 +++-- lib/statsample/test/bartlettsphericity.rb | 2 +- statsample.gemspec | 1 + test/test_factor.rb | 82 ++++++++++++----------- 5 files changed, 59 insertions(+), 46 deletions(-) diff --git a/lib/statsample.rb b/lib/statsample.rb index 6ebdd7f..53b44a5 100644 --- a/lib/statsample.rb +++ b/lib/statsample.rb @@ -141,7 +141,7 @@ def self.create_has_library(library) class_variable_get(cv) end end - + create_has_library :gsl SPLIT_TOKEN = ',' diff --git a/lib/statsample/matrix.rb b/lib/statsample/matrix.rb index e7ad8b3..886ed20 100644 --- a/lib/statsample/matrix.rb +++ b/lib/statsample/matrix.rb @@ -10,6 +10,7 @@ class ::Matrix def to_matrix self end + def to_dataset f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| _("VAR_%d") % (i+1) } ds=Statsample::Dataset.new(f) @@ -24,6 +25,7 @@ def to_dataset ds.name=self.name if self.respond_to? :name ds end + if defined? :eigenpairs alias_method :eigenpairs_ruby, :eigenpairs end @@ -38,17 +40,15 @@ def eigenpairs def eigenvalues eigenpairs.collect {|v| v[0]} end + def eigenvectors eigenpairs.collect {|v| v[1]} end + def eigenvectors_matrix Matrix.columns(eigenvectors) end - - - - def to_gsl out=[] self.row_size.times{|i| @@ -64,9 +64,11 @@ class Col def to_matrix ::Matrix.columns([self.size.times.map {|i| self[i]}]) end + def to_ary to_a end + def to_gsl self end @@ -95,18 +97,23 @@ def to_dataset def row_size size1 end + def column_size size2 end + def determinant det end + def inverse GSL::Linalg::LU.invert(self) end + def eigenvalues eigenpairs.collect {|v| v[0]} end + def eigenvectors eigenpairs.collect {|v| v[1]} end @@ -123,6 +130,7 @@ def eigenvectors_matrix GSL::Eigen::symmv_sort(eigval, eigvec, GSL::Eigen::SORT_VAL_DESC) eigvec end + def eigenpairs eigval, eigvec= GSL::Eigen.symmv(self) GSL::Eigen::symmv_sort(eigval, eigvec, GSL::Eigen::SORT_VAL_DESC) @@ -137,12 +145,14 @@ def eigenpairs def square? size1==size2 end + def to_matrix rows=self.size1 cols=self.size2 out=(0...rows).collect{|i| (0...cols).collect {|j| self[i,j]} } ::Matrix.rows(out) end + def total_sum sum=0 size1.times {|i| diff --git a/lib/statsample/test/bartlettsphericity.rb b/lib/statsample/test/bartlettsphericity.rb index 98b6676..b05ed02 100644 --- a/lib/statsample/test/bartlettsphericity.rb +++ b/lib/statsample/test/bartlettsphericity.rb @@ -31,7 +31,7 @@ def initialize(matrix,ncases) # def compute @value=-((@ncases-1)-(2*@nvars+5).quo(6))*Math::log(@matrix.determinant) - @df=(@nvars*(@nvars-1)).quo(2) + @df=(@nvars*(@nvars-1)) / 2 end def probability 1-Distribution::ChiSquare.cdf(@value,@df) diff --git a/statsample.gemspec b/statsample.gemspec index 91a50d4..8030c72 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -85,4 +85,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.5' s.add_development_dependency 'gettext', '~> 3.1' s.add_development_dependency 'mocha', '~> 1.1' + # s.add_development_dependency 'gsl-nmatrix', '~> 1.17' end diff --git a/test/test_factor.rb b/test/test_factor.rb index 8ca7990..d356224 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -34,51 +34,53 @@ def test_covariance_matrix end def test_principalcomponents_ruby_gsl - ran = Distribution::Normal.rng - - # @r=::Rserve::Connection.new - - samples = 20 - [3, 5, 7].each {|k| - v = {} - v['x0'] = samples.times.map { ran.call }.to_scale.centered - (1...k).each {|i| - v["x#{i}"] = samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}"][ii] * 0.5 }.to_scale.centered - } - - ds = v.to_dataset - cm = ds.covariance_matrix - # @r.assign('ds',ds) - # @r.eval('cm<-cor(ds);sm<-eigen(cm, sym=TRUE);v<-sm$vectors') - # puts "eigenvalues" - # puts @r.eval('v').to_ruby.to_s - pca_ruby = Statsample::Factor::PCA.new(cm, m: k, use_gsl: false) - pca_gsl = Statsample::Factor::PCA.new(cm, m: k, use_gsl: true) - pc_ruby = pca_ruby.principal_components(ds) - pc_gsl = pca_gsl.principal_components(ds) - # Test component matrix correlation! - cm_ruby = pca_ruby.component_matrix - # puts cm_ruby.summary - k.times {|i| - pc_id = "PC_#{i + 1}" - assert_in_delta(pca_ruby.eigenvalues[i], pca_gsl.eigenvalues[i], 1e-10) - # Revert gsl component values - pc_gsl_data = (pc_gsl[pc_id][0] - pc_ruby[pc_id][0]).abs > 1e-6 ? pc_gsl[pc_id].recode(&:-@) : pc_gsl[pc_id] - assert_similar_vector(pc_gsl_data, pc_ruby[pc_id], 1e-6, "PC for #{k} variables") - if false - k.times {|j| # variable - ds_id = "x#{j}" - r = Statsample::Bivariate.correlation(ds[ds_id], pc_ruby[pc_id]) - puts "#{pc_id}-#{ds_id}:#{r}" - } - end + if Statsample.has_gsl? + ran = Distribution::Normal.rng + + # @r=::Rserve::Connection.new + + samples = 20 + [3, 5, 7].each {|k| + v = {} + v['x0'] = samples.times.map { ran.call }.to_scale.centered + (1...k).each {|i| + v["x#{i}"] = samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}"][ii] * 0.5 }.to_scale.centered + } + + ds = v.to_dataset + cm = ds.covariance_matrix + # @r.assign('ds',ds) + # @r.eval('cm<-cor(ds);sm<-eigen(cm, sym=TRUE);v<-sm$vectors') + # puts "eigenvalues" + # puts @r.eval('v').to_ruby.to_s + pca_ruby = Statsample::Factor::PCA.new(cm, m: k, use_gsl: false) + pca_gsl = Statsample::Factor::PCA.new(cm, m: k, use_gsl: true) + pc_ruby = pca_ruby.principal_components(ds) + pc_gsl = pca_gsl.principal_components(ds) + # Test component matrix correlation! + cm_ruby = pca_ruby.component_matrix + # puts cm_ruby.summary + k.times {|i| + pc_id = "PC_#{i + 1}" + assert_in_delta(pca_ruby.eigenvalues[i], pca_gsl.eigenvalues[i], 1e-10) + # Revert gsl component values + pc_gsl_data = (pc_gsl[pc_id][0] - pc_ruby[pc_id][0]).abs > 1e-6 ? pc_gsl[pc_id].recode(&:-@) : pc_gsl[pc_id] + assert_similar_vector(pc_gsl_data, pc_ruby[pc_id], 1e-6, "PC for #{k} variables") + if false + k.times {|j| # variable + ds_id = "x#{j}" + r = Statsample::Bivariate.correlation(ds[ds_id], pc_ruby[pc_id]) + puts "#{pc_id}-#{ds_id}:#{r}" + } + end + } } - } + end # @r.close end def test_principalcomponents - principalcomponents(true) + principalcomponents(true) if Statsample.has_gsl? principalcomponents(false) end From 14a641bb160a8bbcef611c7795b970d46420fa52 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Fri, 8 May 2015 11:38:13 +0530 Subject: [PATCH 029/119] monkey patch Array to make up for missing rb-gsl methods --- lib/statsample.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/statsample.rb b/lib/statsample.rb index 53b44a5..7ebe937 100644 --- a/lib/statsample.rb +++ b/lib/statsample.rb @@ -87,6 +87,32 @@ def recode_repeated self end end + + def sum + inject(:+) + end + + def mean + self.sum / size + end + + # Calcualte sum of squares + def sum_of_squares(m=nil) + m ||= mean + self.inject(0) {|a,x| a + (x-m).square } + end + + # Calculate sample variance + def variance_sample(m=nil) + m ||= mean + sum_of_squares(m).quo(size - 1) + end + + # Calculate sample standard deviation + def sd + m ||= mean + Math::sqrt(variance_sample(m)) + end end def create_test(*args, &_proc) From 38bc8950c9648ffcce870eb58dabaed55ecbc537 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Fri, 8 May 2015 12:30:40 +0530 Subject: [PATCH 030/119] reduce delta to accomodate for non-gsl calc reduce delta to accomodate for non-gsl calc add delta parameter to accomodate for non-gsl calc reduce delta amend --- test/test_reliability_icc.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_reliability_icc.rb b/test/test_reliability_icc.rb index 7148534..3fe0991 100644 --- a/test/test_reliability_icc.rb +++ b/test/test_reliability_icc.rb @@ -182,8 +182,8 @@ class StatsampleReliabilityIccTestCase < Minitest::Test should 'bounds be equal' do @icc.type = t @r_icc = @iccs[t.to_s] - assert_in_delta(@r_icc['lbound'], @icc.lbound) - assert_in_delta(@r_icc['ubound'], @icc.ubound) + assert_in_delta(@r_icc['lbound'], @icc.lbound, 0.1) + assert_in_delta(@r_icc['ubound'], @icc.ubound, 0.1) end should 'summary generated' do assert(@icc.summary.size > 0) From 84b9b71777f501598de612876927a8c1a168f40b Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Fri, 8 May 2015 23:01:08 +0530 Subject: [PATCH 031/119] Changes to make sure statsample works with gsl-nmatrix. Will work smoothly with rb-gsl too. --- lib/statsample/factor.rb | 6 ++---- lib/statsample/factor/map.rb | 3 ++- lib/statsample/matrix.rb | 4 ++++ statsample.gemspec | 4 ++-- test/test_factor.rb | 4 ++++ 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/statsample/factor.rb b/lib/statsample/factor.rb index 686b8b2..ac99348 100644 --- a/lib/statsample/factor.rb +++ b/lib/statsample/factor.rb @@ -34,7 +34,7 @@ module Factor # matrix is not appropriate for factor analysis." # def self.anti_image_covariance_matrix(matrix) - s2=Matrix.diag(*(matrix.inverse.diagonal)).inverse + s2=Matrix.diagonal(*(matrix.inverse.diagonal)).inverse aicm=(s2)*matrix.inverse*(s2) aicm.extend(Statsample::CovariateMatrix) aicm.fields=matrix.fields if matrix.respond_to? :fields @@ -42,13 +42,12 @@ def self.anti_image_covariance_matrix(matrix) end def self.anti_image_correlation_matrix(matrix) matrix=matrix.to_matrix - s=Matrix.diag(*(matrix.inverse.diagonal)).sqrt.inverse + s=Matrix.diagonal(*(matrix.inverse.diagonal)).sqrt.inverse aicm=s*matrix.inverse*s aicm.extend(Statsample::CovariateMatrix) aicm.fields=matrix.fields if matrix.respond_to? :fields aicm - end # Kaiser-Meyer-Olkin measure of sampling adequacy for correlation matrix. @@ -101,6 +100,5 @@ def self.kmo_univariate(matrix, var) end sum_r.quo(sum_r+sum_q) end - end end diff --git a/lib/statsample/factor/map.rb b/lib/statsample/factor/map.rb index 963763a..26ac880 100644 --- a/lib/statsample/factor/map.rb +++ b/lib/statsample/factor/map.rb @@ -75,7 +75,8 @@ def compute (ncol-1).times do |m| puts "MAP:Eigenvalue #{m+1}" if $DEBUG - a=loadings[0..(loadings.row_size-1),0..m] + a=use_gsl ? loadings[0..(loadings.row_size-1),0..m] : + loadings.minor(0..(loadings.row_size-1),0..m) partcov= gsl_m - (a*a.transpose) d=klass_m.diagonal(*(partcov.diagonal.collect {|v| Math::sqrt(1/v)})) diff --git a/lib/statsample/matrix.rb b/lib/statsample/matrix.rb index 886ed20..79e0303 100644 --- a/lib/statsample/matrix.rb +++ b/lib/statsample/matrix.rb @@ -56,6 +56,10 @@ def to_gsl } GSL::Matrix[*out] end + + def []=(i, j, x) + @rows[i][j] = x + end end module GSL diff --git a/statsample.gemspec b/statsample.gemspec index 8030c72..c8e74c8 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -71,7 +71,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'reportbuilder', '~> 1.4' s.add_runtime_dependency 'minimization', '~> 0.2' s.add_runtime_dependency 'dirty-memoize', '~> 0.0.4' - s.add_runtime_dependency 'extendmatrix', '~> 0.3' + s.add_runtime_dependency 'extendmatrix', '~> 0.4' s.add_runtime_dependency 'rserve-client', '~> 0.3' s.add_runtime_dependency 'rubyvis', '~> 0.5.0' s.add_runtime_dependency 'distribution', '~> 0.7' @@ -85,5 +85,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.5' s.add_development_dependency 'gettext', '~> 3.1' s.add_development_dependency 'mocha', '~> 1.1' - # s.add_development_dependency 'gsl-nmatrix', '~> 1.17' + s.add_development_dependency 'gsl-nmatrix', '~> 1.17' end diff --git a/test/test_factor.rb b/test/test_factor.rb index d356224..5e0b697 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -141,11 +141,13 @@ def test_kmo_univariate end # Tested with SPSS and R def test_pca + a = [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_scale b = [2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9].to_scale a.recode! { |c| c - a.mean } b.recode! { |c| c - b.mean } ds = { 'a' => a, 'b' => b }.to_dataset + cov_matrix = Statsample::Bivariate.covariance_matrix(ds) if Statsample.has_gsl? pca = Statsample::Factor::PCA.new(cov_matrix, use_gsl: true) @@ -158,6 +160,8 @@ def test_pca end def pca_set(pca, _type) + + expected_eigenvalues = [1.284, 0.0490] expected_eigenvalues.each_with_index{|ev, i| assert_in_delta(ev, pca.eigenvalues[i], 0.001) From 40e3b46490cdb0343cd26a4fb24fedd33c98c007 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 9 May 2015 00:55:57 +0530 Subject: [PATCH 032/119] reverted dependency versions --- statsample.gemspec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/statsample.gemspec b/statsample.gemspec index c8e74c8..91a50d4 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -71,7 +71,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'reportbuilder', '~> 1.4' s.add_runtime_dependency 'minimization', '~> 0.2' s.add_runtime_dependency 'dirty-memoize', '~> 0.0.4' - s.add_runtime_dependency 'extendmatrix', '~> 0.4' + s.add_runtime_dependency 'extendmatrix', '~> 0.3' s.add_runtime_dependency 'rserve-client', '~> 0.3' s.add_runtime_dependency 'rubyvis', '~> 0.5.0' s.add_runtime_dependency 'distribution', '~> 0.7' @@ -85,5 +85,4 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.5' s.add_development_dependency 'gettext', '~> 3.1' s.add_development_dependency 'mocha', '~> 1.1' - s.add_development_dependency 'gsl-nmatrix', '~> 1.17' end From 02fc1a3992e80fd687332e4aa66381430f004839 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Wed, 13 May 2015 11:22:53 +0530 Subject: [PATCH 033/119] wrote tests for new data types API --- History.txt | 4 ++++ test/test_vector.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/History.txt b/History.txt index 76a2b7a..e96409c 100644 --- a/History.txt +++ b/History.txt @@ -1,3 +1,7 @@ +=== 1.5.0 + * Made sure all methods work properly with and without GSL. + * Now works with either rb-gsl or gsl-nmatrix. + === 1.4.3 / 2015-04-27 * Removed rb-gsl dependency. diff --git a/test/test_vector.rb b/test/test_vector.rb index b73af37..5e25050 100644 --- a/test/test_vector.rb +++ b/test/test_vector.rb @@ -134,6 +134,48 @@ def assert_counting_tokens(b) end end + context "new types :numeric and :object" do + should "set default type of vector to :object" do + v = Statsample::Vector.new [1,2,3,4,5] + assert_equal(:object, v.type) + end + + should "initialize Vector with :numeric type" do + v = Statsample::Vector.new [1,2,3,4,5,nil], :numeric + assert_equal(:numeric, v.type) + assert_equal([1,2,3,4,5], v.valid_data) + end + + should "show a warning when initializing with :scale, :ordinal or :nominal" do + assert_output("WARNING: nominal has been deprecated. Use :object instead.") do + Statsample::Vector.new [1,2,3,4,5,nil,'hello'], :nominal + end + + assert_output("WARNING: scale has been deprecated. Use :numeric instead.") do + Statsample::Vector.new [1,2,3,4,nil,5], :scale + end + + assert_output("WARNING: ordinal has been deprecated. Use :numeric instead.") do + Statsample::Vector.new [1,2,3,4,5], :ordinal + end + end + + should "test that new shorthands work" do + numeric = Statsample::Vector.new([1,2,3,4,nil,5], :numeric) + assert_equal(numeric, [1,2,3,4,nil,5].to_numeric) + assert_equal(numeric, [1,2,3,4,nil,5].to_vector(:numeric)) + + obj = Statsample::Vector.new([1,2,3,4,'one','two'], :object) + assert_equal(obj, [1,2,3,4,'one','two'].to_vector(:object)) + end + + should "test that old shorthands raise warnings" do + assert_output("WARNING: to_scale has been deprecated. Use to_numeric instead.") do + [1,2,3,4,nil,5].to_scale + end + end + end + context '#split_by_separator' do setup do @a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 10, nil], :nominal) From 5c9090af79755fbd2fb227155fae23d31997f9cd Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Wed, 13 May 2015 11:42:17 +0530 Subject: [PATCH 034/119] Changed statsamples treatement of data types from scale nominal and ordinal to :numeric and :object. Simplified data type model will allow for more extensibility and will ease integration with daru. Older methods and data type options are still there but internally convert to the new data types and print a warning to STDOUT that they will be deprecated soon. preparing for overhaul wip wip done in vector.rb done --- History.txt | 1 + benchmarks/correlation_matrix_15_variables.rb | 2 +- benchmarks/correlation_matrix_5_variables.rb | 2 +- .../correlation_matrix.rb | 4 +- examples/dataset.rb | 4 +- examples/icc.rb | 2 +- examples/levene.rb | 4 +- examples/parallel_analysis.rb | 2 +- examples/u_test.rb | 4 +- examples/vector.rb | 2 +- examples/velicer_map_test.rb | 2 +- lib/statsample.rb | 6 +- lib/statsample/anova/oneway.rb | 6 +- lib/statsample/anova/twoway.rb | 6 +- lib/statsample/bivariate.rb | 14 +- lib/statsample/bivariate/pearson.rb | 4 +- lib/statsample/converter/csv.rb | 2 +- lib/statsample/converters.rb | 14 +- lib/statsample/dataset.rb | 16 +- lib/statsample/dominanceanalysis.rb | 8 +- lib/statsample/dominanceanalysis/bootstrap.rb | 16 +- lib/statsample/factor/parallelanalysis.rb | 4 +- lib/statsample/factor/pca.rb | 4 +- lib/statsample/factor/principalaxis.rb | 4 +- lib/statsample/graph/boxplot.rb | 8 +- lib/statsample/graph/histogram.rb | 4 +- lib/statsample/graph/scatterplot.rb | 8 +- lib/statsample/matrix.rb | 4 +- lib/statsample/regression.rb | 4 +- lib/statsample/regression/multiple.rb | 6 +- .../regression/multiple/alglibengine.rb | 10 +- .../regression/multiple/baseengine.rb | 6 +- .../regression/multiple/gslengine.rb | 10 +- .../regression/multiple/rubyengine.rb | 8 +- lib/statsample/reliability/icc.rb | 2 +- .../reliability/multiscaleanalysis.rb | 8 +- lib/statsample/reliability/scaleanalysis.rb | 12 +- .../reliability/skillscaleanalysis.rb | 2 +- lib/statsample/resample.rb | 2 +- lib/statsample/shorthand.rb | 2 +- lib/statsample/test/levene.rb | 8 +- lib/statsample/test/t.rb | 6 +- lib/statsample/test/umannwhitney.rb | 4 +- lib/statsample/vector.rb | 183 +++++++------ lib/statsample/vector/gsl.rb | 32 +-- test/test_analysis.rb | 2 +- test/test_anova_contrast.rb | 8 +- test/test_anovatwowaywithdataset.rb | 2 +- test/test_anovawithvectors.rb | 12 +- test/test_awesome_print_bug.rb | 2 +- test/test_bartlettsphericity.rb | 6 +- test/test_bivariate.rb | 76 +++--- test/test_crosstab.rb | 4 +- test/test_csv.rb | 12 +- test/test_dataset.rb | 158 ++++++------ test/test_factor.rb | 22 +- test/test_factor_pa.rb | 8 +- test/test_ggobi.rb | 6 +- test/test_gsl.rb | 6 +- test/test_histogram.rb | 6 +- test/test_matrix.rb | 10 +- test/test_multiset.rb | 38 +-- test/test_regression.rb | 54 ++-- test/test_reliability.rb | 28 +- test/test_reliability_icc.rb | 10 +- test/test_reliability_skillscale.rb | 12 +- test/test_resample.rb | 2 +- test/test_rserve_extension.rb | 8 +- test/test_statistics.rb | 10 +- test/test_stest.rb | 16 +- test/test_stratified.rb | 6 +- test/test_test_t.rb | 10 +- test/test_umannwhitney.rb | 4 +- test/test_vector.rb | 240 +++++++++--------- test/test_wilcoxonsignedrank.rb | 8 +- test/test_xls.rb | 12 +- 76 files changed, 638 insertions(+), 622 deletions(-) diff --git a/History.txt b/History.txt index e96409c..7d558da 100644 --- a/History.txt +++ b/History.txt @@ -1,6 +1,7 @@ === 1.5.0 * Made sure all methods work properly with and without GSL. * Now works with either rb-gsl or gsl-nmatrix. + * Changed the data types of Statsample::Vector from :ordinal, :scale and :nominal to only :numeric and :object. :numeric replaces :ordinal/:scale and :object replaces :nominal. Older methods for creating the older data types still exist, but throw a warning prodding the user to use the new methods. === 1.4.3 / 2015-04-27 * Removed rb-gsl dependency. diff --git a/benchmarks/correlation_matrix_15_variables.rb b/benchmarks/correlation_matrix_15_variables.rb index 82f56eb..d998bdc 100644 --- a/benchmarks/correlation_matrix_15_variables.rb +++ b/benchmarks/correlation_matrix_15_variables.rb @@ -18,7 +18,7 @@ reps 200 #number of repetitions ds=vars.times.inject({}) {|ac,v| -ac["x#{v}"]=Statsample::Vector.new_scale(cases) {rand()} +ac["x#{v}"]=Statsample::Vector.new_numeric(cases) {rand()} ac }.to_dataset diff --git a/benchmarks/correlation_matrix_5_variables.rb b/benchmarks/correlation_matrix_5_variables.rb index e84f25c..83d4bce 100644 --- a/benchmarks/correlation_matrix_5_variables.rb +++ b/benchmarks/correlation_matrix_5_variables.rb @@ -19,7 +19,7 @@ ds=vars.times.inject({}) {|ac,v| -ac["x#{v}"]=Statsample::Vector.new_scale(cases) {rand()} +ac["x#{v}"]=Statsample::Vector.new_numeric(cases) {rand()} ac }.to_dataset diff --git a/benchmarks/correlation_matrix_methods/correlation_matrix.rb b/benchmarks/correlation_matrix_methods/correlation_matrix.rb index 4f5f842..acbc187 100644 --- a/benchmarks/correlation_matrix_methods/correlation_matrix.rb +++ b/benchmarks/correlation_matrix_methods/correlation_matrix.rb @@ -7,7 +7,7 @@ def create_dataset(vars,cases) ran=Distribution::Normal.rng ds=vars.times.inject({}) {|ac,v| - ac["x#{v}"]=Statsample::Vector.new_scale(cases) {ran.call} + ac["x#{v}"]=Statsample::Vector.new_numeric(cases) {ran.call} ac }.to_dataset end @@ -56,7 +56,7 @@ def prediction_optimized(vars,cases) end -rs.fields.each {|f| rs[f].type=:scale} +rs.fields.each {|f| rs[f].type=:numeric} rs['c_v']=rs.collect {|row| row['cases']*row['vars']} diff --git a/examples/dataset.rb b/examples/dataset.rb index b993ddc..cc319be 100644 --- a/examples/dataset.rb +++ b/examples/dataset.rb @@ -4,8 +4,8 @@ Statsample::Analysis.store(Statsample::Dataset) do samples=1000 - a=Statsample::Vector.new_scale(samples) {r=rand(5); r==4 ? nil: r} - b=Statsample::Vector.new_scale(samples) {r=rand(5); r==4 ? nil: r} + a=Statsample::Vector.new_numeric(samples) {r=rand(5); r==4 ? nil: r} + b=Statsample::Vector.new_numeric(samples) {r=rand(5); r==4 ? nil: r} ds={'a'=>a,'b'=>b}.to_dataset summary(ds) diff --git a/examples/icc.rb b/examples/icc.rb index b563ae4..694800c 100644 --- a/examples/icc.rb +++ b/examples/icc.rb @@ -6,7 +6,7 @@ Statsample::Analysis.store(Statsample::Reliability::ICC) do size=1000 - a=Statsample::Vector.new_scale(size) {rand(10)} + a=Statsample::Vector.new_numeric(size) {rand(10)} b=a.recode{|i|i+rand(4)-2} c=a.recode{|i|i+rand(4)-2} d=a.recode{|i|i+rand(4)-2} diff --git a/examples/levene.rb b/examples/levene.rb index 8529ee2..df8440d 100644 --- a/examples/levene.rb +++ b/examples/levene.rb @@ -5,8 +5,8 @@ Statsample::Analysis.store(Statsample::Test::Levene) do - a=[1,2,3,4,5,6,7,8,100,10].to_scale - b=[30,40,50,60,70,80,90,100,110,120].to_scale + a=[1,2,3,4,5,6,7,8,100,10].to_numeric + b=[30,40,50,60,70,80,90,100,110,120].to_numeric summary(levene([a,b])) end diff --git a/examples/parallel_analysis.rb b/examples/parallel_analysis.rb index 0684bda..8b2709a 100644 --- a/examples/parallel_analysis.rb +++ b/examples/parallel_analysis.rb @@ -15,7 +15,7 @@ vectors={} variables.times do |i| - vectors["v#{i}"]=samples.times.collect {|nv| f1[nv]*i+(f2[nv]*(15-i))+((f3[nv]*(30-i))*1.5)*rng.call}.to_scale + vectors["v#{i}"]=samples.times.collect {|nv| f1[nv]*i+(f2[nv]*(15-i))+((f3[nv]*(30-i))*1.5)*rng.call}.to_numeric vectors["v#{i}"].name="Vector #{i}" end diff --git a/examples/u_test.rb b/examples/u_test.rb index d5ae14f..a93934a 100644 --- a/examples/u_test.rb +++ b/examples/u_test.rb @@ -4,8 +4,8 @@ Statsample::Analysis.store(Statsample::Test::UMannWhitney) do - a=10.times.map {rand(100)}.to_scale - b=20.times.map {(rand(20))**2+50}.to_scale + a=10.times.map {rand(100)}.to_numeric + b=20.times.map {(rand(20))**2+50}.to_numeric u=Statsample::Test::UMannWhitney.new(a,b) summary u diff --git a/examples/vector.rb b/examples/vector.rb index f64e62b..25f2b5d 100644 --- a/examples/vector.rb +++ b/examples/vector.rb @@ -5,7 +5,7 @@ Statsample::Analysis.store(Statsample::Vector) do - a=Statsample::Vector.new_scale(1000) {r=rand(5); r==4 ? nil: r;} + a=Statsample::Vector.new_numeric(1000) {r=rand(5); r==4 ? nil: r;} summary a b=c(1,2,3,4,6..10) summary b diff --git a/examples/velicer_map_test.rb b/examples/velicer_map_test.rb index 8ec3ed4..6514179 100644 --- a/examples/velicer_map_test.rb +++ b/examples/velicer_map_test.rb @@ -21,7 +21,7 @@ else f1[nv]*2 + f2[nv] *3 +rng.call end - }.to_scale + }.to_numeric end diff --git a/lib/statsample.rb b/lib/statsample.rb index 7ebe937..573a5f2 100644 --- a/lib/statsample.rb +++ b/lib/statsample.rb @@ -228,9 +228,9 @@ def vector_cols_matrix(*vs) # Returns a duplicate of the input vectors, without missing data # for any of the vectors. # - # a=[1,2,3,6,7,nil,3,5].to_scale - # b=[nil,nil,5,6,4,5,10,2].to_scale - # c=[2,4,6,7,4,5,6,7].to_scale + # a=[1,2,3,6,7,nil,3,5].to_numeric + # b=[nil,nil,5,6,4,5,10,2].to_numeric + # c=[2,4,6,7,4,5,6,7].to_numeric # a2,b2,c2=Statsample.only_valid(a,b,c) # => [#, # #, diff --git a/lib/statsample/anova/oneway.rb b/lib/statsample/anova/oneway.rb index e0c20a5..c895318 100644 --- a/lib/statsample/anova/oneway.rb +++ b/lib/statsample/anova/oneway.rb @@ -67,9 +67,9 @@ def report_building_table(builder) #:nodoc: # One Way Anova with vectors # Example: - # v1=[2,3,4,5,6].to_scale - # v2=[3,3,4,5,6].to_scale - # v3=[5,3,1,5,6].to_scale + # v1=[2,3,4,5,6].to_numeric + # v2=[3,3,4,5,6].to_numeric + # v3=[5,3,1,5,6].to_numeric # anova=Statsample::Anova::OneWayWithVectors.new([v1,v2,v3]) # anova.f # => 0.0243902439024391 diff --git a/lib/statsample/anova/twoway.rb b/lib/statsample/anova/twoway.rb index f623e6c..e8340dd 100644 --- a/lib/statsample/anova/twoway.rb +++ b/lib/statsample/anova/twoway.rb @@ -107,9 +107,9 @@ def report_building_table(builder) #:nodoc: # Two Way Anova with vectors # Example: - # v1=[1,1,2,2].to_scale - # v2=[1,2,1,2].to_scale - # v3=[5,3,1,5].to_scale + # v1=[1,1,2,2].to_numeric + # v2=[1,2,1,2].to_numeric + # v3=[5,3,1,5].to_numeric # anova=Statsample::Anova::TwoWayWithVectors.new(:a=>v1,:b=>v2, :dependent=>v3) # class TwoWayWithVectors < TwoWay diff --git a/lib/statsample/bivariate.rb b/lib/statsample/bivariate.rb index d24e5ff..abb9f09 100644 --- a/lib/statsample/bivariate.rb +++ b/lib/statsample/bivariate.rb @@ -125,7 +125,7 @@ def residuals(from,del) nv.push(froms[i]-r*dels[i]) end end - nv.to_vector(:scale) + nv.to_vector(:numeric) end # Correlation between v1 and v2, controling the effect of # control on both. @@ -169,7 +169,7 @@ def covariance_matrix(ds) def covariance_matrix_pairwise(ds) cache={} matrix=ds.collect_matrix do |row,col| - if (ds[row].type!=:scale or ds[col].type!=:scale) + if (ds[row].type!=:numeric or ds[col].type!=:numeric) nil elsif row==col ds[row].variance @@ -215,7 +215,7 @@ def correlation_matrix_pairwise(ds) cm=ds.collect_matrix do |row,col| if row==col 1.0 - elsif (ds[row].type!=:scale or ds[col].type!=:scale) + elsif (ds[row].type!=:numeric or ds[col].type!=:numeric) nil else if cache[[col,row]].nil? @@ -248,7 +248,7 @@ def correlation_probability_matrix(ds, tails=:both) rows=ds.fields.collect do |row| ds.fields.collect do |col| v1a,v2a=Statsample.only_valid_clone(ds[row],ds[col]) - (row==col or ds[row].type!=:scale or ds[col].type!=:scale) ? nil : prop_pearson(t_pearson(ds[row],ds[col]), v1a.size, tails) + (row==col or ds[row].type!=:numeric or ds[col].type!=:numeric) ? nil : prop_pearson(t_pearson(ds[row],ds[col]), v1a.size, tails) end end Matrix.rows(rows) @@ -257,7 +257,7 @@ def correlation_probability_matrix(ds, tails=:both) # Spearman ranked correlation coefficient (rho) between 2 vectors def spearman(v1,v2) v1a,v2a=Statsample.only_valid_clone(v1,v2) - v1r,v2r=v1a.ranked(:scale),v2a.ranked(:scale) + v1r,v2r=v1a.ranked(:numeric),v2a.ranked(:numeric) pearson(v1r,v2r) end # Calculate Point biserial correlation. Equal to Pearson correlation, with @@ -265,7 +265,7 @@ def spearman(v1,v2) def point_biserial(dichotomous,continous) ds={'d'=>dichotomous,'c'=>continous}.to_dataset.dup_only_valid raise(TypeError, "First vector should be dichotomous") if ds['d'].factors.size!=2 - raise(TypeError, "Second vector should be continous") if ds['c'].type!=:scale + raise(TypeError, "Second vector should be continous") if ds['c'].type!=:numeric f0=ds['d'].factors.sort[0] m0=ds.filter_field('c') {|c| c['d']==f0} m1=ds.filter_field('c') {|c| c['d']!=f0} @@ -276,7 +276,7 @@ def point_biserial(dichotomous,continous) def tau_a(v1,v2) v1a,v2a=Statsample.only_valid_clone(v1,v2) n=v1.size - v1r,v2r=v1a.ranked(:scale),v2a.ranked(:scale) + v1r,v2r=v1a.ranked(:numeric),v2a.ranked(:numeric) o1=ordered_pairs(v1r) o2=ordered_pairs(v2r) delta= o1.size*2-(o2 & o1).size*2 diff --git a/lib/statsample/bivariate/pearson.rb b/lib/statsample/bivariate/pearson.rb index 8dd6dea..df38966 100644 --- a/lib/statsample/bivariate/pearson.rb +++ b/lib/statsample/bivariate/pearson.rb @@ -7,8 +7,8 @@ module Bivariate # variables. # # == Usage - # a = [1,2,3,4,5,6].to_scale - # b = [2,3,4,5,6,7].to_scale + # a = [1,2,3,4,5,6].to_numeric + # b = [2,3,4,5,6,7].to_numeric # pearson = Statsample::Bivariate::Pearson.new(a,b) # puts pearson.r # puts pearson.t diff --git a/lib/statsample/converter/csv.rb b/lib/statsample/converter/csv.rb index b0ee2dd..b611fa1 100644 --- a/lib/statsample/converter/csv.rb +++ b/lib/statsample/converter/csv.rb @@ -39,7 +39,7 @@ def read(filename, empty = [''], ignore_lines = 0, opts = {}) end end - convert_to_scale_and_date(ds, fields) + convert_to_numeric_and_date(ds, fields) ds.update_valid_data ds end diff --git a/lib/statsample/converters.rb b/lib/statsample/converters.rb index 5f2bcd3..3869262 100644 --- a/lib/statsample/converters.rb +++ b/lib/statsample/converters.rb @@ -18,7 +18,7 @@ def read(dbh,query) sth.column_info.each {|c| vectors[c['name']]=Statsample::Vector.new([]) vectors[c['name']].name=c['name'] - vectors[c['name']].type= (c['type_name']=='INTEGER' or c['type_name']=='DOUBLE') ? :scale : :nominal + vectors[c['name']].type= (c['type_name']=='INTEGER' or c['type_name']=='DOUBLE') ? :numeric : :object fields.push(c['name']) } ds=Statsample::Dataset.new(vectors,fields) @@ -106,10 +106,10 @@ def process_row(row,empty) end end end - def convert_to_scale_and_date(ds,fields) + def convert_to_numeric_and_date(ds,fields) fields.each do |f| - if ds[f].can_be_scale? - ds[f].type=:scale + if ds[f].can_be_numeric? + ds[f].type=:numeric elsif ds[f].can_be_date? ds[f].type=:date end @@ -128,7 +128,7 @@ def read(filename, fields) next if row==["\x1A"] ds.add_case_array(row) end - convert_to_scale_and_date(ds,fields) + convert_to_numeric_and_date(ds,fields) ds.update_valid_data fields.each {|f| ds[f].name=f @@ -231,7 +231,7 @@ def read(filename, opts=Hash.new) raise end end - convert_to_scale_and_date(ds, fields) + convert_to_numeric_and_date(ds, fields) ds.update_valid_data fields.each {|f| ds[f].name=f @@ -345,7 +345,7 @@ def values_definition(c,missing) # nickname = nickname def variable_definition(carrier,v,name,nickname=nil) nickname = (nickname.nil? ? "" : "nickname=\"#{nickname}\"" ) - if v.type==:nominal or v.data.find {|d| d.is_a? String } + if v.type==:object or v.data.find {|d| d.is_a? String } carrier.categorials.push(name) carrier.conversions[name]={} factors=v.factors diff --git a/lib/statsample/dataset.rb b/lib/statsample/dataset.rb index 787320c..e5e2e6a 100644 --- a/lib/statsample/dataset.rb +++ b/lib/statsample/dataset.rb @@ -52,8 +52,8 @@ def to_s # # The fast way to create a dataset uses Hash#to_dataset, with # field order as arguments - # v1 = [1,2,3].to_scale - # v2 = [1,2,3].to_scale + # v1 = [1,2,3].to_numeric + # v2 = [1,2,3].to_numeric # ds = {'v1'=>v2, 'v2'=>v2}.to_dataset(%w{v2 v1}) class Dataset @@ -477,7 +477,7 @@ def add_vectors_by_split(name,join='-',sep=Statsample::SPLIT_TOKEN) } end - def vector_by_calculation(type=:scale) + def vector_by_calculation(type=:numeric) a=[] each do |row| a.push(yield(row)) @@ -547,7 +547,7 @@ def vector_mean(fields=nil, max_invalid=0) a.push(sum.quo(size-invalids)) end end - a=a.to_vector(:scale) + a=a.to_vector(:numeric) a.name=_("Means from %s") % @name a end @@ -680,7 +680,7 @@ def check_order #:nodoc: end # Retrieves a Statsample::Vector, based on the result # of calculation performed on each case. - def collect(type=:scale) + def collect(type=:numeric) data=[] each {|row| data.push yield(row) @@ -688,7 +688,7 @@ def collect(type=:scale) Statsample::Vector.new(data,type) end # Same as Statsample::Vector.collect, but giving case index as second parameter on yield. - def collect_with_index(type=:scale) + def collect_with_index(type=:numeric) data=[] each_with_index {|row, i| data.push(yield(row, i)) @@ -869,7 +869,7 @@ def to_multiset_by_split_multiple_fields(*fields) # => Vector [4,6] def compute(text) @fields.each{|f| - if @vectors[f].type=:scale + if @vectors[f].type=:numeric text.gsub!(f,"row['#{f}'].to_f") else text.gsub!(f,"row['#{f}']") @@ -958,7 +958,7 @@ def one_to_many(parent_fields, pattern) max_n=0 h=parent_fields.inject({}) {|a,v| a[v]=Statsample::Vector.new([], @vectors[v].type);a } # Adding _row_id - h['_col_id']=[].to_scale + h['_col_id']=[].to_numeric ds_vars.push("_col_id") @fields.each do |f| if f=~re diff --git a/lib/statsample/dominanceanalysis.rb b/lib/statsample/dominanceanalysis.rb index 6b4da5a..2c60577 100644 --- a/lib/statsample/dominanceanalysis.rb +++ b/lib/statsample/dominanceanalysis.rb @@ -7,9 +7,9 @@ module Statsample # # == Use # - # a=1000.times.collect {rand}.to_scale - # b=1000.times.collect {rand}.to_scale - # c=1000.times.collect {rand}.to_scale + # a=1000.times.collect {rand}.to_numeric + # b=1000.times.collect {rand}.to_numeric + # c=1000.times.collect {rand}.to_numeric # ds={'a'=>a,'b'=>b,'c'=>c}.to_dataset # ds['y']=ds.collect{|row| row['a']*5+row['b']*3+row['c']*2+rand()} # da=Statsample::DominanceAnalysis.new(ds,'y') @@ -279,7 +279,7 @@ def md_k(k) def get_averages(averages) out={} - averages.each{|key,val| out[key]=val.to_vector(:scale).mean } + averages.each{|key,val| out[key]=val.to_vector(:numeric).mean } out end # Hash with average for each k size model. diff --git a/lib/statsample/dominanceanalysis/bootstrap.rb b/lib/statsample/dominanceanalysis/bootstrap.rb index 32d1588..7bc029a 100644 --- a/lib/statsample/dominanceanalysis/bootstrap.rb +++ b/lib/statsample/dominanceanalysis/bootstrap.rb @@ -6,10 +6,10 @@ class DominanceAnalysis # == Usage # # require 'statsample' - # a=100.times.collect {rand}.to_scale - # b=100.times.collect {rand}.to_scale - # c=100.times.collect {rand}.to_scale - # d=100.times.collect {rand}.to_scale + # a=100.times.collect {rand}.to_numeric + # b=100.times.collect {rand}.to_numeric + # c=100.times.collect {rand}.to_numeric + # d=100.times.collect {rand}.to_numeric # ds={'a'=>a,'b'=>b,'c'=>c,'d'=>d}.to_dataset # ds['y']=ds.collect{|row| row['a']*5+row['b']*2+row['c']*2+row['d']*2+10*rand()} # dab=Statsample::DominanceAnalysis::Bootstrap.new(ds2, 'y', :debug=>true) @@ -182,7 +182,7 @@ def report_building(builder) # :nodoc: table.row([_("Complete dominance"),"","","","","","",""]) table.hr @pairs.each{|pair| - std=@samples_td[pair].to_vector(:scale) + std=@samples_td[pair].to_vector(:numeric) ttd=da.total_dominance_pairwise(pair[0],pair[1]) table.row(summary_pairs(pair,std,ttd)) } @@ -190,7 +190,7 @@ def report_building(builder) # :nodoc: table.row([_("Conditional dominance"),"","","","","","",""]) table.hr @pairs.each{|pair| - std=@samples_cd[pair].to_vector(:scale) + std=@samples_cd[pair].to_vector(:numeric) ttd=da.conditional_dominance_pairwise(pair[0],pair[1]) table.row(summary_pairs(pair,std,ttd)) @@ -199,7 +199,7 @@ def report_building(builder) # :nodoc: table.row([_("General Dominance"),"","","","","","",""]) table.hr @pairs.each{|pair| - std=@samples_gd[pair].to_vector(:scale) + std=@samples_gd[pair].to_vector(:numeric) ttd=da.general_dominance_pairwise(pair[0],pair[1]) table.row(summary_pairs(pair,std,ttd)) } @@ -208,7 +208,7 @@ def report_building(builder) # :nodoc: table=ReportBuilder::Table.new(:name=>_("General averages"), :header=>[_("var"), _("mean"), _("se"), _("p.5"), _("p.95")]) @fields.each{|f| - v=@samples_ga[f].to_vector(:scale) + v=@samples_ga[f].to_vector(:numeric) row=[@ds[f].name, sprintf("%0.3f",v.mean), sprintf("%0.3f",v.sd), sprintf("%0.3f",v.percentil(5)),sprintf("%0.3f",v.percentil(95))] table.row(row) diff --git a/lib/statsample/factor/parallelanalysis.rb b/lib/statsample/factor/parallelanalysis.rb index 5a7ff28..a4c4a38 100644 --- a/lib/statsample/factor/parallelanalysis.rb +++ b/lib/statsample/factor/parallelanalysis.rb @@ -124,7 +124,7 @@ def compute @original=Statsample::Bivariate.send(matrix_method, @ds).eigenvalues unless no_data @ds_eigenvalues=Statsample::Dataset.new((1..@n_variables).map{|v| "ev_%05d" % v}) - @ds_eigenvalues.fields.each {|f| @ds_eigenvalues[f].type=:scale} + @ds_eigenvalues.fields.each {|f| @ds_eigenvalues[f].type=:numeric} if bootstrap_method==:parameter or bootstrap_method==:random rng = Distribution::Normal.rng end @@ -137,7 +137,7 @@ def compute @fields.each do |f| if bootstrap_method==:random - ds_bootstrap[f]=@n_cases.times.map {|c| rng.call}.to_scale + ds_bootstrap[f]=@n_cases.times.map {|c| rng.call}.to_numeric elsif bootstrap_method==:data ds_bootstrap[f]=ds[f].sample_with_replacement(@n_cases) else diff --git a/lib/statsample/factor/pca.rb b/lib/statsample/factor/pca.rb index fa5fb37..e3ffcef 100644 --- a/lib/statsample/factor/pca.rb +++ b/lib/statsample/factor/pca.rb @@ -13,8 +13,8 @@ module Factor # # == Usage: # require 'statsample' - # a=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_scale - # b=[2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9].to_scale + # a=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_numeric + # b=[2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9].to_numeric # ds={'a'=>a,'b'=>b}.to_dataset # cor_matrix=Statsample::Bivariate.correlation_matrix(ds) # pca=Statsample::Factor::PCA.new(cor_matrix) diff --git a/lib/statsample/factor/principalaxis.rb b/lib/statsample/factor/principalaxis.rb index 4420bf3..d57cbcd 100644 --- a/lib/statsample/factor/principalaxis.rb +++ b/lib/statsample/factor/principalaxis.rb @@ -6,8 +6,8 @@ module Factor # # == Usage: # require 'statsample' - # a=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_scale - # b=[2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9].to_scale + # a=[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_numeric + # b=[2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9].to_numeric # ds={'a'=>a,'b'=>b}.to_dataset # cor_matrix=Statsample::Bivariate.correlation_matrix(ds) # pa=Statsample::Factor::PrincipalAxis.new(cor_matrix) diff --git a/lib/statsample/graph/boxplot.rb b/lib/statsample/graph/boxplot.rb index da1cd7d..03dcbd2 100644 --- a/lib/statsample/graph/boxplot.rb +++ b/lib/statsample/graph/boxplot.rb @@ -8,12 +8,12 @@ module Graph # # == Usage # === Svg output - # a=[1,2,3,4].to_scale - # b=[3,4,5,6].to_scale + # a=[1,2,3,4].to_numeric + # b=[3,4,5,6].to_numeric # puts Statsample::Graph::Boxplot.new(:vectors=>[a,b]).to_svg # === Using ReportBuilder - # a=[1,2,3,4].to_scale - # b=[3,4,5,6].to_scale + # a=[1,2,3,4].to_numeric + # b=[3,4,5,6].to_numeric # rb=ReportBuilder.new # rb.add(Statsample::Graph::Boxplot.new(:vectors=>[a,b])) # rb.save_html('boxplot.html') diff --git a/lib/statsample/graph/histogram.rb b/lib/statsample/graph/histogram.rb index 3fd21d7..ef6da76 100644 --- a/lib/statsample/graph/histogram.rb +++ b/lib/statsample/graph/histogram.rb @@ -6,10 +6,10 @@ module Graph # # == Usage # === Svg output - # a=[1,2,3,4].to_scale + # a=[1,2,3,4].to_numeric # puts Statsample::Graph::Histogram.new(a).to_svg # === Using ReportBuilder - # a=[1,2,3,4].to_scale + # a=[1,2,3,4].to_numeric # rb=ReportBuilder.new # rb.add(Statsample::Graph::Histogram.new(a)) # rb.save_html('histogram.html') diff --git a/lib/statsample/graph/scatterplot.rb b/lib/statsample/graph/scatterplot.rb index d6f2ee8..23515ef 100644 --- a/lib/statsample/graph/scatterplot.rb +++ b/lib/statsample/graph/scatterplot.rb @@ -10,12 +10,12 @@ module Graph # The data is displayed as a collection of points, each having the value of one variable determining the position on the horizontal axis and the value of the other variable determining the position on the vertical axis.[2] This kind of plot is also called a scatter chart, scatter diagram and scatter graph. # == Usage # === Svg output - # a=[1,2,3,4].to_scale - # b=[3,4,5,6].to_scale + # a=[1,2,3,4].to_numeric + # b=[3,4,5,6].to_numeric # puts Statsample::Graph::Scatterplot.new(a,b).to_svg # === Using ReportBuilder - # a=[1,2,3,4].to_scale - # b=[3,4,5,6].to_scale + # a=[1,2,3,4].to_numeric + # b=[3,4,5,6].to_numeric # rb=ReportBuilder.new # rb.add(Statsample::Graph::Scatterplot.new(a,b)) # rb.save_html('scatter.html') diff --git a/lib/statsample/matrix.rb b/lib/statsample/matrix.rb index 79e0303..7c94ed5 100644 --- a/lib/statsample/matrix.rb +++ b/lib/statsample/matrix.rb @@ -15,7 +15,7 @@ def to_dataset f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| _("VAR_%d") % (i+1) } ds=Statsample::Dataset.new(f) f.each do |ff| - ds[ff].type=:scale + ds[ff].type=:numeric ds[ff].name=ff end row_size.times {|i| @@ -87,7 +87,7 @@ def to_dataset f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| _("VAR_%d") % (i+1) } ds=Statsample::Dataset.new(f) f.each do |ff| - ds[ff].type=:scale + ds[ff].type=:numeric ds[ff].name=ff end row_size.times {|i| diff --git a/lib/statsample/regression.rb b/lib/statsample/regression.rb index 0016e8a..f9a7cd2 100644 --- a/lib/statsample/regression.rb +++ b/lib/statsample/regression.rb @@ -25,8 +25,8 @@ module Regression # * x: independent Vector # * y: dependent Vector # Usage: - # x=100.times.collect {|i| rand(100)}.to_scale - # y=100.times.collect {|i| 2+x[i]*2+rand()}.to_scale + # x=100.times.collect {|i| rand(100)}.to_numeric + # y=100.times.collect {|i| 2+x[i]*2+rand()}.to_numeric # sr=Statsample::Regression.simple(x,y) # sr.a # => 2.51763295177808 diff --git a/lib/statsample/regression/multiple.rb b/lib/statsample/regression/multiple.rb index 317efbc..75b8397 100644 --- a/lib/statsample/regression/multiple.rb +++ b/lib/statsample/regression/multiple.rb @@ -6,9 +6,9 @@ module Regression # Use:. # # require 'statsample' - # a=1000.times.collect {rand}.to_scale - # b=1000.times.collect {rand}.to_scale - # c=1000.times.collect {rand}.to_scale + # a=1000.times.collect {rand}.to_numeric + # b=1000.times.collect {rand}.to_numeric + # c=1000.times.collect {rand}.to_numeric # ds={'a'=>a,'b'=>b,'c'=>c}.to_dataset # ds['y']=ds.collect{|row| row['a']*5+row['b']*3+row['c']*2+rand()} # lr=Statsample::Regression.multiple(ds,'y') diff --git a/lib/statsample/regression/multiple/alglibengine.rb b/lib/statsample/regression/multiple/alglibengine.rb index d6ab942..b774383 100644 --- a/lib/statsample/regression/multiple/alglibengine.rb +++ b/lib/statsample/regression/multiple/alglibengine.rb @@ -9,10 +9,10 @@ module Multiple # If you need pairwise, use RubyEngine # Example: # -# @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:scale) -# @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:scale) -# @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:scale) -# @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:scale) +# @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:numeric) +# @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:numeric) +# @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:numeric) +# @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:numeric) # ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset # lr=Statsample::Regression::Multiple::AlglibEngine.new(ds,'y') # @@ -109,7 +109,7 @@ def standarized_residuals red_sd=residuals.sds res.collect {|v| v.quo(red_sd) - }.to_vector(:scale) + }.to_vector(:numeric) end end end diff --git a/lib/statsample/regression/multiple/baseengine.rb b/lib/statsample/regression/multiple/baseengine.rb index d5e08ae..5f287a1 100644 --- a/lib/statsample/regression/multiple/baseengine.rb +++ b/lib/statsample/regression/multiple/baseengine.rb @@ -53,7 +53,7 @@ def predicted else process(vect) end - }.to_vector(:scale) + }.to_vector(:numeric) end # Retrieves a vector with standarized values for y def standarized_predicted @@ -69,7 +69,7 @@ def residuals else @ds[@y_var][i] - process(vect) end - }.to_vector(:scale) + }.to_vector(:numeric) end # R Multiple def r @@ -133,7 +133,7 @@ def probability def tolerance(var) ds=assign_names(@dep_columns) ds.each{|k,v| - ds[k]=v.to_vector(:scale) + ds[k]=v.to_vector(:numeric) } lr=self.class.new(ds.to_dataset,var) 1-lr.r2 diff --git a/lib/statsample/regression/multiple/gslengine.rb b/lib/statsample/regression/multiple/gslengine.rb index 5f3ef32..ad6492c 100644 --- a/lib/statsample/regression/multiple/gslengine.rb +++ b/lib/statsample/regression/multiple/gslengine.rb @@ -9,10 +9,10 @@ module Multiple # If you need pairwise, use RubyEngine # Example: # - # @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:scale) - # @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:scale) - # @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:scale) - # @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:scale) + # @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:numeric) + # @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:numeric) + # @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:numeric) + # @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:numeric) # ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset # lr=Statsample::Regression::Multiple::GslEngine.new(ds,'y') # @@ -109,7 +109,7 @@ def standarized_residuals red_sd=residuals.sds res.collect {|v| v.quo(red_sd) - }.to_vector(:scale) + }.to_vector(:numeric) end # Standard error for coeffs diff --git a/lib/statsample/regression/multiple/rubyengine.rb b/lib/statsample/regression/multiple/rubyengine.rb index fcee05f..13c1718 100644 --- a/lib/statsample/regression/multiple/rubyengine.rb +++ b/lib/statsample/regression/multiple/rubyengine.rb @@ -8,10 +8,10 @@ module Multiple # # Example: # -# @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:scale) -# @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:scale) -# @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:scale) -# @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:scale) +# @a=[1,3,2,4,3,5,4,6,5,7].to_vector(:numeric) +# @b=[3,3,4,4,5,5,6,6,4,4].to_vector(:numeric) +# @c=[11,22,30,40,50,65,78,79,99,100].to_vector(:numeric) +# @y=[3,4,5,6,7,8,9,10,20,30].to_vector(:numeric) # ds={'a'=>@a,'b'=>@b,'c'=>@c,'y'=>@y}.to_dataset # lr=Statsample::Regression::Multiple::RubyEngine.new(ds,'y') diff --git a/lib/statsample/reliability/icc.rb b/lib/statsample/reliability/icc.rb index 1277acc..1f8160c 100644 --- a/lib/statsample/reliability/icc.rb +++ b/lib/statsample/reliability/icc.rb @@ -7,7 +7,7 @@ module Reliability # == Usage # require 'statsample' # size=1000 - # a = size.times.map {rand(10)}.to_scale + # a = size.times.map {rand(10)}.to_numeric # b = a.recode{|i|i+rand(4)-2} # c =a.recode{|i|i+rand(4)-2} # d = a.recode{|i|i+rand(4)-2} diff --git a/lib/statsample/reliability/multiscaleanalysis.rb b/lib/statsample/reliability/multiscaleanalysis.rb index 3222593..3bc4cf2 100644 --- a/lib/statsample/reliability/multiscaleanalysis.rb +++ b/lib/statsample/reliability/multiscaleanalysis.rb @@ -6,10 +6,10 @@ module Reliability # PCA and Factor Analysis. # # == Usage - # @x1=[1,1,1,1,2,2,2,2,3,3,3,30].to_vector(:scale) - # @x2=[1,1,1,2,2,3,3,3,3,4,4,50].to_vector(:scale) - # @x3=[2,2,1,1,1,2,2,2,3,4,5,40].to_vector(:scale) - # @x4=[1,2,3,4,4,4,4,3,4,4,5,30].to_vector(:scale) + # @x1=[1,1,1,1,2,2,2,2,3,3,3,30].to_vector(:numeric) + # @x2=[1,1,1,2,2,3,3,3,3,4,4,50].to_vector(:numeric) + # @x3=[2,2,1,1,1,2,2,2,3,4,5,40].to_vector(:numeric) + # @x4=[1,2,3,4,4,4,4,3,4,4,5,30].to_vector(:numeric) # ds={'x1'=>@x1,'x2'=>@x2,'x3'=>@x3,'x4'=>@x4}.to_dataset # opts={:name=>"Scales", # Name of analysis # :summary_correlation_matrix=>true, # Add correlation matrix diff --git a/lib/statsample/reliability/scaleanalysis.rb b/lib/statsample/reliability/scaleanalysis.rb index 9a48d0e..86c76a4 100644 --- a/lib/statsample/reliability/scaleanalysis.rb +++ b/lib/statsample/reliability/scaleanalysis.rb @@ -3,10 +3,10 @@ module Reliability # Analysis of a Scale. Analoge of Scale Reliability analysis on SPSS. # Returns several statistics for complete scale and each item # == Usage - # @x1=[1,1,1,1,2,2,2,2,3,3,3,30].to_vector(:scale) - # @x2=[1,1,1,2,2,3,3,3,3,4,4,50].to_vector(:scale) - # @x3=[2,2,1,1,1,2,2,2,3,4,5,40].to_vector(:scale) - # @x4=[1,2,3,4,4,4,4,3,4,4,5,30].to_vector(:scale) + # @x1=[1,1,1,1,2,2,2,2,3,3,3,30].to_vector(:numeric) + # @x2=[1,1,1,2,2,3,3,3,3,4,4,50].to_vector(:numeric) + # @x3=[2,2,1,1,1,2,2,2,3,4,5,40].to_vector(:numeric) + # @x4=[1,2,3,4,4,4,4,3,4,4,5,30].to_vector(:numeric) # ds={'x1'=>@x1,'x2'=>@x2,'x3'=>@x3,'x4'=>@x4}.to_dataset # ia=Statsample::Reliability::ScaleAnalysis.new(ds) # puts ia.summary @@ -49,7 +49,7 @@ def initialize(ds, opts=Hash.new) @cov_m=Statsample::Bivariate.covariance_matrix(@ds) # Mean for covariances and variances - @variances=@k.times.map {|i| @cov_m[i,i]}.to_scale + @variances=@k.times.map {|i| @cov_m[i,i]}.to_numeric @variances_mean=@variances.mean @covariances_mean=(@variance-@variances.sum).quo(@k**2-@k) #begin @@ -97,7 +97,7 @@ def item_total_correlation end end def mean_rpb - item_total_correlation.values.to_scale.mean + item_total_correlation.values.to_numeric.mean end def item_statistics @is||=@ds.fields.inject({}) do |a,v| diff --git a/lib/statsample/reliability/skillscaleanalysis.rb b/lib/statsample/reliability/skillscaleanalysis.rb index 5ce410b..c1eff12 100644 --- a/lib/statsample/reliability/skillscaleanalysis.rb +++ b/lib/statsample/reliability/skillscaleanalysis.rb @@ -54,7 +54,7 @@ def scale_analysis def corrected_dataset if @cds.nil? @cds=@ds.dup_empty - @key.keys.each {|k| @cds[k].type=:scale; @cds[k].name=@ds[k].name} + @key.keys.each {|k| @cds[k].type=:numeric; @cds[k].name=@ds[k].name} @ds.each do |row| out={} row.each do |k,v| diff --git a/lib/statsample/resample.rb b/lib/statsample/resample.rb index 8a1795d..b23277e 100644 --- a/lib/statsample/resample.rb +++ b/lib/statsample/resample.rb @@ -7,7 +7,7 @@ def repeat_and_save(times,&action) def generate (size,low,upper) range=upper-low+1 - Vector.new((0...size).collect {|x| rand(range)+low },:scale) + Vector.new((0...size).collect {|x| rand(range)+low },:numeric) end end diff --git a/lib/statsample/shorthand.rb b/lib/statsample/shorthand.rb index d4956f3..7ec5f43 100644 --- a/lib/statsample/shorthand.rb +++ b/lib/statsample/shorthand.rb @@ -52,7 +52,7 @@ def vector(*args) # Random generation for the normal distribution def rnorm(n,mean=0,sd=1) rng=Distribution::Normal.rng(mean,sd) - Statsample::Vector.new_scale(n) { rng.call} + Statsample::Vector.new_numeric(n) { rng.call} end # Creates a new Statsample::Dataset # Each key is transformed into string diff --git a/lib/statsample/test/levene.rb b/lib/statsample/test/levene.rb index 4727ceb..4bbcd3f 100644 --- a/lib/statsample/test/levene.rb +++ b/lib/statsample/test/levene.rb @@ -5,8 +5,8 @@ module Test #
Levene's test ( Levene, 1960) is used to test if k samples have equal variances. Equal variances across samples is called homogeneity of variance. Some statistical tests, for example the analysis of variance, assume that variances are equal across groups or samples. The Levene test can be used to verify that assumption.
# Use: # require 'statsample' - # a=[1,2,3,4,5,6,7,8,100,10].to_scale - # b=[30,40,50,60,70,80,90,100,110,120].to_scale + # a=[1,2,3,4,5,6,7,8,100,10].to_numeric + # b=[30,40,50,60,70,80,90,100,110,120].to_numeric # # levene=Statsample::Test::Levene.new([a,b]) # puts levene.summary @@ -52,12 +52,12 @@ def compute zi=@vectors.collect {|vector| mean=vector.mean - vector.collect {|v| (v-mean).abs }.to_scale + vector.collect {|v| (v-mean).abs }.to_numeric } total_mean=zi.inject([]) {|ac,vector| ac+vector.valid_data - }.to_scale.mean + }.to_numeric.mean k=@vectors.size diff --git a/lib/statsample/test/t.rb b/lib/statsample/test/t.rb index d0306a9..b371651 100644 --- a/lib/statsample/test/t.rb +++ b/lib/statsample/test/t.rb @@ -125,7 +125,7 @@ def report_building_t(s) # One Sample t-test # == Usage - # a=1000.times.map {rand(100)}.to_scale + # a=1000.times.map {rand(100)}.to_numeric # t_1=Statsample::Test::T::OneSample.new(a, {:u=>50}) # t_1.summary # @@ -196,8 +196,8 @@ def report_building(b) # :nodoc: # Two Sample t-test. # # == Usage - # a=1000.times.map {rand(100)}.to_scale - # b=1000.times.map {rand(100)}.to_scale + # a=1000.times.map {rand(100)}.to_numeric + # b=1000.times.map {rand(100)}.to_numeric # t_2=Statsample::Test::T::TwoSamplesIndependent.new(a,b) # t_2.summary # === Output diff --git a/lib/statsample/test/umannwhitney.rb b/lib/statsample/test/umannwhitney.rb index e41d93d..7d02e38 100644 --- a/lib/statsample/test/umannwhitney.rb +++ b/lib/statsample/test/umannwhitney.rb @@ -120,7 +120,7 @@ def initialize(v1,v2, opts=Hash.new) @v2=v2 @n1=v1.valid_data.size @n2=v2.valid_data.size - data=(v1.valid_data+v2.valid_data).to_scale + data=(v1.valid_data+v2.valid_data).to_numeric groups=(([0]*@n1)+([1]*@n2)).to_vector ds={'g'=>groups, 'data'=>data}.to_dataset @t=nil @@ -128,7 +128,7 @@ def initialize(v1,v2, opts=Hash.new) if(@ties) adjust_for_ties(ds['data']) end - ds['ranked']=ds['data'].ranked(:scale) + ds['ranked']=ds['data'].ranked(:numeric) @n=ds.cases diff --git a/lib/statsample/vector.rb b/lib/statsample/vector.rb index 1ad98a4..eae7213 100644 --- a/lib/statsample/vector.rb +++ b/lib/statsample/vector.rb @@ -8,9 +8,15 @@ def to_vector(*args) Statsample::Vector.new(self,*args) end - # Creates a new Statsample::Vector object of type :scale + # Creates a new Statsample::Vector object of type :scale. + # Deprecated. Use to_numeric instead. def to_scale(*args) - Statsample::Vector.new(self, :scale, *args) + puts "WARNING: to_scale has been deprecated. Use to_numeric instead." + Statsample::Vector.new(self, :numeric, *args) + end + + def to_numeric(*args) + Statsample::Vector.new(self, :numeric, *args) end end @@ -31,10 +37,10 @@ module Statsample # Collection of values on one dimension. Works as a column on a Spreadsheet. # # == Usage - # The fast way to create a vector uses Array.to_vector or Array.to_scale. + # The fast way to create a vector uses Array.to_vector or Array.to_numeric. # - # v=[1,2,3,4].to_vector(:scale) - # v=[1,2,3,4].to_scale + # v=[1,2,3,4].to_vector(:numeric) + # v=[1,2,3,4].to_numeric # class Vector include Enumerable @@ -42,7 +48,7 @@ class Vector include Summarizable include Statsample::VectorShorthands - # Level of measurement. Could be :nominal, :ordinal or :scale + # Level of measurement. Could be :object, :numeric attr_reader :type # Original data. attr_reader :data @@ -71,7 +77,17 @@ class Vector # * :today_values Array of 'today' values. See Vector#today_values # * :labels Labels for data values # * :name Name of vector - def initialize(data=[], type=:nominal, opts=Hash.new) + def initialize(data=[], type=:object, opts=Hash.new) + if type == :ordinal or type == :scale + puts "WARNING: #{type} has been deprecated. Use :numeric instead." + type = :numeric + end + + if type == :nominal + puts "WARNING: nominal has been deprecated. Use :object instead." + type = :object + end + @data=data.is_a?(Array) ? data : data.to_a @type=type opts_default={ @@ -95,7 +111,7 @@ def initialize(data=[], type=:nominal, opts=Hash.new) @date_data_with_nils=[] @missing_data=[] @has_missing_data=nil - @scale_data=nil + @numeric_data=nil set_valid_data self.type=type end @@ -119,23 +135,29 @@ def self.[](*args) end end vector=new(values) - vector.type=:scale if vector.can_be_scale? + vector.type=:numeric if vector.can_be_numeric? vector end - # Create a new scale type vector + # Create a new numeric type vector # Parameters # [n] Size # [val] Value of each value # [&block] If block provided, is used to set the values of vector - def self.new_scale(n,val=nil, &block) + def self.new_numeric(n,val=nil, &block) if block - vector=n.times.map {|i| block.call(i)}.to_scale + vector=n.times.map {|i| block.call(i)}.to_numeric else - vector=n.times.map { val}.to_scale + vector=n.times.map { val}.to_numeric end - vector.type=:scale + vector.type=:numeric vector end + + # Deprecated. Use new_numeric instead. + def self.new_scale(n, val=nil,&block) + puts "WARNING: .new_scale has been deprecated. Use .new_numeric instead." + new_numeric n, val, &block + end # Creates a duplicate of the Vector. # Note: data, missing_values and labels are duplicated, so # changes on original vector doesn't propages to copies. @@ -161,33 +183,34 @@ def check_type(t) #:nodoc: def _check_type(t) #:nodoc: - raise NoMethodError if (t==:scale and @type!=:scale) or (t==:ordinal and @type==:nominal) or (t==:date) or (:date==@type) + raise NoMethodError if (t == :numeric and @type == :object) or + (t == :date) or (:date == @type) end def vector_standarized_compute(m,sd) # :nodoc: - @data_with_nils.collect{|x| x.nil? ? nil : (x.to_f - m).quo(sd) }.to_vector(:scale) + @data_with_nils.collect{|x| x.nil? ? nil : (x.to_f - m).quo(sd) }.to_vector(:numeric) end # Return a vector usign the standarized values for data # with sd with denominator n-1. With variance=0 or mean nil, # returns a vector of equal size full of nils # def vector_standarized(use_population=false) - check_type :scale + check_type :numeric m=mean sd=use_population ? sdp : sds - return ([nil]*size).to_scale if mean.nil? or sd==0.0 + return ([nil]*size).to_numeric if mean.nil? or sd==0.0 vector=vector_standarized_compute(m,sd) vector.name=_("%s(standarized)") % @name vector end def vector_centered_compute(m) #:nodoc: - @data_with_nils.collect {|x| x.nil? ? nil : x.to_f-m }.to_scale + @data_with_nils.collect {|x| x.nil? ? nil : x.to_f-m }.to_numeric end # Return a centered vector def vector_centered - check_type :scale + check_type :numeric m=mean - return ([nil]*size).to_scale if mean.nil? + return ([nil]*size).to_numeric if mean.nil? vector=vector_centered_compute(m) vector.name=_("%s(centered)") % @name vector @@ -198,14 +221,14 @@ def vector_centered # Return a vector with values replaced with the percentiles # of each values def vector_percentil - check_type :ordinal + check_type :numeric c=@valid_data.size vector=ranked.map {|i| i.nil? ? nil : (i.quo(c)*100).to_f }.to_vector(@type) vector.name=_("%s(percentil)") % @name vector end def box_cox_transformation(lambda) # :nodoc: - raise "Should be a scale" unless @type==:scale + raise "Should be a numeric" unless @type==:numeric @data_with_nils.collect{|x| if !x.nil? if(lambda==0) @@ -216,7 +239,7 @@ def box_cox_transformation(lambda) # :nodoc: else nil end - }.to_vector(:scale) + }.to_vector(:numeric) end # Vector equality. @@ -269,7 +292,7 @@ def dichotomize(low = nil) else 0 end - end.to_scale + end.to_numeric end # Iterate on each item. # Equivalent to @@ -313,7 +336,7 @@ def set_valid_data @data_with_nils.clear @date_data_with_nils.clear set_valid_data_intern - set_scale_data if(@type==:scale) + set_numeric_data if(@type==:numeric) set_date_data if(@type==:date) end if Statsample::STATSAMPLE__.respond_to?(:set_valid_data_intern) @@ -394,7 +417,7 @@ def today_values=(vals) # Set level of measurement. def type=(t) @type=t - set_scale_data if(t==:scale) + set_numeric_data if(t==:numeric) set_date_data if (t==:date) end def to_a @@ -450,7 +473,7 @@ def _vector_ari(method,v) # :nodoc: sum.push(nil) end } - Statsample::Vector.new(sum, :scale) + Statsample::Vector.new(sum, :numeric) elsif(v.respond_to? method ) Statsample::Vector.new( @data.collect {|x| @@ -459,7 +482,7 @@ def _vector_ari(method,v) # :nodoc: else nil end - } , :scale) + } , :numeric) else raise TypeError,"You should pass a scalar or a array/vector" end @@ -487,11 +510,11 @@ def splitted(sep=Statsample::SPLIT_TOKEN) # # a=Vector.new(["a,b","c,d","a,b"]) # a.split_by_separator - # => {"a"=># {"a"=>#, - # "b"=>##, - # "c"=>##} # def split_by_separator(sep=Statsample::SPLIT_TOKEN) @@ -513,7 +536,7 @@ def split_by_separator(sep=Statsample::SPLIT_TOKEN) end end out.inject({}){|s,v| - s[v[0]]=Vector.new(v[1],:nominal) + s[v[0]]=Vector.new(v[1],:object) s } end @@ -554,8 +577,8 @@ def bootstrap(estimators, nr, s=nil) end es.each do |est| - bss[est]=bss[est].to_scale - bss[est].type=:scale + bss[est]=bss[est].to_numeric + bss[est].type=:numeric end bss.to_dataset @@ -595,7 +618,7 @@ def jacknife(estimators, k=1) nb.times do |i| other=@data_with_nils.dup other.slice!(i*k,k) - other=other.to_scale + other=other.to_numeric es.each do |estimator| # Add pseudovalue ps[estimator].push( nb * est_n[estimator] - (nb-1) * h_est[estimator].call(other)) @@ -604,8 +627,8 @@ def jacknife(estimators, k=1) es.each do |est| - ps[est]=ps[est].to_scale - ps[est].type=:scale + ps[est]=ps[est].to_numeric + ps[est].type=:numeric end ps.to_dataset end @@ -702,7 +725,7 @@ def can_be_date? end end # Return true if all data is Numeric or nil - def can_be_scale? + def can_be_numeric? if @data.find {|v| !v.nil? and !v.is_a? Numeric and !@missing_values.include? v} false else @@ -728,8 +751,8 @@ def inspect end # Retrieves uniques values for data. def factors - if @type==:scale - @scale_data.uniq.sort + if @type==:numeric + @numeric_data.uniq.sort elsif @type==:date @date_data_with_nils.uniq.sort else @@ -781,7 +804,7 @@ def report_building(b) b.section(:name=>name) do |s| s.text _("n :%d") % n s.text _("n valid:%d") % n_valid - if @type==:nominal + if @type==:object s.text _("factors:%s") % factors.join(",") s.text _("mode: %s") % mode @@ -793,8 +816,8 @@ def report_building(b) end end - s.text _("median: %s") % median.to_s if(@type==:ordinal or @type==:scale) - if(@type==:scale) + s.text _("median: %s") % median.to_s if(@type==:numeric or @type==:numeric) + if(@type==:numeric) s.text _("mean: %0.4f") % mean if sd s.text _("std.dev.: %0.4f") % sd @@ -829,7 +852,7 @@ def proportion_confidence_interval_z(n_poblation,margin=0.95,v=1) end ###### - ### Ordinal Methods + ### numeric Methods ###### # == Percentil @@ -843,7 +866,7 @@ def proportion_confidence_interval_z(n_poblation,margin=0.95,v=1) # This is the NIST recommended method (http://en.wikipedia.org/wiki/Percentile#NIST_method) # def percentil(q, strategy = :midpoint) - check_type :ordinal + check_type :numeric sorted=@valid_data.sort case strategy @@ -873,8 +896,8 @@ def percentil(q, strategy = :midpoint) end # Returns a ranked vector. - def ranked(type=:ordinal) - check_type :ordinal + def ranked(type=:numeric) + check_type :numeric i=0 r=frequencies.sort.inject({}){|a,v| a[v[0]]=(i+1 + i+v[1]).quo(2) @@ -885,17 +908,17 @@ def ranked(type=:ordinal) end # Return the median (percentil 50) def median - check_type :ordinal + check_type :numeric percentil(50) end # Minimun value def min - check_type :ordinal + check_type :numeric @valid_data.min end # Maximum value def max - check_type :ordinal + check_type :numeric @valid_data.max end @@ -915,8 +938,8 @@ def set_date_data end end - def set_scale_data - @scale_data=@valid_data.collect do|x| + def set_numeric_data + @numeric_data=@valid_data.collect do|x| if x.is_a? Numeric x elsif x.is_a? String and x.to_i==x.to_f @@ -927,21 +950,21 @@ def set_scale_data end end - private :set_date_data, :set_scale_data + private :set_date_data, :set_numeric_data # The range of the data (max - min) def range; - check_type :scale - @scale_data.max - @scale_data.min + check_type :numeric + @numeric_data.max - @numeric_data.min end # The sum of values for the data def sum - check_type :scale - @scale_data.inject(0){|a,x|x+a} ; + check_type :numeric + @numeric_data.inject(0){|a,x|x+a} ; end # The arithmetical mean of data def mean - check_type :scale + check_type :numeric sum.to_f.quo(n_valid) end # Sum of squares for the data around a value. @@ -949,28 +972,28 @@ def mean # ss= sum{(xi-m)^2} # def sum_of_squares(m=nil) - check_type :scale + check_type :numeric m||=mean - @scale_data.inject(0){|a,x| a+(x-m).square} + @numeric_data.inject(0){|a,x| a+(x-m).square} end # Sum of squared deviation def sum_of_squared_deviation - check_type :scale - @scale_data.inject(0) {|a,x| x.square+a} - (sum.square.quo(n_valid)) + check_type :numeric + @numeric_data.inject(0) {|a,x| x.square+a} - (sum.square.quo(n_valid)) end # Population variance (denominator N) def variance_population(m=nil) - check_type :scale + check_type :numeric m||=mean - squares=@scale_data.inject(0){|a,x| x.square+a} + squares=@numeric_data.inject(0){|a,x| x.square+a} squares.quo(n_valid) - m.square end # Population Standard deviation (denominator N) def standard_deviation_population(m=nil) - check_type :scale + check_type :numeric Math::sqrt( variance_population(m) ) end @@ -978,9 +1001,9 @@ def standard_deviation_population(m=nil) # author: Al Chou def average_deviation_population( m = nil ) - check_type :scale + check_type :numeric m ||= mean - ( @scale_data.inject( 0 ) { |a, x| ( x - m ).abs + a } ).quo( n_valid ) + ( @numeric_data.inject( 0 ) { |a, x| ( x - m ).abs + a } ).quo( n_valid ) end def median_absolute_deviation med=median @@ -989,43 +1012,43 @@ def median_absolute_deviation alias :mad :median_absolute_deviation # Sample Variance (denominator n-1) def variance_sample(m=nil) - check_type :scale + check_type :numeric m||=mean sum_of_squares(m).quo(n_valid - 1) end # Sample Standard deviation (denominator n-1) def standard_deviation_sample(m=nil) - check_type :scale + check_type :numeric m||=mean Math::sqrt(variance_sample(m)) end # Skewness of the sample def skew(m=nil) - check_type :scale + check_type :numeric m||=mean - th=@scale_data.inject(0){|a,x| a+((x-m)**3)} - th.quo((@scale_data.size)*sd(m)**3) + th=@numeric_data.inject(0){|a,x| a+((x-m)**3)} + th.quo((@numeric_data.size)*sd(m)**3) end # Kurtosis of the sample def kurtosis(m=nil) - check_type :scale + check_type :numeric m||=mean - fo=@scale_data.inject(0){|a,x| a+((x-m)**4)} - fo.quo((@scale_data.size)*sd(m)**4)-3 + fo=@numeric_data.inject(0){|a,x| a+((x-m)**4)} + fo.quo((@numeric_data.size)*sd(m)**4)-3 end # Product of all values on the sample # def product - check_type :scale - @scale_data.inject(1){|a,x| a*x } + check_type :numeric + @numeric_data.inject(1){|a,x| a*x } end # With a fixnum, creates X bins within the range of data # With an Array, each value will be a cut point def histogram(bins=10) - check_type :scale + check_type :numeric if bins.is_a? Array #h=Statsample::Histogram.new(self, bins) @@ -1050,7 +1073,7 @@ def histogram(bins=10) # Coefficient of variation # Calculed with the sample standard deviation def coefficient_of_variation - check_type :scale + check_type :numeric standard_deviation_sample.quo(mean) end # Standard error of the distribution mean diff --git a/lib/statsample/vector/gsl.rb b/lib/statsample/vector/gsl.rb index 9b12418..e1a7527 100644 --- a/lib/statsample/vector/gsl.rb +++ b/lib/statsample/vector/gsl.rb @@ -18,7 +18,7 @@ def push(v) end def gsl - @gsl||=GSL::Vector.alloc(@scale_data) if @scale_data.size>0 + @gsl||=GSL::Vector.alloc(@numeric_data) if @numeric_data.size>0 end alias :to_gsl :gsl @@ -26,7 +26,7 @@ def vector_standarized_compute(m,sd) if flawed? vector_standarized_compute_ruby(m,sd) else - gsl.collect {|x| (x.to_f - m).quo(sd)}.to_scale + gsl.collect {|x| (x.to_f - m).quo(sd)}.to_numeric end end @@ -34,20 +34,20 @@ def vector_centered_compute(m) if flawed? vector_centered_compute_ruby(m) else - gsl.collect {|x| (x.to_f - m)}.to_scale + gsl.collect {|x| (x.to_f - m)}.to_numeric end end def sample_with_replacement(sample=1) - if(@type!=:scale) + if(@type!=:numeric) sample_with_replacement_ruby(sample) else r = GSL::Rng.alloc(GSL::Rng::MT19937,rand(10000)) - Statsample::Vector.new(r.sample(gsl, sample).to_a,:scale) + Statsample::Vector.new(r.sample(gsl, sample).to_a,:numeric) end end def sample_without_replacement(sample=1) - if(@type!=:scale) + if(@type!=:numeric) sample_without_replacement_ruby(sample) else r = GSL::Rng.alloc(GSL::Rng::MT19937,rand(10000)) @@ -55,50 +55,50 @@ def sample_without_replacement(sample=1) end end def median - if @type!=:scale + if @type!=:numeric median_ruby else - sorted=GSL::Vector.alloc(@scale_data.sort) + sorted=GSL::Vector.alloc(@numeric_data.sort) GSL::Stats::median_from_sorted_data(sorted) end end def sum - check_type :scale + check_type :numeric gsl.nil? ? nil : gsl.sum end def mean - check_type :scale + check_type :numeric gsl.nil? ? nil : gsl.mean end def variance_sample(m=nil) - check_type :scale + check_type :numeric m||=mean gsl.nil? ? nil : gsl.variance_m end def standard_deviation_sample(m=nil) - check_type :scale + check_type :numeric m||=mean gsl.nil? ? nil : gsl.sd(m) end def variance_population(m=nil) # :nodoc: - check_type :scale + check_type :numeric m||=mean gsl.nil? ? nil : gsl.variance_with_fixed_mean(m) end def standard_deviation_population(m=nil) # :nodoc: - check_type :scale + check_type :numeric m||=mean gsl.nil? ? nil : gsl.sd_with_fixed_mean(m) end def skew # :nodoc: - check_type :scale + check_type :numeric gsl.nil? ? nil : gsl.skew end def kurtosis # :nodoc: - check_type :scale + check_type :numeric gsl.nil? ? nil : gsl.kurtosis end end diff --git a/test/test_analysis.rb b/test/test_analysis.rb index 55d5b6f..03ca19c 100644 --- a/test/test_analysis.rb +++ b/test/test_analysis.rb @@ -39,7 +39,7 @@ class StatsampleAnalysisTestCase < Minitest::Test should 'to_text returns the same as a normal ReportBuilder object' do rb = ReportBuilder.new(name: :test) section = ReportBuilder::Section.new(name: 'first') - a = [1, 2, 3].to_scale + a = [1, 2, 3].to_numeric section.add('first') section.add(a) rb.add(section) diff --git a/test/test_anova_contrast.rb b/test/test_anova_contrast.rb index f86ae0f..bf87f6f 100644 --- a/test/test_anova_contrast.rb +++ b/test/test_anova_contrast.rb @@ -2,10 +2,10 @@ class StatsampleAnovaContrastTestCase < Minitest::Test context(Statsample::Anova::Contrast) do setup do - constant = [12, 13, 11, 12, 12].to_scale - frequent = [9, 10, 9, 13, 14].to_scale - infrequent = [15, 16, 17, 16, 16].to_scale - never = [17, 18, 12, 18, 20].to_scale + constant = [12, 13, 11, 12, 12].to_numeric + frequent = [9, 10, 9, 13, 14].to_numeric + infrequent = [15, 16, 17, 16, 16].to_numeric + never = [17, 18, 12, 18, 20].to_numeric @vectors = [constant, frequent, infrequent, never] @c = Statsample::Anova::Contrast.new(vectors: @vectors) end diff --git a/test/test_anovatwowaywithdataset.rb b/test/test_anovatwowaywithdataset.rb index a9eecb7..ccbba17 100644 --- a/test/test_anovatwowaywithdataset.rb +++ b/test/test_anovatwowaywithdataset.rb @@ -4,7 +4,7 @@ class StatsampleAnovaTwoWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::TwoWayWithVectors) do setup do - @pa = [5, 4, 3, 4, 2, 18, 19, 14, 12, 15, 6, 7, 5, 8, 4, 6, 9, 5, 9, 3].to_scale + @pa = [5, 4, 3, 4, 2, 18, 19, 14, 12, 15, 6, 7, 5, 8, 4, 6, 9, 5, 9, 3].to_numeric @pa.name = 'Passive Avoidance' @a = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1].to_vector @a.labels = { 0 => '0%', 1 => '35%' } diff --git a/test/test_anovawithvectors.rb b/test/test_anovawithvectors.rb index 8793db8..ae38f66 100644 --- a/test/test_anovawithvectors.rb +++ b/test/test_anovawithvectors.rb @@ -3,9 +3,9 @@ class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::OneWayWithVectors) do context('when initializing') do setup do - @v1 = 10.times.map { rand(100) }.to_scale - @v2 = 10.times.map { rand(100) }.to_scale - @v3 = 10.times.map { rand(100) }.to_scale + @v1 = 10.times.map { rand(100) }.to_numeric + @v2 = 10.times.map { rand(100) }.to_numeric + @v3 = 10.times.map { rand(100) }.to_numeric end should 'be the same using [] or args*' do a1 = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3) @@ -28,9 +28,9 @@ class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test end end setup do - @v1 = [3, 3, 2, 3, 6].to_vector(:scale) - @v2 = [7, 6, 5, 6, 7].to_vector(:scale) - @v3 = [9, 8, 9, 7, 8].to_vector(:scale) + @v1 = [3, 3, 2, 3, 6].to_vector(:numeric) + @v2 = [7, 6, 5, 6, 7].to_vector(:numeric) + @v3 = [9, 8, 9, 7, 8].to_vector(:numeric) @name = 'Anova testing' @anova = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3, name: @name) end diff --git a/test/test_awesome_print_bug.rb b/test/test_awesome_print_bug.rb index f6db9f6..2d4df9d 100644 --- a/test/test_awesome_print_bug.rb +++ b/test/test_awesome_print_bug.rb @@ -5,7 +5,7 @@ class StatsampleAwesomePrintBug < Minitest::Test require 'awesome_print' end should 'should be flawless' do - a = [1, 2, 3].to_scale + a = [1, 2, 3].to_numeric assert(a != [1, 2, 3]) assert_nothing_raised do diff --git a/test/test_bartlettsphericity.rb b/test/test_bartlettsphericity.rb index 9a65b08..4c724fb 100644 --- a/test/test_bartlettsphericity.rb +++ b/test/test_bartlettsphericity.rb @@ -4,9 +4,9 @@ class StatsampleBartlettSphericityTestCase < Minitest::Test include Statsample::Test context Statsample::Test::BartlettSphericity do setup do - @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_scale - @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_scale - @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_scale + @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_numeric + @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_numeric + @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_numeric # KMO: 0.490 ds = { 'v1' => @v1, 'v2' => @v2, 'v3' => @v3 }.to_dataset cor = Statsample::Bivariate.correlation_matrix(ds) diff --git a/test/test_bivariate.rb b/test/test_bivariate.rb index be95370..82ea824 100644 --- a/test/test_bivariate.rb +++ b/test/test_bivariate.rb @@ -1,38 +1,38 @@ require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleBivariateTestCase < Minitest::Test should 'method sum of squares should be correct' do - v1 = [1, 2, 3, 4, 5, 6].to_vector(:scale) - v2 = [6, 2, 4, 10, 12, 8].to_vector(:scale) + v1 = [1, 2, 3, 4, 5, 6].to_vector(:numeric) + v2 = [6, 2, 4, 10, 12, 8].to_vector(:numeric) assert_equal(23.0, Statsample::Bivariate.sum_of_squares(v1, v2)) end should_with_gsl 'return same covariance with ruby and gls implementation' do - v1 = 20.times.collect { |_a| rand }.to_scale - v2 = 20.times.collect { |_a| rand }.to_scale + v1 = 20.times.collect { |_a| rand }.to_numeric + v2 = 20.times.collect { |_a| rand }.to_numeric assert_in_delta(Statsample::Bivariate.covariance(v1, v2), Statsample::Bivariate.covariance_slow(v1, v2), 0.001) end should_with_gsl 'return same correlation with ruby and gls implementation' do - v1 = 20.times.collect { |_a| rand }.to_scale - v2 = 20.times.collect { |_a| rand }.to_scale + v1 = 20.times.collect { |_a| rand }.to_numeric + v2 = 20.times.collect { |_a| rand }.to_numeric assert_in_delta(GSL::Stats.correlation(v1.gsl, v2.gsl), Statsample::Bivariate.pearson_slow(v1, v2), 1e-10) end should 'return correct pearson correlation' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) assert_in_delta(0.525, Statsample::Bivariate.pearson(v1, v2), 0.001) assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v1, v2), 0.001) - v3 = [6, 2, 1000, 1000, 5, 4, 7, 8, 4, 3, 2, nil].to_vector(:scale) - v4 = [2, nil, nil, nil, 3, 7, 8, 6, 4, 3, 2, 500].to_vector(:scale) + v3 = [6, 2, 1000, 1000, 5, 4, 7, 8, 4, 3, 2, nil].to_vector(:numeric) + v4 = [2, nil, nil, nil, 3, 7, 8, 6, 4, 3, 2, 500].to_vector(:numeric) assert_in_delta(0.525, Statsample::Bivariate.pearson(v3, v4), 0.001) # Test ruby method v3a, v4a = Statsample.only_valid v3, v4 assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v3a, v4a), 0.001) end should 'return correct values for t_pearson and prop_pearson' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) r = Statsample::Bivariate::Pearson.new(v1, v2) assert_in_delta(0.525, r.r, 0.001) assert_in_delta(Statsample::Bivariate.t_pearson(v1, v2), r.t, 0.001) @@ -40,10 +40,10 @@ class StatsampleBivariateTestCase < Minitest::Test assert(r.summary.size > 0) end should 'return correct correlation_matrix with nils values' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) - v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:scale) - v4 = [2, nil, nil, nil, 3, 7, 8, 6].to_vector(:scale) + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) + v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:numeric) + v4 = [2, nil, nil, nil, 3, 7, 8, 6].to_vector(:numeric) ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4 }.to_dataset c = proc { |n1, n2| Statsample::Bivariate.pearson(n1, n2) } expected = Matrix[[c.call(v1, v1), c.call(v1, v2), c.call(v1, v3), c.call(v1, v4)], [c.call(v2, v1), c.call(v2, v2), c.call(v2, v3), c.call(v2, v4)], [c.call(v3, v1), c.call(v3, v2), c.call(v3, v3), c.call(v3, v4)], @@ -61,11 +61,11 @@ class StatsampleBivariateTestCase < Minitest::Test end should_with_gsl 'return same values for optimized and pairwise covariance matrix' do cases = 100 - v1 = Statsample::Vector.new_scale(cases) { rand } - v2 = Statsample::Vector.new_scale(cases) { rand } - v3 = Statsample::Vector.new_scale(cases) { rand } - v4 = Statsample::Vector.new_scale(cases) { rand } - v5 = Statsample::Vector.new_scale(cases) { rand } + v1 = Statsample::Vector.new_numeric(cases) { rand } + v2 = Statsample::Vector.new_numeric(cases) { rand } + v3 = Statsample::Vector.new_numeric(cases) { rand } + v4 = Statsample::Vector.new_numeric(cases) { rand } + v5 = Statsample::Vector.new_numeric(cases) { rand } ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'v5' => v5 }.to_dataset @@ -76,11 +76,11 @@ class StatsampleBivariateTestCase < Minitest::Test end should_with_gsl 'return same values for optimized and pairwise correlation matrix' do cases = 100 - v1 = Statsample::Vector.new_scale(cases) { rand } - v2 = Statsample::Vector.new_scale(cases) { rand } - v3 = Statsample::Vector.new_scale(cases) { rand } - v4 = Statsample::Vector.new_scale(cases) { rand } - v5 = Statsample::Vector.new_scale(cases) { rand } + v1 = Statsample::Vector.new_numeric(cases) { rand } + v2 = Statsample::Vector.new_numeric(cases) { rand } + v3 = Statsample::Vector.new_numeric(cases) { rand } + v4 = Statsample::Vector.new_numeric(cases) { rand } + v5 = Statsample::Vector.new_numeric(cases) { rand } ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'v5' => v5 }.to_dataset @@ -90,10 +90,10 @@ class StatsampleBivariateTestCase < Minitest::Test assert_equal_matrix(cor_opt, cor_pw, 1e-15) end should 'return correct correlation_matrix without nils values' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:scale) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:scale) - v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:scale) - v4 = [2, 4, 6, 7, 3, 7, 8, 6].to_vector(:scale) + v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) + v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) + v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:numeric) + v4 = [2, 4, 6, 7, 3, 7, 8, 6].to_vector(:numeric) ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4 }.to_dataset c = proc { |n1, n2| Statsample::Bivariate.pearson(n1, n2) } expected = Matrix[[c.call(v1, v1), c.call(v1, v2), c.call(v1, v3), c.call(v1, v4)], [c.call(v2, v1), c.call(v2, v2), c.call(v2, v3), c.call(v2, v4)], [c.call(v3, v1), c.call(v3, v2), c.call(v3, v3), c.call(v3, v4)], @@ -129,25 +129,25 @@ class StatsampleBivariateTestCase < Minitest::Test end should "return correct value for Spearman's rho" do - v1 = [86, 97, 99, 100, 101, 103, 106, 110, 112, 113].to_vector(:scale) - v2 = [0, 20, 28, 27, 50, 29, 7, 17, 6, 12].to_vector(:scale) + v1 = [86, 97, 99, 100, 101, 103, 106, 110, 112, 113].to_vector(:numeric) + v2 = [0, 20, 28, 27, 50, 29, 7, 17, 6, 12].to_vector(:numeric) assert_in_delta(-0.175758, Statsample::Bivariate.spearman(v1, v2), 0.0001) end should 'return correct value for point_biserial correlation' do - c = [1, 3, 5, 6, 7, 100, 200, 300, 400, 300].to_vector(:scale) - d = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0].to_vector(:scale) + c = [1, 3, 5, 6, 7, 100, 200, 300, 400, 300].to_vector(:numeric) + d = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0].to_vector(:numeric) assert_raises TypeError do Statsample::Bivariate.point_biserial(c, d) end assert_in_delta(Statsample::Bivariate.point_biserial(d, c), Statsample::Bivariate.pearson(d, c), 0.0001) end should 'return correct value for tau_a and tau_b' do - v1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].to_vector(:ordinal) - v2 = [1, 3, 4, 5, 7, 8, 2, 9, 10, 6, 11].to_vector(:ordinal) + v1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].to_vector(:numeric) + v2 = [1, 3, 4, 5, 7, 8, 2, 9, 10, 6, 11].to_vector(:numeric) assert_in_delta(0.6727, Statsample::Bivariate.tau_a(v1, v2), 0.001) assert_in_delta(0.6727, Statsample::Bivariate.tau_b((Statsample::Crosstab.new(v1, v2).to_matrix)), 0.001) - v1 = [12, 14, 14, 17, 19, 19, 19, 19, 19, 20, 21, 21, 21, 21, 21, 22, 23, 24, 24, 24, 26, 26, 27].to_vector(:ordinal) - v2 = [11, 4, 4, 2, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0].to_vector(:ordinal) + v1 = [12, 14, 14, 17, 19, 19, 19, 19, 19, 20, 21, 21, 21, 21, 21, 22, 23, 24, 24, 24, 26, 26, 27].to_vector(:numeric) + v2 = [11, 4, 4, 2, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0].to_vector(:numeric) assert_in_delta(-0.376201540231705, Statsample::Bivariate.tau_b(Statsample::Crosstab.new(v1, v2).to_matrix), 0.001) end should 'return correct value for gamma correlation' do diff --git a/test/test_crosstab.rb b/test/test_crosstab.rb index c926bb2..28ab370 100644 --- a/test/test_crosstab.rb +++ b/test/test_crosstab.rb @@ -58,8 +58,8 @@ def test_expected end def test_crosstab_with_scale - v1 = %w(1 1 1 1 1 0 0 0 0 0).to_scale - v2 = %w(0 0 0 0 0 1 1 1 1 1).to_scale + v1 = %w(1 1 1 1 1 0 0 0 0 0).to_numeric + v2 = %w(0 0 0 0 0 1 1 1 1 1).to_numeric ct = Statsample::Crosstab.new(v1, v2) assert_equal(Matrix[[0, 5], [5, 0]], ct.to_matrix) assert_nothing_raised { ct.summary } diff --git a/test/test_csv.rb b/test/test_csv.rb index 86fd7cf..ea92025 100644 --- a/test/test_csv.rb +++ b/test/test_csv.rb @@ -8,11 +8,11 @@ def setup def test_read header = %w(id name age city a1) data = { - 'id' => [1, 2, 3, 4, 5, 6].to_vector(:scale), - 'name' => %w(Alex Claude Peter Franz George Fernand).to_vector(:nominal), - 'age' => [20, 23, 25, 27, 5.5, nil].to_vector(:scale), - 'city' => ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:nominal), - 'a1' => ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:nominal) + 'id' => [1, 2, 3, 4, 5, 6].to_vector(:numeric), + 'name' => %w(Alex Claude Peter Franz George Fernand).to_vector(:object), + 'age' => [20, 23, 25, 27, 5.5, nil].to_vector(:numeric), + 'city' => ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:object), + 'a1' => ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:object) } ds_exp = Statsample::Dataset.new(data, header) @@ -34,7 +34,7 @@ def test_nil def test_repeated ds = Statsample::CSV.read('test/fixtures/repeated_fields.csv') assert_equal(%w(id name_1 age_1 city a1 name_2 age_2), ds.fields) - age = [3, 4, 5, 6, nil, 8].to_vector(:scale) + age = [3, 4, 5, 6, nil, 8].to_vector(:numeric) assert_equal(age, ds['age_2']) end diff --git a/test/test_dataset.rb b/test/test_dataset.rb index 411bd2c..6e1b240 100644 --- a/test/test_dataset.rb +++ b/test/test_dataset.rb @@ -58,9 +58,9 @@ def test_fields end def test_merge - a = [1, 2, 3].to_scale + a = [1, 2, 3].to_numeric b = [3, 4, 5].to_vector - c = [4, 5, 6].to_scale + c = [4, 5, 6].to_numeric d = [7, 8, 9].to_vector e = [10, 20, 30].to_vector ds1 = { 'a' => a, 'b' => b }.to_dataset @@ -117,51 +117,51 @@ def test_add_vector end def test_vector_by_calculation - a1 = [1, 2, 3, 4, 5, 6, 7].to_vector(:scale) - a2 = [10, 20, 30, 40, 50, 60, 70].to_vector(:scale) - a3 = [100, 200, 300, 400, 500, 600, 700].to_vector(:scale) + a1 = [1, 2, 3, 4, 5, 6, 7].to_vector(:numeric) + a2 = [10, 20, 30, 40, 50, 60, 70].to_vector(:numeric) + a3 = [100, 200, 300, 400, 500, 600, 700].to_vector(:numeric) ds = { 'a1' => a1, 'a2' => a2, 'a3' => a3 }.to_dataset total = ds.vector_by_calculation {|row| row['a1'] + row['a2'] + row['a3'] } - expected = [111, 222, 333, 444, 555, 666, 777].to_vector(:scale) + expected = [111, 222, 333, 444, 555, 666, 777].to_vector(:numeric) assert_equal(expected, total) end def test_vector_sum - a1 = [1, 2, 3, 4, 5, nil].to_vector(:scale) - a2 = [10, 10, 20, 20, 20, 30].to_vector(:scale) - b1 = [nil, 1, 1, 1, 1, 2].to_vector(:scale) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) + a1 = [1, 2, 3, 4, 5, nil].to_vector(:numeric) + a2 = [10, 10, 20, 20, 20, 30].to_vector(:numeric) + b1 = [nil, 1, 1, 1, 1, 2].to_vector(:numeric) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2 }.to_dataset total = ds.vector_sum a = ds.vector_sum(%w(a1 a2)) b = ds.vector_sum(%w(b1 b2)) - expected_a = [11, 12, 23, 24, 25, nil].to_vector(:scale) - expected_b = [nil, 3, 3, nil, 3, 5].to_vector(:scale) - expected_total = [nil, 15, 26, nil, 28, nil].to_vector(:scale) + expected_a = [11, 12, 23, 24, 25, nil].to_vector(:numeric) + expected_b = [nil, 3, 3, nil, 3, 5].to_vector(:numeric) + expected_total = [nil, 15, 26, nil, 28, nil].to_vector(:numeric) assert_equal(expected_a, a) assert_equal(expected_b, b) assert_equal(expected_total, total) end def test_vector_missing_values - a1 = [1, nil, 3, 4, 5, nil].to_vector(:scale) - a2 = [10, nil, 20, 20, 20, 30].to_vector(:scale) - b1 = [nil, nil, 1, 1, 1, 2].to_vector(:scale) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) - c = [nil, 2, 4, 2, 2, 2].to_vector(:scale) + a1 = [1, nil, 3, 4, 5, nil].to_vector(:numeric) + a2 = [10, nil, 20, 20, 20, 30].to_vector(:numeric) + b1 = [nil, nil, 1, 1, 1, 2].to_vector(:numeric) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) + c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset - mva = [2, 3, 0, 1, 0, 1].to_vector(:scale) + mva = [2, 3, 0, 1, 0, 1].to_vector(:numeric) assert_equal(mva, ds.vector_missing_values) end def test_has_missing_values - a1 = [1, nil, 3, 4, 5, nil].to_vector(:scale) - a2 = [10, nil, 20, 20, 20, 30].to_vector(:scale) - b1 = [nil, nil, 1, 1, 1, 2].to_vector(:scale) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) - c = [nil, 2, 4, 2, 2, 2].to_vector(:scale) + a1 = [1, nil, 3, 4, 5, nil].to_vector(:numeric) + a2 = [10, nil, 20, 20, 20, 30].to_vector(:numeric) + b1 = [nil, nil, 1, 1, 1, 2].to_vector(:numeric) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) + c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset assert(ds.has_missing_data?) clean = ds.dup_only_valid @@ -169,31 +169,31 @@ def test_has_missing_values end def test_vector_count_characters - a1 = [1, 'abcde', 3, 4, 5, nil].to_vector(:scale) - a2 = [10, 20.3, 20, 20, 20, 30].to_vector(:scale) - b1 = [nil, '343434', 1, 1, 1, 2].to_vector(:scale) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) - c = [nil, 2, 'This is a nice example', 2, 2, 2].to_vector(:scale) + a1 = [1, 'abcde', 3, 4, 5, nil].to_vector(:numeric) + a2 = [10, 20.3, 20, 20, 20, 30].to_vector(:numeric) + b1 = [nil, '343434', 1, 1, 1, 2].to_vector(:numeric) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) + c = [nil, 2, 'This is a nice example', 2, 2, 2].to_vector(:numeric) ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset - exp = [4, 17, 27, 5, 6, 5].to_vector(:scale) + exp = [4, 17, 27, 5, 6, 5].to_vector(:numeric) assert_equal(exp, ds.vector_count_characters) end def test_vector_mean - a1 = [1, 2, 3, 4, 5, nil].to_vector(:scale) - a2 = [10, 10, 20, 20, 20, 30].to_vector(:scale) - b1 = [nil, 1, 1, 1, 1, 2].to_vector(:scale) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:scale) - c = [nil, 2, 4, 2, 2, 2].to_vector(:scale) + a1 = [1, 2, 3, 4, 5, nil].to_vector(:numeric) + a2 = [10, 10, 20, 20, 20, 30].to_vector(:numeric) + b1 = [nil, 1, 1, 1, 1, 2].to_vector(:numeric) + b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) + c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset total = ds.vector_mean a = ds.vector_mean(%w(a1 a2), 1) b = ds.vector_mean(%w(b1 b2), 1) c = ds.vector_mean(%w(b1 b2 c), 1) - expected_a = [5.5, 6, 11.5, 12, 12.5, 30].to_vector(:scale) - expected_b = [2, 1.5, 1.5, 1, 1.5, 2.5].to_vector(:scale) - expected_c = [nil, 5.0 / 3, 7.0 / 3, 1.5, 5.0 / 3, 7.0 / 3].to_vector(:scale) - expected_total = [nil, 3.4, 6, nil, 6.0, nil].to_vector(:scale) + expected_a = [5.5, 6, 11.5, 12, 12.5, 30].to_vector(:numeric) + expected_b = [2, 1.5, 1.5, 1, 1.5, 2.5].to_vector(:numeric) + expected_c = [nil, 5.0 / 3, 7.0 / 3, 1.5, 5.0 / 3, 7.0 / 3].to_vector(:numeric) + expected_total = [nil, 3.4, 6, nil, 6.0, nil].to_vector(:numeric) assert_equal(expected_a, a) assert_equal(expected_b, b) assert_equal(expected_c, c) @@ -210,9 +210,9 @@ def test_each_array end def test_recode - @ds['age'].type = :scale + @ds['age'].type = :numeric @ds.recode!('age') { |c| c['id'] * 2 } - expected = [2, 4, 6, 8, 10].to_vector(:scale) + expected = [2, 4, 6, 8, 10].to_vector(:numeric) assert_equal(expected, @ds['age']) end @@ -231,8 +231,8 @@ def test_delete_vector end def test_change_type - @ds.col('age').type = :scale - assert_equal(:scale, @ds.col('age').type) + @ds.col('age').type = :numeric + assert_equal(:numeric, @ds.col('age').type) end def test_split_by_separator_recode @@ -255,13 +255,13 @@ def test_split_by_separator end def test_percentiles - v1 = (1..100).to_a.to_scale + v1 = (1..100).to_a.to_numeric assert_equal(50.5, v1.median) assert_equal(25.5, v1.percentil(25)) - v2 = (1..99).to_a.to_scale + v2 = (1..99).to_a.to_numeric assert_equal(50, v2.median) assert_equal(25, v2.percentil(25)) - v3 = (1..50).to_a.to_scale + v3 = (1..50).to_a.to_numeric assert_equal(25.5, v3.median) assert_equal(13, v3.percentil(25)) end @@ -336,7 +336,7 @@ def test_dup assert_equal(ds1.fields, ds2.fields) assert_not_same(ds1.fields, ds2.fields) - ds1['v1'].type = :scale + ds1['v1'].type = :numeric # dup partial ds3 = ds1.dup('v1') ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) @@ -355,7 +355,7 @@ def test_dup assert_not_equal(ds1['v1'], ds3['v1']) assert_equal([], ds3['v1'].data) assert_equal([], ds3['v2'].data) - assert_equal(:scale, ds3['v1'].type) + assert_equal(:numeric, ds3['v1'].type) assert_equal(ds1.fields, ds2.fields) assert_not_same(ds1.fields, ds2.fields) end @@ -368,9 +368,9 @@ def test_from_to end def test_each_array_with_nils - v1 = [1, -99, 3, 4, 'na'].to_vector(:scale, missing_values: [-99, 'na']) - v2 = [5, 6, -99, 8, 20].to_vector(:scale, missing_values: [-99]) - v3 = [9, 10, 11, 12, 20].to_vector(:scale, missing_values: [-99]) + v1 = [1, -99, 3, 4, 'na'].to_vector(:numeric, missing_values: [-99, 'na']) + v2 = [5, 6, -99, 8, 20].to_vector(:numeric, missing_values: [-99]) + v3 = [9, 10, 11, 12, 20].to_vector(:numeric, missing_values: [-99]) ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) ds2 = ds1.dup_empty ds1.each_array_with_nils {|row| @@ -382,40 +382,40 @@ def test_each_array_with_nils end def test_dup_only_valid - v1 = [1, nil, 3, 4].to_vector(:scale) - v2 = [5, 6, nil, 8].to_vector(:scale) - v3 = [9, 10, 11, 12].to_vector(:scale) + v1 = [1, nil, 3, 4].to_vector(:numeric) + v2 = [5, 6, nil, 8].to_vector(:numeric) + v3 = [9, 10, 11, 12].to_vector(:numeric) ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) ds2 = ds1.dup_only_valid - expected = Statsample::Dataset.new('v1' => [1, 4].to_vector(:scale), 'v2' => [5, 8].to_vector(:scale), 'v3' => [9, 12].to_vector(:scale)) + expected = Statsample::Dataset.new('v1' => [1, 4].to_vector(:numeric), 'v2' => [5, 8].to_vector(:numeric), 'v3' => [9, 12].to_vector(:numeric)) assert_equal(expected, ds2) assert_equal(expected.vectors.values, Statsample.only_valid(v1, v2, v3)) - expected_partial = Statsample::Dataset.new('v1' => [1, 3, 4].to_vector(:scale), 'v3' => [9, 11, 12].to_vector(:scale)) + expected_partial = Statsample::Dataset.new('v1' => [1, 3, 4].to_vector(:numeric), 'v3' => [9, 11, 12].to_vector(:numeric)) assert_equal(expected_partial, ds1.dup_only_valid(%w(v1 v3))) end def test_filter - @ds['age'].type = :scale + @ds['age'].type = :numeric filtered = @ds.filter { |c| c['id'] == 2 or c['id'] == 4 } - expected = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([2, 4]), 'name' => Statsample::Vector.new(%w(Claude Franz)), 'age' => Statsample::Vector.new([23, 27], :scale), + expected = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([2, 4]), 'name' => Statsample::Vector.new(%w(Claude Franz)), 'age' => Statsample::Vector.new([23, 27], :numeric), 'city' => Statsample::Vector.new(%w(London Paris)), 'a1' => Statsample::Vector.new(['b,c', nil]) }, %w(id name age city a1)) assert_equal(expected, filtered) end def test_filter_field - @ds['age'].type = :scale + @ds['age'].type = :numeric filtered = @ds.filter_field('id') { |c| c['id'] == 2 or c['id'] == 4 } expected = [2, 4].to_vector assert_equal(expected, filtered) end def test_verify - name = %w(r1 r2 r3 r4).to_vector(:nominal) - v1 = [1, 2, 3, 4].to_vector(:scale) - v2 = [4, 3, 2, 1].to_vector(:scale) - v3 = [10, 20, 30, 40].to_vector(:scale) - v4 = %w(a b a b).to_vector(:nominal) + name = %w(r1 r2 r3 r4).to_vector(:object) + v1 = [1, 2, 3, 4].to_vector(:numeric) + v2 = [4, 3, 2, 1].to_vector(:numeric) + v3 = [10, 20, 30, 40].to_vector(:numeric) + v4 = %w(a b a b).to_vector(:object) ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'id' => name }.to_dataset ds.fields = %w(v1 v2 v3 v4 id) # Correct @@ -432,14 +432,14 @@ def test_verify end def test_compute_operation - v1 = [1, 2, 3, 4].to_vector(:scale) - v2 = [4, 3, 2, 1].to_vector(:scale) - v3 = [10, 20, 30, 40].to_vector(:scale) - vscale = [1.quo(2), 1, 3.quo(2), 2].to_vector(:scale) - vsum = [1 + 4 + 10.0, 2 + 3 + 20.0, 3 + 2 + 30.0, 4 + 1 + 40.0].to_vector(:scale) - vmult = [1 * 4, 2 * 3, 3 * 2, 4 * 1].to_vector(:scale) + v1 = [1, 2, 3, 4].to_vector(:numeric) + v2 = [4, 3, 2, 1].to_vector(:numeric) + v3 = [10, 20, 30, 40].to_vector(:numeric) + vnumeric = [1.quo(2), 1, 3.quo(2), 2].to_vector(:numeric) + vsum = [1 + 4 + 10.0, 2 + 3 + 20.0, 3 + 2 + 30.0, 4 + 1 + 40.0].to_vector(:numeric) + vmult = [1 * 4, 2 * 3, 3 * 2, 4 * 1].to_vector(:numeric) ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3 }.to_dataset - assert_equal(vscale, ds.compute('v1/2')) + assert_equal(vnumeric, ds.compute('v1/2')) assert_equal(vsum, ds.compute('v1+v2+v3')) assert_equal(vmult, ds.compute('v1*v2')) end @@ -447,15 +447,15 @@ def test_compute_operation def test_crosstab_with_asignation v1 = %w(a a a b b b c c c).to_vector v2 = %w(a b c a b c a b c).to_vector - v3 = %w(0 1 0 0 1 1 0 0 1).to_scale + v3 = %w(0 1 0 0 1 1 0 0 1).to_numeric ds = Statsample::Dataset.crosstab_by_asignation(v1, v2, v3) - assert_equal(:nominal, ds['_id'].type) - assert_equal(:scale, ds['a'].type) - assert_equal(:scale, ds['b'].type) + assert_equal(:object, ds['_id'].type) + assert_equal(:numeric, ds['a'].type) + assert_equal(:numeric, ds['b'].type) ev_id = %w(a b c).to_vector - ev_a = %w(0 0 0).to_scale - ev_b = %w(1 1 0).to_scale - ev_c = %w(0 1 1).to_scale + ev_a = %w(0 0 0).to_numeric + ev_b = %w(1 1 0).to_numeric + ev_c = %w(0 1 1).to_numeric ds2 = { '_id' => ev_id, 'a' => ev_a, 'b' => ev_b, 'c' => ev_c }.to_dataset assert_equal(ds, ds2) end @@ -472,7 +472,7 @@ def test_one_to_many ids = %w(1 1 2 2 2).to_vector colors = %w(red blue green orange white).to_vector values = [10, 20, 15, 30, 20].to_vector - col_ids = [1, 2, 1, 2, 3].to_scale + col_ids = [1, 2, 1, 2, 3].to_numeric ds_expected = { 'id' => ids, '_col_id' => col_ids, 'color' => colors, 'value' => values }.to_dataset(%w(id _col_id color value)) assert_equal(ds_expected, ds.one_to_many(%w(id), 'car_%v%n')) end diff --git a/test/test_factor.rb b/test/test_factor.rb index 5e0b697..9494f2c 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -18,8 +18,8 @@ def test_covariance_matrix pca = Statsample::Factor::PCA.new(cm, m: 6) # puts pca.summary # puts pca.feature_matrix - exp_eig = [2.985, 0.931, 0.242, 0.194, 0.085, 0.035].to_scale - assert_similar_vector(exp_eig, pca.eigenvalues.to_scale, 0.1) + exp_eig = [2.985, 0.931, 0.242, 0.194, 0.085, 0.035].to_numeric + assert_similar_vector(exp_eig, pca.eigenvalues.to_numeric, 0.1) pcs = pca.principal_components(ds) k = 6 comp_matrix = pca.component_matrix @@ -42,9 +42,9 @@ def test_principalcomponents_ruby_gsl samples = 20 [3, 5, 7].each {|k| v = {} - v['x0'] = samples.times.map { ran.call }.to_scale.centered + v['x0'] = samples.times.map { ran.call }.to_numeric.centered (1...k).each {|i| - v["x#{i}"] = samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}"][ii] * 0.5 }.to_scale.centered + v["x#{i}"] = samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}"][ii] * 0.5 }.to_numeric.centered } ds = v.to_dataset @@ -87,8 +87,8 @@ def test_principalcomponents def principalcomponents(gsl) ran = Distribution::Normal.rng samples = 50 - x1 = samples.times.map { ran.call }.to_scale - x2 = samples.times.map { |i| ran.call * 0.5 + x1[i] * 0.5 }.to_scale + x1 = samples.times.map { ran.call }.to_numeric + x2 = samples.times.map { |i| ran.call * 0.5 + x1[i] * 0.5 }.to_numeric ds = { 'x1' => x1, 'x2' => x2 }.to_dataset cm = ds.correlation_matrix @@ -121,9 +121,9 @@ def test_antiimage end def test_kmo - @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_scale - @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_scale - @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_scale + @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_numeric + @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_numeric + @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_numeric # KMO: 0.490 ds = { 'v1' => @v1, 'v2' => @v2, 'v3' => @v3 }.to_dataset cor = Statsample::Bivariate.correlation_matrix(ds) @@ -142,8 +142,8 @@ def test_kmo_univariate # Tested with SPSS and R def test_pca - a = [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_scale - b = [2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9].to_scale + a = [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_numeric + b = [2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9].to_numeric a.recode! { |c| c - a.mean } b.recode! { |c| c - b.mean } ds = { 'a' => a, 'b' => b }.to_dataset diff --git a/test/test_factor_pa.rb b/test/test_factor_pa.rb index bc16ea2..8d7cbbf 100644 --- a/test/test_factor_pa.rb +++ b/test/test_factor_pa.rb @@ -15,18 +15,18 @@ def test_parallelanalysis_with_data variables = 10 iterations = 50 rng = Distribution::Normal.rng - f1 = samples.times.collect { rng.call }.to_scale - f2 = samples.times.collect { rng.call }.to_scale + f1 = samples.times.collect { rng.call }.to_numeric + f2 = samples.times.collect { rng.call }.to_numeric vectors = {} variables.times do |i| if i < 5 vectors["v#{i}"] = samples.times.collect {|nv| f1[nv] * 5 + f2[nv] * 2 + rng.call - }.to_scale + }.to_numeric else vectors["v#{i}"] = samples.times.collect {|nv| f2[nv] * 5 + f1[nv] * 2 + rng.call - }.to_scale + }.to_numeric end end ds = vectors.to_dataset diff --git a/test/test_ggobi.rb b/test/test_ggobi.rb index 88bcd96..1d67fb2 100644 --- a/test/test_ggobi.rb +++ b/test/test_ggobi.rb @@ -2,10 +2,10 @@ require 'ostruct' class StatsampleGGobiTestCase < Minitest::Test def setup - v1 = ([10.2, 20.3, 10, 20, 30, 40, 30, 20, 30, 40] * 10).to_vector(:scale) - @v2 = (%w(a b c a a a b b c d) * 10).to_vector(:nominal) + v1 = ([10.2, 20.3, 10, 20, 30, 40, 30, 20, 30, 40] * 10).to_vector(:numeric) + @v2 = (%w(a b c a a a b b c d) * 10).to_vector(:object) @v2.labels = { 'a' => 'letter a', 'd' => 'letter d' } - v3 = ([1, 2, 3, 4, 5, 4, 3, 2, 1, 2] * 10).to_vector(:ordinal) + v3 = ([1, 2, 3, 4, 5, 4, 3, 2, 1, 2] * 10).to_vector(:numeric) @ds = { 'v1' => v1, 'v2' => @v2, 'v3' => v3 }.to_dataset end diff --git a/test/test_gsl.rb b/test/test_gsl.rb index 800c484..a344523 100644 --- a/test/test_gsl.rb +++ b/test/test_gsl.rb @@ -1,9 +1,9 @@ require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleGSLTestCase < Minitest::Test should_with_gsl 'matrix with gsl' do - a = [1, 2, 3, 4, 20].to_vector(:scale) - b = [3, 2, 3, 4, 50].to_vector(:scale) - c = [6, 2, 3, 4, 3].to_vector(:scale) + a = [1, 2, 3, 4, 20].to_vector(:numeric) + b = [3, 2, 3, 4, 50].to_vector(:numeric) + c = [6, 2, 3, 4, 3].to_vector(:numeric) ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset gsl = ds.to_matrix.to_gsl assert_equal(5, gsl.size1) diff --git a/test/test_histogram.rb b/test/test_histogram.rb index b68809f..eb0a3e1 100644 --- a/test/test_histogram.rb +++ b/test/test_histogram.rb @@ -75,13 +75,13 @@ class StatsampleHistogramTestCase < Minitest::Test assert_equal(min, h.min_val) end should 'return correct estimated mean' do - a = [1.5, 1.5, 1.5, 3.5, 3.5, 3.5].to_scale + a = [1.5, 1.5, 1.5, 3.5, 3.5, 3.5].to_numeric h = Statsample::Histogram.alloc(5, [0, 5]) h.increment(a) assert_equal(2.5, h.estimated_mean) end should 'return correct estimated standard deviation' do - a = [0.5, 1.5, 1.5, 1.5, 2.5, 3.5, 3.5, 3.5, 4.5].to_scale + a = [0.5, 1.5, 1.5, 1.5, 2.5, 3.5, 3.5, 3.5, 4.5].to_numeric h = Statsample::Histogram.alloc(5, [0, 5]) h.increment(a) assert_equal(a.sd, h.estimated_standard_deviation) @@ -100,7 +100,7 @@ class StatsampleHistogramTestCase < Minitest::Test end should 'not raise exception when all values equal' do assert_nothing_raised do - a = [5, 5, 5, 5, 5, 5].to_scale + a = [5, 5, 5, 5, 5, 5].to_numeric h = Statsample::Graph::Histogram.new(a) h.to_svg end diff --git a/test/test_matrix.rb b/test/test_matrix.rb index 65da751..ac99f0d 100644 --- a/test/test_matrix.rb +++ b/test/test_matrix.rb @@ -7,8 +7,8 @@ def test_to_dataset m.fields_y = %w(x1 x2) m.name = 'test' samples = 100 - x1 = [1, 2, 3].to_scale - x2 = [4, 5, 6].to_scale + x1 = [1, 2, 3].to_numeric + x2 = [4, 5, 6].to_numeric ds = { 'x1' => x1, 'x2' => x2 }.to_dataset ds.name = 'test' obs = m.to_dataset @@ -33,9 +33,9 @@ def test_covariate assert_equal(:covariance, a._type) - a = 50.times.collect { rand }.to_scale - b = 50.times.collect { rand }.to_scale - c = 50.times.collect { rand }.to_scale + a = 50.times.collect { rand }.to_numeric + b = 50.times.collect { rand }.to_numeric + c = 50.times.collect { rand }.to_numeric ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset corr = Statsample::Bivariate.correlation_matrix(ds) real = Statsample::Bivariate.covariance_matrix(ds).correlation diff --git a/test/test_multiset.rb b/test/test_multiset.rb index d443cff..43c5c27 100644 --- a/test/test_multiset.rb +++ b/test/test_multiset.rb @@ -3,8 +3,8 @@ class StatsampleMultisetTestCase < Minitest::Test def setup @x = %w(a a a a b b b b).to_vector - @y = [1, 2, 3, 4, 5, 6, 7, 8].to_scale - @z = [10, 11, 12, 13, 14, 15, 16, 17].to_scale + @y = [1, 2, 3, 4, 5, 6, 7, 8].to_numeric + @z = [10, 11, 12, 13, 14, 15, 16, 17].to_numeric @ds = { 'x' => @x, 'y' => @y, 'z' => @z }.to_dataset @ms = @ds.to_multiset_by_split('x') end @@ -44,9 +44,9 @@ def test_creation_empty end def test_to_multiset_by_split_one - sex = %w(m m m m m f f f f m).to_vector(:nominal) - city = %w(London Paris NY London Paris NY London Paris NY Tome).to_vector(:nominal) - age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:scale) + sex = %w(m m m m m f f f f m).to_vector(:object) + city = %w(London Paris NY London Paris NY London Paris NY Tome).to_vector(:object) + age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:numeric) ds = { 'sex' => sex, 'city' => city, 'age' => age }.to_dataset ms = ds.to_multiset_by_split('sex') assert_equal(2, ms.n_datasets) @@ -58,10 +58,10 @@ def test_to_multiset_by_split_one end def test_to_multiset_by_split_multiple - sex = %w(m m m m m m m m m m f f f f f f f f f f).to_vector(:nominal) - city = %w(London London London Paris Paris London London London Paris Paris London London London Paris Paris London London London Paris Paris).to_vector(:nominal) - hair = %w(blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black).to_vector(:nominal) - age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40, 10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:scale) + sex = %w(m m m m m m m m m m f f f f f f f f f f).to_vector(:object) + city = %w(London London London Paris Paris London London London Paris Paris London London London Paris Paris London London London Paris Paris).to_vector(:object) + hair = %w(blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black).to_vector(:object) + age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40, 10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:numeric) ds = { 'sex' => sex, 'city' => city, 'hair' => hair, 'age' => age }.to_dataset(%w(sex city hair age)) ms = ds.to_multiset_by_split('sex', 'city', 'hair') assert_equal(8, ms.n_datasets) @@ -84,8 +84,8 @@ def test_stratum_proportion end def test_stratum_scale - boys = { 'test' => [50, 55, 60, 62, 62, 65, 67, 67, 70, 70, 73, 73, 75, 78, 78, 80, 85, 90].to_vector(:scale) }.to_dataset - girls = { 'test' => [70, 70, 72, 72, 75, 75, 78, 78, 80, 80, 82, 82, 85, 85, 88, 88, 90, 90].to_vector(:scale) }.to_dataset + boys = { 'test' => [50, 55, 60, 62, 62, 65, 67, 67, 70, 70, 73, 73, 75, 78, 78, 80, 85, 90].to_vector(:numeric) }.to_dataset + girls = { 'test' => [70, 70, 72, 72, 75, 75, 78, 78, 80, 80, 82, 82, 85, 85, 88, 88, 90, 90].to_vector(:numeric) }.to_dataset ms = Statsample::Multiset.new(['test']) ms.add_dataset('boys', boys) ms.add_dataset('girls', girls) @@ -106,12 +106,12 @@ def test_each 'b' => %w(b b b b).to_vector } ype = { - 'a' => [1, 2, 3, 4].to_scale, - 'b' => [5, 6, 7, 8].to_scale + 'a' => [1, 2, 3, 4].to_numeric, + 'b' => [5, 6, 7, 8].to_numeric } zpe = { - 'a' => [10, 11, 12, 13].to_scale, - 'b' => [14, 15, 16, 17].to_scale + 'a' => [10, 11, 12, 13].to_numeric, + 'b' => [14, 15, 16, 17].to_numeric } xp, yp, zp = {}, {}, {} @ms.each {|k, ds| @@ -127,9 +127,9 @@ def test_each def test_multiset_union_with_block r1 = rand r2 = rand - ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_scale + ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_numeric - ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_scale + ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_numeric ds2 = @ms.union {|k, ds| ds['y'].recode!{|v| @@ -146,9 +146,9 @@ def test_multiset_union_with_block def test_multiset_union r1 = rand r2 = rand - ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_scale + ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_numeric - ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_scale + ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_numeric @ms.each {|k, ds| ds['y'].recode!{|v| k == 'a' ? v * r1 : v * r2 diff --git a/test/test_regression.rb b/test/test_regression.rb index db70fba..d973e79 100644 --- a/test/test_regression.rb +++ b/test/test_regression.rb @@ -3,9 +3,9 @@ class StatsampleRegressionTestCase < Minitest::Test context 'Example with missing data' do setup do - @x = [0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857].to_scale + @x = [0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857].to_numeric - @y = [nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_scale + @y = [nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_numeric @ds = { 'x' => @x, 'y' => @y }.to_dataset @lr = Statsample::Regression::Multiple::RubyEngine.new(@ds, 'y') end @@ -26,10 +26,10 @@ class StatsampleRegressionTestCase < Minitest::Test a, b = rand, rand - x1 = samples.times.map { rand }.to_scale - x2 = samples.times.map { rand }.to_scale - x3 = samples.times.map { |i| x1[i] * (1 + a) + x2[i] * (1 + b) }.to_scale - y = samples.times.map { |i| x1[i] + x2[i] + x3[i] + rand }.to_scale + x1 = samples.times.map { rand }.to_numeric + x2 = samples.times.map { rand }.to_numeric + x3 = samples.times.map { |i| x1[i] * (1 + a) + x2[i] * (1 + b) }.to_numeric + y = samples.times.map { |i| x1[i] + x2[i] + x3[i] + rand }.to_numeric ds = { 'x1' => x1, 'x2' => x2, 'x3' => x3, 'y' => y }.to_dataset @@ -38,8 +38,8 @@ class StatsampleRegressionTestCase < Minitest::Test } end def test_parameters - @x = [13, 20, 10, 33, 15].to_vector(:scale) - @y = [23, 18, 35, 10, 27].to_vector(:scale) + @x = [13, 20, 10, 33, 15].to_vector(:numeric) + @y = [23, 18, 35, 10, 27].to_vector(:numeric) reg = Statsample::Regression::Simple.new_from_vectors(@x, @y) _test_simple_regression(reg) ds = { 'x' => @x, 'y' => @y }.to_dataset @@ -57,9 +57,9 @@ def _test_simple_regression(reg) end def test_summaries - a = 10.times.map { rand(100) }.to_scale - b = 10.times.map { rand(100) }.to_scale - y = 10.times.map { rand(100) }.to_scale + a = 10.times.map { rand(100) }.to_numeric + b = 10.times.map { rand(100) }.to_numeric + y = 10.times.map { rand(100) }.to_numeric ds = { 'a' => a, 'b' => b, 'y' => y }.to_dataset lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') assert(lr.summary.size > 0) @@ -87,10 +87,10 @@ def test_multiple_dependent end def test_multiple_regression_pairwise_2 - @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 3, nil, 3, nil, 3].to_vector(:scale) - @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4, 2, 2, nil, 6, 2].to_vector(:scale) - @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100, nil, 3, 7, nil, 7].to_vector(:scale) - @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 30, 40, nil, 50, nil].to_vector(:scale) + @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 3, nil, 3, nil, 3].to_vector(:numeric) + @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4, 2, 2, nil, 6, 2].to_vector(:numeric) + @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100, nil, 3, 7, nil, 7].to_vector(:numeric) + @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 30, 40, nil, 50, nil].to_vector(:numeric) ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') assert_in_delta(2407.436, lr.sst, 0.001) @@ -103,10 +103,10 @@ def test_multiple_regression_pairwise_2 def test_multiple_regression_gsl if Statsample.has_gsl? - @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:scale) - @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:scale) - @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:scale) - @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:scale) + @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:numeric) + @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:numeric) + @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:numeric) + @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:numeric) ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset lr = Statsample::Regression::Multiple::GslEngine.new(ds, 'y') assert(lr.summary.size > 0) @@ -174,10 +174,10 @@ def model_test(lr, name = 'undefined') end def test_regression_matrix - @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:scale) - @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:scale) - @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:scale) - @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:scale) + @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:numeric) + @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:numeric) + @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:numeric) + @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:numeric) ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset cor = Statsample::Bivariate.correlation_matrix(ds) @@ -194,10 +194,10 @@ def test_regression_matrix end def test_regression_rubyengine - @a = [nil, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:scale) - @b = [nil, 3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:scale) - @c = [nil, 11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:scale) - @y = [nil, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:scale) + @a = [nil, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:numeric) + @b = [nil, 3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:numeric) + @c = [nil, 11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:numeric) + @y = [nil, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:numeric) ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') assert_equal(11, lr.total_cases) diff --git a/test/test_reliability.rb b/test/test_reliability.rb index b3b3bdb..2b6c57b 100644 --- a/test/test_reliability.rb +++ b/test/test_reliability.rb @@ -16,9 +16,9 @@ class StatsampleReliabilityTestCase < Minitest::Test @samples = 40 @n_variables = rand(10) + 2 @ds = Statsample::Dataset.new - base = @samples.times.collect { |_a| rand }.to_scale + base = @samples.times.collect { |_a| rand }.to_numeric @n_variables.times do |i| - @ds[i] = base.collect { |v| v + rand }.to_scale + @ds[i] = base.collect { |v| v + rand }.to_numeric end @ds.update_valid_data @@ -67,9 +67,9 @@ class StatsampleReliabilityTestCase < Minitest::Test @samples = 100 @points = rand(10) + 3 @max_point = (@points - 1) * 3 - @x1 = @samples.times.map { rand(@points) }.to_scale - @x2 = @samples.times.map { rand(@points) }.to_scale - @x3 = @samples.times.map { rand(@points) }.to_scale + @x1 = @samples.times.map { rand(@points) }.to_numeric + @x2 = @samples.times.map { rand(@points) }.to_numeric + @x3 = @samples.times.map { rand(@points) }.to_numeric @ds = { 'a' => @x1, 'b' => @x2, 'c' => @x3 }.to_dataset @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds) end @@ -77,11 +77,11 @@ class StatsampleReliabilityTestCase < Minitest::Test assert_equal(@ds.vector_sum, @icc.vector_total) end should 'have a correct different vector_total' do - x2 = @samples.times.map { rand(10) }.to_scale + x2 = @samples.times.map { rand(10) }.to_numeric @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds, x2) assert_equal(x2, @icc.vector_total) assert_raises(ArgumentError) do - inc = (@samples + 10).times.map { rand(10) }.to_scale + inc = (@samples + 10).times.map { rand(10) }.to_numeric @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds, inc) end end @@ -119,7 +119,7 @@ class StatsampleReliabilityTestCase < Minitest::Test h = {} @scales.times {|s| @items_per_scale.times {|i| - h["#{s}_#{i}"] = (size.times.map { (s * 2) + rand }).to_scale + h["#{s}_#{i}"] = (size.times.map { (s * 2) + rand }).to_numeric } } @ds = h.to_dataset @@ -177,10 +177,10 @@ class StatsampleReliabilityTestCase < Minitest::Test end context Statsample::Reliability::ScaleAnalysis do setup do - @x1 = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 30].to_scale - @x2 = [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 50].to_scale - @x3 = [2, 2, 1, 1, 1, 2, 2, 2, 3, 4, 5, 40].to_scale - @x4 = [1, 2, 3, 4, 4, 4, 4, 3, 4, 4, 5, 30].to_scale + @x1 = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 30].to_numeric + @x2 = [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 50].to_numeric + @x3 = [2, 2, 1, 1, 1, 2, 2, 2, 3, 4, 5, 40].to_numeric + @x4 = [1, 2, 3, 4, 4, 4, 4, 3, 4, 4, 5, 30].to_numeric @ds = { 'x1' => @x1, 'x2' => @x2, 'x3' => @x3, 'x4' => @x4 }.to_dataset @ia = Statsample::Reliability::ScaleAnalysis.new(@ds) @cov_matrix = @ia.cov_m @@ -188,7 +188,7 @@ class StatsampleReliabilityTestCase < Minitest::Test should 'return correct values for item analysis' do assert_in_delta(0.980, @ia.alpha, 0.001) assert_in_delta(0.999, @ia.alpha_standarized, 0.001) - var_mean = 4.times.map { |m| @cov_matrix[m, m] }.to_scale.mean + var_mean = 4.times.map { |m| @cov_matrix[m, m] }.to_numeric.mean assert_in_delta(var_mean, @ia.variances_mean) assert_equal(@x1.mean, @ia.item_statistics['x1'][:mean]) assert_equal(@x4.mean, @ia.item_statistics['x4'][:mean]) @@ -211,7 +211,7 @@ class StatsampleReliabilityTestCase < Minitest::Test end } } - assert_in_delta(covariances.to_scale.mean, @ia.covariances_mean) + assert_in_delta(covariances.to_numeric.mean, @ia.covariances_mean) assert_in_delta(0.999, @ia.item_total_correlation['x1'], 0.001) assert_in_delta(1050.455, @ia.stats_if_deleted['x1'][:variance_sample], 0.001) end diff --git a/test/test_reliability_icc.rb b/test/test_reliability_icc.rb index 3fe0991..a252791 100644 --- a/test/test_reliability_icc.rb +++ b/test/test_reliability_icc.rb @@ -5,10 +5,10 @@ class StatsampleReliabilityIccTestCase < Minitest::Test context Statsample::Reliability::ICC do setup do - a = [9, 6, 8, 7, 10, 6].to_scale - b = [2, 1, 4, 1, 5, 2].to_scale - c = [5, 3, 6, 2, 6, 4].to_scale - d = [8, 2, 8, 6, 9, 7].to_scale + a = [9, 6, 8, 7, 10, 6].to_numeric + b = [2, 1, 4, 1, 5, 2].to_numeric + c = [5, 3, 6, 2, 6, 4].to_numeric + d = [8, 2, 8, 6, 9, 7].to_numeric @ds = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset @icc = Statsample::Reliability::ICC.new(@ds) end @@ -122,7 +122,7 @@ class StatsampleReliabilityIccTestCase < Minitest::Test setup do if $reliability_icc.nil? size = 100 - a = size.times.map { rand(10) }.to_scale + a = size.times.map { rand(10) }.to_numeric b = a.recode { |i| i + rand(4) - 2 } c = a.recode { |i| i + rand(4) - 2 } d = a.recode { |i| i + rand(4) - 2 } diff --git a/test/test_reliability_skillscale.rb b/test/test_reliability_skillscale.rb index ea7f15c..d7dd9a0 100644 --- a/test/test_reliability_skillscale.rb +++ b/test/test_reliability_skillscale.rb @@ -5,7 +5,7 @@ class StatsampleReliabilitySkillScaleTestCase < Minitest::Test setup do options = %w(a b c d e) cases = 20 - @id = cases.times.map { |v| v }.to_scale + @id = cases.times.map { |v| v }.to_numeric @a = cases.times.map { options[rand(5)] }.to_vector @b = cases.times.map { options[rand(5)] }.to_vector @c = cases.times.map { options[rand(5)] }.to_vector @@ -17,11 +17,11 @@ class StatsampleReliabilitySkillScaleTestCase < Minitest::Test @ds = { 'id' => @id, 'a' => @a, 'b' => @b, 'c' => @c, 'd' => @d, 'e' => @e }.to_dataset @key = { 'a' => 'a', 'b' => options[rand(5)], 'c' => options[rand(5)], 'd' => options[rand(5)], 'e' => options[rand(5)] } @ssa = Statsample::Reliability::SkillScaleAnalysis.new(@ds, @key) - @ac = @a.map { |v| v == @key['a'] ? 1 : 0 }.to_scale - @bc = @b.map { |v| v == @key['b'] ? 1 : 0 }.to_scale - @cc = @c.map { |v| v == @key['c'] ? 1 : 0 }.to_scale - @dc = @d.map { |v| v == @key['d'] ? 1 : 0 }.to_scale - @ec = @e.map { |v| v.nil? ? nil : (v == @key['e'] ? 1 : 0) }.to_scale + @ac = @a.map { |v| v == @key['a'] ? 1 : 0 }.to_numeric + @bc = @b.map { |v| v == @key['b'] ? 1 : 0 }.to_numeric + @cc = @c.map { |v| v == @key['c'] ? 1 : 0 }.to_numeric + @dc = @d.map { |v| v == @key['d'] ? 1 : 0 }.to_numeric + @ec = @e.map { |v| v.nil? ? nil : (v == @key['e'] ? 1 : 0) }.to_numeric end should 'return proper corrected dataset' do cds = { 'id' => @id, 'a' => @ac, 'b' => @bc, 'c' => @cc, 'd' => @dc, 'e' => @ec }.to_dataset diff --git a/test/test_resample.rb b/test/test_resample.rb index 4d353ff..8ea3257 100644 --- a/test/test_resample.rb +++ b/test/test_resample.rb @@ -17,7 +17,7 @@ def test_repeat_and_save Statsample::Resample.generate(20, 1, 10).count(1) } assert_equal(400, r.size) - v = Statsample::Vector.new(r, :scale) + v = Statsample::Vector.new(r, :numeric) a = v.count { |x| x > 3 } assert(a >= 30 && a <= 70) end diff --git a/test/test_rserve_extension.rb b/test/test_rserve_extension.rb index ce7ef4e..1c7ed6d 100644 --- a/test/test_rserve_extension.rb +++ b/test/test_rserve_extension.rb @@ -12,7 +12,7 @@ class StatsampleRserveExtensionTestCase < Minitest::Test @r.close end should 'return a valid rexp for numeric vector' do - a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale + a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric rexp = a.to_REXP assert(rexp.is_a? Rserve::REXP::Double) assert_equal(rexp.to_ruby, a.data_with_nils) @@ -20,9 +20,9 @@ class StatsampleRserveExtensionTestCase < Minitest::Test assert_equal(a.data_with_nils, @r.eval('a').to_ruby) end should 'return a valid rserve dataframe for statsample datasets' do - a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale - b = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale - c = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_scale + a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric + b = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric + c = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset rexp = ds.to_REXP assert(rexp.is_a? Rserve::REXP::GenericVector) diff --git a/test/test_statistics.rb b/test/test_statistics.rb index 4e17370..7d2ce37 100644 --- a/test/test_statistics.rb +++ b/test/test_statistics.rb @@ -32,7 +32,7 @@ def test_is_number end def test_estimation_mean - v = ([42] * 23 + [41] * 4 + [36] * 1 + [32] * 1 + [29] * 1 + [27] * 2 + [23] * 1 + [19] * 1 + [16] * 2 + [15] * 2 + [14, 11, 10, 9, 7] + [6] * 3 + [5] * 2 + [4, 3]).to_vector(:scale) + v = ([42] * 23 + [41] * 4 + [36] * 1 + [32] * 1 + [29] * 1 + [27] * 2 + [23] * 1 + [19] * 1 + [16] * 2 + [15] * 2 + [14, 11, 10, 9, 7] + [6] * 3 + [5] * 2 + [4, 3]).to_vector(:numeric) assert_equal(50, v.size) assert_equal(1471, v.sum) # limits=Statsample::SRS.mean_confidence_interval_z(v.mean(), v.sds(), v.size,676,0.80) @@ -57,17 +57,17 @@ def test_estimation_proportion def test_ml if true - # real=[1,1,1,1].to_vector(:scale) + # real=[1,1,1,1].to_vector(:numeric) - # pred=[0.0001,0.0001,0.0001,0.0001].to_vector(:scale) + # pred=[0.0001,0.0001,0.0001,0.0001].to_vector(:numeric) # puts Statsample::Bivariate.maximum_likehood_dichotomic(pred,real) end end def test_simple_linear_regression - a = [1, 2, 3, 4, 5, 6].to_vector(:scale) - b = [6, 2, 4, 10, 12, 8].to_vector(:scale) + a = [1, 2, 3, 4, 5, 6].to_vector(:numeric) + b = [6, 2, 4, 10, 12, 8].to_vector(:numeric) reg = Statsample::Regression::Simple.new_from_vectors(a, b) assert_in_delta((reg.ssr + reg.sse).to_f, reg.sst, 0.001) assert_in_delta(Statsample::Bivariate.pearson(a, b), reg.r, 0.001) diff --git a/test/test_stest.rb b/test/test_stest.rb index 1ce8244..298a825 100644 --- a/test/test_stest.rb +++ b/test/test_stest.rb @@ -24,25 +24,25 @@ def test_chi_square_matrix_only_observed end def test_u_mannwhitney - a = [1, 2, 3, 4, 5, 6].to_scale - b = [0, 5, 7, 9, 10, 11].to_scale + a = [1, 2, 3, 4, 5, 6].to_numeric + b = [0, 5, 7, 9, 10, 11].to_numeric assert_equal(7.5, Statsample::Test.u_mannwhitney(a, b).u) assert_equal(7.5, Statsample::Test.u_mannwhitney(b, a).u) - a = [1, 7, 8, 9, 10, 11].to_scale - b = [2, 3, 4, 5, 6, 12].to_scale + a = [1, 7, 8, 9, 10, 11].to_numeric + b = [2, 3, 4, 5, 6, 12].to_numeric assert_equal(11, Statsample::Test.u_mannwhitney(a, b).u) end def test_levene - a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_scale - b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_scale + a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_numeric + b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_numeric levene = Statsample::Test::Levene.new([a, b]) assert_levene(levene) end def test_levene_dataset - a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_scale - b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_scale + a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_numeric + b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_numeric ds = { 'a' => a, 'b' => b }.to_dataset levene = Statsample::Test::Levene.new(ds) assert_levene(levene) diff --git a/test/test_stratified.rb b/test/test_stratified.rb index 9ab0f8f..423c97d 100644 --- a/test/test_stratified.rb +++ b/test/test_stratified.rb @@ -9,9 +9,9 @@ def test_mean a = [10, 20, 30, 40, 50] b = [110, 120, 130, 140] pop = a + b - av = a.to_vector(:scale) - bv = b.to_vector(:scale) - popv = pop.to_vector(:scale) + av = a.to_vector(:numeric) + bv = b.to_vector(:numeric) + popv = pop.to_vector(:numeric) assert_equal(popv.mean, Statsample::StratifiedSample.mean(av, bv)) end end diff --git a/test/test_test_t.rb b/test/test_test_t.rb index 26ce98e..e42ef5d 100644 --- a/test/test_test_t.rb +++ b/test/test_test_t.rb @@ -4,8 +4,8 @@ class StatsampleTestTTestCase < Minitest::Test include Math context T do setup do - @a = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99].to_scale - @b = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98].to_scale + @a = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99].to_numeric + @b = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98].to_numeric @x1 = @a.mean @x2 = @b.mean @s1 = @a.sd @@ -20,8 +20,8 @@ class StatsampleTestTTestCase < Minitest::Test assert(t.summary.size > 0) end should 'calculate correctly t for one sample' do - t1 = [6, 4, 6, 7, 4, 5, 5, 12, 6, 1].to_scale - t2 = [9, 6, 5, 10, 10, 8, 7, 10, 6, 5].to_scale + t1 = [6, 4, 6, 7, 4, 5, 5, 12, 6, 1].to_numeric + t2 = [9, 6, 5, 10, 10, 8, 7, 10, 6, 5].to_numeric d = t1 - t2 t = Statsample::Test::T::OneSample.new(d) assert_in_delta(-2.631, t.t, 0.001) @@ -48,7 +48,7 @@ class StatsampleTestTTestCase < Minitest::Test assert_in_delta(0.09095, t.probability_not_equal_variance, 0.001) end should 'be the same using shorthand' do - v = 100.times.map { rand(100) }.to_scale + v = 100.times.map { rand(100) }.to_numeric assert_equal(Statsample::Test.t_one_sample(v).t, T::OneSample.new(v).t) end should 'calculate all values for one sample T test' do diff --git a/test/test_umannwhitney.rb b/test/test_umannwhitney.rb index 1f09562..ab3c65d 100644 --- a/test/test_umannwhitney.rb +++ b/test/test_umannwhitney.rb @@ -4,8 +4,8 @@ class StatsampleUMannWhitneyTestCase < Minitest::Test include Statsample::Test context Statsample::Test::UMannWhitney do setup do - @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15].to_scale - @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19].to_scale + @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15].to_numeric + @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19].to_numeric @u = Statsample::Test::UMannWhitney.new(@v1, @v2) end should 'have same result using class or Test#u_mannwhitney' do diff --git a/test/test_vector.rb b/test/test_vector.rb index 5e25050..b2df4ca 100644 --- a/test/test_vector.rb +++ b/test/test_vector.rb @@ -4,7 +4,7 @@ class StatsampleTestVector < Minitest::Test include Statsample::Shorthand def setup - @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :nominal) + @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :object) @c.name = 'Test Vector' @c.missing_values = [-99] end @@ -19,8 +19,8 @@ def assert_counting_tokens(b) context Statsample do setup do @sample = 100 - @a = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_scale - @b = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_scale + @a = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_numeric + @b = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_numeric @correct_a = [] @correct_b = [] @a.each_with_index do |_v, i| @@ -29,8 +29,8 @@ def assert_counting_tokens(b) @correct_b.push(@b[i]) end end - @correct_a = @correct_a.to_scale - @correct_b = @correct_b.to_scale + @correct_a = @correct_a.to_numeric + @correct_b = @correct_b.to_numeric @common = lambda do |av, bv| assert_equal(@correct_a, av, 'A no es esperado') @@ -58,7 +58,7 @@ def assert_counting_tokens(b) end context Statsample::Vector do setup do - @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :nominal) + @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :object) @c.name = 'Test Vector' @c.missing_values = [-99] end @@ -71,7 +71,7 @@ def assert_counting_tokens(b) context 'using matrix operations' do setup do - @a = [1, 2, 3, 4, 5].to_scale + @a = [1, 2, 3, 4, 5].to_numeric end should 'to_matrix returns a matrix with 1 row' do mh = Matrix[[1, 2, 3, 4, 5]] @@ -83,22 +83,22 @@ def assert_counting_tokens(b) end should 'returns valid submatrixes' do # 3*4 + 2*5 = 22 - a = [3, 2].to_vector(:scale) - b = [4, 5].to_vector(:scale) + a = [3, 2].to_vector(:numeric) + b = [4, 5].to_vector(:numeric) assert_equal(22, (a.to_matrix * b.to_matrix(:vertical))[0, 0]) end end context 'when initializing' do setup do @data = (10.times.map { rand(100) }) + [nil] - @original = Statsample::Vector.new(@data, :scale) + @original = Statsample::Vector.new(@data, :numeric) end should 'be the sample using []' do second = Statsample::Vector[*@data] assert_equal(@original, second) end should '[] returns same results as R-c()' do - reference = [0, 4, 5, 6, 10].to_scale + reference = [0, 4, 5, 6, 10].to_numeric assert_equal(reference, Statsample::Vector[0, 4, 5, 6, 10]) assert_equal(reference, Statsample::Vector[0, 4..6, 10]) assert_equal(reference, Statsample::Vector[[0], [4, 5, 6], [10]]) @@ -107,29 +107,29 @@ def assert_counting_tokens(b) assert_equal(reference, Statsample::Vector[[0], [4, 5, 6].to_vector, [10]]) end should 'be the same usign #to_vector' do - lazy1 = @data.to_vector(:scale) + lazy1 = @data.to_vector(:numeric) assert_equal(@original, lazy1) end - should 'be the same using #to_scale' do - lazy2 = @data.to_scale + should 'be the same using #to_numeric' do + lazy2 = @data.to_numeric assert_equal(@original, lazy2) - assert_equal(:scale, lazy2.type) + assert_equal(:numeric, lazy2.type) assert_equal(@data.find_all { |v| !v.nil? }, lazy2.valid_data) end - should 'could use new_scale with size only' do - v1 = 10.times.map { nil }.to_scale - v2 = Statsample::Vector.new_scale(10) + should 'could use new_numeric with size only' do + v1 = 10.times.map { nil }.to_numeric + v2 = Statsample::Vector.new_numeric(10) assert_equal(v1, v2) end - should 'could use new_scale with size and value' do + should 'could use new_numeric with size and value' do a = rand - v1 = 10.times.map { a }.to_scale - v2 = Statsample::Vector.new_scale(10, a) + v1 = 10.times.map { a }.to_numeric + v2 = Statsample::Vector.new_numeric(10, a) assert_equal(v1, v2) end - should 'could use new_scale with func' do - v1 = 10.times.map { |i| i * 2 }.to_scale - v2 = Statsample::Vector.new_scale(10) { |i| i * 2 } + should 'could use new_numeric with func' do + v1 = 10.times.map { |i| i * 2 }.to_numeric + v2 = Statsample::Vector.new_numeric(10) { |i| i * 2 } assert_equal(v1, v2) end end @@ -146,18 +146,22 @@ def assert_counting_tokens(b) assert_equal([1,2,3,4,5], v.valid_data) end - should "show a warning when initializing with :scale, :ordinal or :nominal" do - assert_output("WARNING: nominal has been deprecated. Use :object instead.") do + should "show a warning when initializing with :nominal, :numeric or :ordinal" do + assert_output("WARNING: nominal has been deprecated. Use :object instead.\n") do Statsample::Vector.new [1,2,3,4,5,nil,'hello'], :nominal end - assert_output("WARNING: scale has been deprecated. Use :numeric instead.") do + assert_output("WARNING: scale has been deprecated. Use :numeric instead.\n") do Statsample::Vector.new [1,2,3,4,nil,5], :scale end - assert_output("WARNING: ordinal has been deprecated. Use :numeric instead.") do + assert_output("WARNING: ordinal has been deprecated. Use :numeric instead.\n") do Statsample::Vector.new [1,2,3,4,5], :ordinal end + + assert_output("WARNING: .new_scale has been deprecated. Use .new_numeric instead.\n") do + Statsample::Vector.new_scale 10, 1 + end end should "test that new shorthands work" do @@ -170,7 +174,7 @@ def assert_counting_tokens(b) end should "test that old shorthands raise warnings" do - assert_output("WARNING: to_scale has been deprecated. Use to_numeric instead.") do + assert_output("WARNING: to_scale has been deprecated. Use to_numeric instead.\n") do [1,2,3,4,nil,5].to_scale end end @@ -178,7 +182,7 @@ def assert_counting_tokens(b) context '#split_by_separator' do setup do - @a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 10, nil], :nominal) + @a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 10, nil], :object) @b = @a.split_by_separator(',') end should 'returns a Hash' do @@ -199,17 +203,17 @@ def assert_counting_tokens(b) assert_equal({ 'a' => 3, 'b' => 1, 'c' => 1, 'd' => 2, 10 => 1 }, @a.split_by_separator_freq) end should 'using a different separator give the same values' do - a = Statsample::Vector.new(['a', 'a*b', 'c*d', 'a*d', 10, nil], :nominal) + a = Statsample::Vector.new(['a', 'a*b', 'c*d', 'a*d', 10, nil], :object) b = a.split_by_separator('*') assert_counting_tokens(b) end end should 'return correct median_absolute_deviation' do - a = [1, 1, 2, 2, 4, 6, 9].to_scale + a = [1, 1, 2, 2, 4, 6, 9].to_numeric assert_equal(1, a.median_absolute_deviation) end should 'return correct histogram' do - a = 10.times.map { |v| v }.to_scale + a = 10.times.map { |v| v }.to_numeric hist = a.histogram(2) assert_equal([5, 5], hist.bin) 3.times do |i| @@ -220,8 +224,8 @@ def assert_counting_tokens(b) @c.name == 'Test Vector' end should 'without explicit name, returns vector with succesive numbers' do - a = 10.times.map { rand(100) }.to_scale - b = 10.times.map { rand(100) }.to_scale + a = 10.times.map { rand(100) }.to_numeric + b = 10.times.map { rand(100) }.to_numeric assert_match(/Vector \d+/, a.name) a.name =~ /Vector (\d+)/ next_number = Regexp.last_match(1).to_i + 1 @@ -247,7 +251,7 @@ def assert_counting_tokens(b) assert_equal(exp2, exp) end should '#product returns the * of all values' do - a = [1, 2, 3, 4, 5].to_vector(:scale) + a = [1, 2, 3, 4, 5].to_vector(:numeric) assert_equal(120, a.product) end @@ -290,7 +294,7 @@ def assert_counting_tokens(b) should 'GSL::Vector based should push correcty' do if Statsample.has_gsl? - v = GSL::Vector[1, 2, 3, 4, 5].to_scale + v = GSL::Vector[1, 2, 3, 4, 5].to_numeric v.push(nil) assert_equal([1, 2, 3, 4, 5, nil], v.to_a) assert(v.flawed?) @@ -300,36 +304,32 @@ def assert_counting_tokens(b) end should 'split correctly' do - a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 'd', 10, nil], :nominal) + a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 'd', 10, nil], :object) assert_equal([%w(a), %w(a b), %w(c d), %w(a d), %w(d), [10], nil], a.splitted) end should 'multiply correct for scalar' do - a = [1, 2, 3].to_scale - assert_equal([5, 10, 15].to_scale, a * 5) + a = [1, 2, 3].to_numeric + assert_equal([5, 10, 15].to_numeric, a * 5) end should 'multiply correct with other vector' do - a = [1, 2, 3].to_scale - b = [2, 4, 6].to_scale + a = [1, 2, 3].to_numeric + b = [2, 4, 6].to_numeric - assert_equal([2, 8, 18].to_scale, a * b) + assert_equal([2, 8, 18].to_numeric, a * b) end should 'sum correct for scalar' do - a = [1, 2, 3].to_scale - assert_equal([11, 12, 13].to_scale, a + 10) + a = [1, 2, 3].to_numeric + assert_equal([11, 12, 13].to_numeric, a + 10) end - should 'raise NoMethodError when method requires ordinal and vector is nominal' do - @c.type = :nominal + should 'raise NoMethodError when method requires numeric and vector is object' do + @c.type = :object assert_raise(::NoMethodError) { @c.median } end - should 'raise NoMethodError when method requires scalar and vector is ordinal' do - @c.type = :ordinal - assert_raise(::NoMethodError) { @c.mean } - end should 'jacknife correctly with named method' do # First example - a = [1, 2, 3, 4].to_scale + a = [1, 2, 3, 4].to_numeric ds = a.jacknife(:mean) assert_equal(a.mean, ds[:mean].mean) ds = a.jacknife([:mean, :sd]) @@ -338,9 +338,9 @@ def assert_counting_tokens(b) end should 'jacknife correctly with custom method' do # Second example - a = [17.23, 18.71, 13.93, 18.81, 15.78, 11.29, 14.91, 13.39, 18.21, 11.57, 14.28, 10.94, 18.83, 15.52, 13.45, 15.25].to_scale + a = [17.23, 18.71, 13.93, 18.81, 15.78, 11.29, 14.91, 13.39, 18.21, 11.57, 14.28, 10.94, 18.83, 15.52, 13.45, 15.25].to_numeric ds = a.jacknife(log_s2: ->(v) { Math.log(v.variance) }) - exp = [1.605, 2.972, 1.151, 3.097, 0.998, 3.308, 0.942, 1.393, 2.416, 2.951, 1.043, 3.806, 3.122, 0.958, 1.362, 0.937].to_scale + exp = [1.605, 2.972, 1.151, 3.097, 0.998, 3.308, 0.942, 1.393, 2.416, 2.951, 1.043, 3.806, 3.122, 0.958, 1.362, 0.937].to_numeric assert_similar_vector(exp, ds[:log_s2], 0.001) assert_in_delta(2.00389, ds[:log_s2].mean, 0.00001) @@ -350,7 +350,7 @@ def assert_counting_tokens(b) a = rnorm(6) ds = a.jacknife(:mean, 2) mean = a.mean - exp = [3 * mean - 2 * (a[2] + a[3] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[2] + a[3]) / 4].to_scale + exp = [3 * mean - 2 * (a[2] + a[3] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[2] + a[3]) / 4].to_numeric assert_similar_vector(exp, ds[:mean], 1e-13) end should 'bootstrap should return a vector with mean=mu and sd=se' do @@ -362,7 +362,7 @@ def assert_counting_tokens(b) end end - def test_nominal + def test_object assert_equal(@c[1], 5) assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c.frequencies) assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c._frequencies) @@ -378,8 +378,8 @@ def test_equality v1 = [1, 2, 3].to_vector v2 = [1, 2, 3].to_vector assert_equal(v1, v2) - v1 = [1, 2, 3].to_vector(:nominal) - v2 = [1, 2, 3].to_vector(:ordinal) + v1 = [1, 2, 3].to_vector(:object) + v2 = [1, 2, 3].to_vector(:numeric) assert_not_equal(v1, v2) v2 = [1, 2, 3] assert_not_equal(v1, v2) @@ -390,23 +390,23 @@ def test_equality end def test_vector_percentil - a = [1, 2, 2, 3, 4, 5, 5, 5, 6, 10].to_scale - expected = [10, 25, 25, 40, 50, 70, 70, 70, 90, 100].to_scale + a = [1, 2, 2, 3, 4, 5, 5, 5, 6, 10].to_numeric + expected = [10, 25, 25, 40, 50, 70, 70, 70, 90, 100].to_numeric assert_equal(expected, a.vector_percentil) - a = [1, nil, nil, 2, 2, 3, 4, nil, nil, 5, 5, 5, 6, 10].to_scale - expected = [10, nil, nil, 25, 25, 40, 50, nil, nil, 70, 70, 70, 90, 100].to_scale + a = [1, nil, nil, 2, 2, 3, 4, nil, nil, 5, 5, 5, 6, 10].to_numeric + expected = [10, nil, nil, 25, 25, 40, 50, nil, nil, 70, 70, 70, 90, 100].to_numeric assert_equal(expected, a.vector_percentil) end - def test_ordinal - @c.type = :ordinal + def test_numeric + @c.type = :numeric assert_equal(5, @c.median) assert_equal(4, @c.percentil(25)) assert_equal(7, @c.percentil(75)) - v = [200_000, 200_000, 210_000, 220_000, 230_000, 250_000, 250_000, 250_000, 270_000, 300_000, 450_000, 130_000, 140_000, 140_000, 140_000, 145_000, 148_000, 165_000, 170_000, 180_000, 180_000, 180_000, 180_000, 180_000, 180_000].to_scale + v = [200_000, 200_000, 210_000, 220_000, 230_000, 250_000, 250_000, 250_000, 270_000, 300_000, 450_000, 130_000, 140_000, 140_000, 140_000, 145_000, 148_000, 165_000, 170_000, 180_000, 180_000, 180_000, 180_000, 180_000, 180_000].to_numeric assert_equal(180_000, v.median) - a = [7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 9.0, 9.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 12.0, 13.0, 14.0, 14.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0].to_scale + a = [7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 9.0, 9.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 12.0, 13.0, 14.0, 14.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0].to_numeric assert_equal(4.5, a.percentil(25)) assert_equal(6.5, a.percentil(50)) assert_equal(9.5, a.percentil(75)) @@ -414,14 +414,14 @@ def test_ordinal end def test_linear_percentil_strategy - values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116].shuffle.to_scale + values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116].shuffle.to_numeric assert_equal 102, values.percentil(0, :linear) assert_equal 104.75, values.percentil(25, :linear) assert_equal 108.5, values.percentil(50, :linear) assert_equal 112.75, values.percentil(75, :linear) assert_equal 116, values.percentil(100, :linear) - values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116, 118].shuffle.to_scale + values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116, 118].shuffle.to_numeric assert_equal 102, values.percentil(0, :linear) assert_equal 105, values.percentil(25, :linear) assert_equal 109, values.percentil(50, :linear) @@ -430,16 +430,16 @@ def test_linear_percentil_strategy end def test_ranked - v1 = [0.8, 1.2, 1.2, 2.3, 18].to_vector(:ordinal) - expected = [1, 2.5, 2.5, 4, 5].to_vector(:ordinal) + v1 = [0.8, 1.2, 1.2, 2.3, 18].to_vector(:numeric) + expected = [1, 2.5, 2.5, 4, 5].to_vector(:numeric) assert_equal(expected, v1.ranked) - v1 = [nil, 0.8, 1.2, 1.2, 2.3, 18, nil].to_vector(:ordinal) - expected = [nil, 1, 2.5, 2.5, 4, 5, nil].to_vector(:ordinal) + v1 = [nil, 0.8, 1.2, 1.2, 2.3, 18, nil].to_vector(:numeric) + expected = [nil, 1, 2.5, 2.5, 4, 5, nil].to_vector(:numeric) assert_equal(expected, v1.ranked) end - def test_scale - a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :scale) + def test_numeric + a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :numeric) assert_equal(10, a.sum) i = 0 factors = a.factors.sort @@ -453,7 +453,7 @@ def test_scale def test_vector_centered mean = rand samples = 11 - centered = samples.times.map { |i| i - ((samples / 2).floor).to_i }.to_scale + centered = samples.times.map { |i| i - ((samples / 2).floor).to_i }.to_numeric not_centered = centered.recode { |v| v + mean } obs = not_centered.centered centered.each_with_index do |v, i| @@ -462,9 +462,9 @@ def test_vector_centered end def test_vector_standarized - v1 = [1, 2, 3, 4, nil].to_vector(:scale) + v1 = [1, 2, 3, 4, nil].to_vector(:numeric) sds = v1.sds - expected = [((1 - 2.5).quo(sds)), ((2 - 2.5).quo(sds)), ((3 - 2.5).quo(sds)), ((4 - 2.5).quo(sds)), nil].to_vector(:scale) + expected = [((1 - 2.5).quo(sds)), ((2 - 2.5).quo(sds)), ((3 - 2.5).quo(sds)), ((4 - 2.5).quo(sds)), nil].to_vector(:numeric) vs = v1.vector_standarized assert_equal(expected, vs) assert_equal(0, vs.mean) @@ -472,39 +472,31 @@ def test_vector_standarized end def test_vector_standarized_with_zero_variance - v1 = 100.times.map { |_i| 1 }.to_scale - exp = 100.times.map { nil }.to_scale + v1 = 100.times.map { |_i| 1 }.to_numeric + exp = 100.times.map { nil }.to_numeric assert_equal(exp, v1.standarized) end def test_check_type v = Statsample::Vector.new - v.type = :nominal - assert_raise(NoMethodError) { v.check_type(:scale) } - assert_raise(NoMethodError) { v.check_type(:ordinal) } - assert(v.check_type(:nominal).nil?) - - v.type = :ordinal - - assert_raise(NoMethodError) { v.check_type(:scale) } + v.type = :object + assert_raise(NoMethodError) { v.check_type(:numeric) } + assert(v.check_type(:object).nil?) - assert(v.check_type(:ordinal).nil?) - assert(v.check_type(:nominal).nil?) + v.type = :numeric - v.type = :scale - assert(v.check_type(:scale).nil?) - assert(v.check_type(:ordinal).nil?) - assert(v.check_type(:nominal).nil?) + assert(v.check_type(:numeric).nil?) + assert(v.check_type(:object).nil?) v.type = :date - assert_raise(NoMethodError) { v.check_type(:scale) } - assert_raise(NoMethodError) { v.check_type(:ordinal) } - assert_raise(NoMethodError) { v.check_type(:nominal) } + assert_raise(NoMethodError) { v.check_type(:numeric) } + assert_raise(NoMethodError) { v.check_type(:numeric) } + assert_raise(NoMethodError) { v.check_type(:object) } end def test_add - a = Statsample::Vector.new([1, 2, 3, 4, 5], :scale) - b = Statsample::Vector.new([11, 12, 13, 14, 15], :scale) + a = Statsample::Vector.new([1, 2, 3, 4, 5], :numeric) + b = Statsample::Vector.new([11, 12, 13, 14, 15], :numeric) assert_equal([3, 4, 5, 6, 7], (a + 2).to_a) assert_equal([12, 14, 16, 18, 20], (a + b).to_a) assert_raise ArgumentError do @@ -513,15 +505,15 @@ def test_add assert_raise TypeError do a + 'string' end - a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :scale) - b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :scale) + a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :numeric) + b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :numeric) assert_equal([nil, 13, nil, 16, 18, 20], (a + b).to_a) assert_equal([nil, 13, nil, 16, 18, 20], (a + b.to_a).to_a) end def test_minus - a = Statsample::Vector.new([1, 2, 3, 4, 5], :scale) - b = Statsample::Vector.new([11, 12, 13, 14, 15], :scale) + a = Statsample::Vector.new([1, 2, 3, 4, 5], :numeric) + b = Statsample::Vector.new([11, 12, 13, 14, 15], :numeric) assert_equal([-1, 0, 1, 2, 3], (a - 2).to_a) assert_equal([10, 10, 10, 10, 10], (b - a).to_a) assert_raise ArgumentError do @@ -530,19 +522,19 @@ def test_minus assert_raise TypeError do a - 'string' end - a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :scale) - b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :scale) + a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :numeric) + b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :numeric) assert_equal([nil, 11, nil, 10, 10, 10], (b - a).to_a) assert_equal([nil, 11, nil, 10, 10, 10], (b - a.to_a).to_a) end def test_sum_of_squares - a = [1, 2, 3, 4, 5, 6].to_vector(:scale) + a = [1, 2, 3, 4, 5, 6].to_vector(:numeric) assert_equal(17.5, a.sum_of_squared_deviation) end def test_average_deviation - a = [1, 2, 3, 4, 5, 6, 7, 8, 9].to_scale + a = [1, 2, 3, 4, 5, 6, 7, 8, 9].to_numeric assert_equal(20.quo(9), a.average_deviation_population) end @@ -553,7 +545,7 @@ def test_samples assert_raise ArgumentError do @c.sample_without_replacement(20) end - @c.type = :scale + @c.type = :numeric srand(1) assert_equal(100, @c.sample_with_replacement(100).size) assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) @@ -584,7 +576,7 @@ def test_set_value def test_gsl if Statsample.has_gsl? - a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :scale) + a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :numeric) assert_equal(2, a.mean) assert_equal(a.variance_sample_ruby, a.variance_sample) @@ -592,17 +584,17 @@ def test_gsl assert_equal(a.variance_population_ruby, a.variance_population) assert_equal(a.standard_deviation_population_ruby, a.standard_deviation_population) assert_nothing_raised do - a = [].to_vector(:scale) + a = [].to_vector(:numeric) end a.add(1, false) a.add(2, false) a.set_valid_data assert_equal(3, a.sum) - b = [1, 2, nil, 3, 4, 5, nil, 6].to_vector(:scale) + b = [1, 2, nil, 3, 4, 5, nil, 6].to_vector(:numeric) assert_equal(21, b.sum) assert_equal(3.5, b.mean) assert_equal(6, b.gsl.size) - c = [10, 20, 30, 40, 50, 100, 1000, 2000, 5000].to_scale + c = [10, 20, 30, 40, 50, 100, 1000, 2000, 5000].to_numeric assert_in_delta(c.skew, c.skew_ruby, 0.0001) assert_in_delta(c.kurtosis, c.kurtosis_ruby, 0.0001) end @@ -617,7 +609,7 @@ def test_vector_matrix end def test_marshalling - v1 = (0..100).to_a.collect { |_n| rand(100) }.to_vector(:scale) + v1 = (0..100).to_a.collect { |_n| rand(100) }.to_vector(:numeric) v2 = Marshal.load(Marshal.dump(v1)) assert_equal(v1, v2) end @@ -629,7 +621,7 @@ def test_dup assert_not_same(v1.data, v2.data) assert_equal(v1.type, v2.type) - v1.type = :ordinal + v1.type = :numeric assert_not_equal(v1.type, v2.type) assert_equal(v1.missing_values, v2.missing_values) assert_not_same(v1.missing_values, v2.missing_values) @@ -641,8 +633,8 @@ def test_dup assert_not_equal(v1.data, v3.data) assert_not_same(v1.data, v3.data) assert_equal(v1.type, v3.type) - v1.type = :ordinal - v3.type = :nominal + v1.type = :numeric + v3.type = :object assert_not_equal(v1.type, v3.type) assert_equal(v1.missing_values, v3.missing_values) assert_not_same(v1.missing_values, v3.missing_values) @@ -651,33 +643,33 @@ def test_dup end def test_paired_ties - a = [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 4].to_vector(:ordinal) - expected = [2, 2, 2, 4.5, 4.5, 6, 7.5, 7.5, 10, 10, 10].to_vector(:ordinal) + a = [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 4].to_vector(:numeric) + expected = [2, 2, 2, 4.5, 4.5, 6, 7.5, 7.5, 10, 10, 10].to_vector(:numeric) assert_equal(expected, a.ranked) end def test_dichotomize a = [0, 0, 0, 1, 2, 3, nil].to_vector - exp = [0, 0, 0, 1, 1, 1, nil].to_scale + exp = [0, 0, 0, 1, 1, 1, nil].to_numeric assert_equal(exp, a.dichotomize) a = [1, 1, 1, 2, 2, 2, 3].to_vector - exp = [0, 0, 0, 1, 1, 1, 1].to_scale + exp = [0, 0, 0, 1, 1, 1, 1].to_numeric assert_equal(exp, a.dichotomize) a = [0, 0, 0, 1, 2, 3, nil].to_vector - exp = [0, 0, 0, 0, 1, 1, nil].to_scale + exp = [0, 0, 0, 0, 1, 1, nil].to_numeric assert_equal(exp, a.dichotomize(1)) a = %w(a a a b c d).to_vector - exp = [0, 0, 0, 1, 1, 1].to_scale + exp = [0, 0, 0, 1, 1, 1].to_numeric assert_equal(exp, a.dichotomize) end def test_can_be_methods a = [0, 0, 0, 1, 2, 3, nil].to_vector - assert(a.can_be_scale?) + assert(a.can_be_numeric?) a = [0, 's', 0, 1, 2, 3, nil].to_vector - assert(!a.can_be_scale?) + assert(!a.can_be_numeric?) a.missing_values = ['s'] - assert(a.can_be_scale?) + assert(a.can_be_numeric?) a = [Date.new(2009, 10, 10), Date.today, '2009-10-10', '2009-1-1', nil, 'NOW'].to_vector assert(a.can_be_date?) diff --git a/test/test_wilcoxonsignedrank.rb b/test/test_wilcoxonsignedrank.rb index 41dfa6c..04cedd3 100644 --- a/test/test_wilcoxonsignedrank.rb +++ b/test/test_wilcoxonsignedrank.rb @@ -5,8 +5,8 @@ class StatsampleUMannWhitneyTestCase < Minitest::Test context Statsample::Test::WilcoxonSignedRank do context 'Example 1' do setup do - @v1 = [110, 122, 125, 120, 140, 124, 123, 137, 135, 145].to_scale - @v2 = [125, 115, 130, 140, 140, 115, 140, 125, 140, 135].to_scale + @v1 = [110, 122, 125, 120, 140, 124, 123, 137, 135, 145].to_numeric + @v2 = [125, 115, 130, 140, 140, 115, 140, 125, 140, 135].to_numeric @u = Statsample::Test::WilcoxonSignedRank.new(@v1, @v2) end should 'have same result using class or Test#u_mannwhitney' do @@ -34,8 +34,8 @@ class StatsampleUMannWhitneyTestCase < Minitest::Test context 'Example 2' do setup do - @v2 = [78, 24, 64, 45, 64, 52, 30, 50, 64, 50, 78, 22, 84, 40, 90, 72].to_scale - @v1 = [78, 24, 62, 48, 68, 56, 25, 44, 56, 40, 68, 36, 68, 20, 58, 32].to_scale + @v2 = [78, 24, 64, 45, 64, 52, 30, 50, 64, 50, 78, 22, 84, 40, 90, 72].to_numeric + @v1 = [78, 24, 62, 48, 68, 56, 25, 44, 56, 40, 68, 36, 68, 20, 58, 32].to_numeric @u = Statsample::Test::WilcoxonSignedRank.new(@v1, @v2) end should 'have same result using class or Test#u_mannwhitney' do diff --git a/test/test_xls.rb b/test/test_xls.rb index 4a5959d..0eb5e2d 100644 --- a/test/test_xls.rb +++ b/test/test_xls.rb @@ -11,11 +11,11 @@ class StatsampleExcelTestCase < Minitest::Test assert_equal(%w(id name age city a1), @ds.fields) end should 'set a dataset equal to expected' do - id = [1, 2, 3, 4, 5, 6].to_vector(:scale) - name = %w(Alex Claude Peter Franz George Fernand).to_vector(:nominal) - age = [20, 23, 25, nil, 5.5, nil].to_vector(:scale) - city = ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:nominal) - a1 = ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:nominal) + id = [1, 2, 3, 4, 5, 6].to_vector(:numeric) + name = %w(Alex Claude Peter Franz George Fernand).to_vector(:object) + age = [20, 23, 25, nil, 5.5, nil].to_vector(:numeric) + city = ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:object) + a1 = ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:object) ds_exp = Statsample::Dataset.new({ 'id' => id, 'name' => name, 'age' => age, 'city' => city, 'a1' => a1 }, %w(id name age city a1)) ds_exp.fields.each{|f| assert_equal(ds_exp[f], @ds[f]) @@ -28,7 +28,7 @@ class StatsampleExcelTestCase < Minitest::Test end context 'Excel writer' do setup do - a = 100.times.map { rand(100) }.to_scale + a = 100.times.map { rand(100) }.to_numeric b = (['b'] * 100).to_vector @ds = { 'b' => b, 'a' => a }.to_dataset(%w(b a)) tempfile = Tempfile.new('test_write.xls') From d6d313f32eb5e3e6c1cc2261b4049f87eb4044b0 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 14 May 2015 09:52:17 +0530 Subject: [PATCH 035/119] output warnings to stderr and revert HISTORY file minor changes to warnings and revert HISTORY file changes reverted history.txt changed puts to STDERR.puts changed to --- History.txt | 5 ----- lib/statsample/vector.rb | 8 ++++---- test/test_vector.rb | 10 +++++----- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/History.txt b/History.txt index 7d558da..76a2b7a 100644 --- a/History.txt +++ b/History.txt @@ -1,8 +1,3 @@ -=== 1.5.0 - * Made sure all methods work properly with and without GSL. - * Now works with either rb-gsl or gsl-nmatrix. - * Changed the data types of Statsample::Vector from :ordinal, :scale and :nominal to only :numeric and :object. :numeric replaces :ordinal/:scale and :object replaces :nominal. Older methods for creating the older data types still exist, but throw a warning prodding the user to use the new methods. - === 1.4.3 / 2015-04-27 * Removed rb-gsl dependency. diff --git a/lib/statsample/vector.rb b/lib/statsample/vector.rb index eae7213..fb5476b 100644 --- a/lib/statsample/vector.rb +++ b/lib/statsample/vector.rb @@ -11,7 +11,7 @@ def to_vector(*args) # Creates a new Statsample::Vector object of type :scale. # Deprecated. Use to_numeric instead. def to_scale(*args) - puts "WARNING: to_scale has been deprecated. Use to_numeric instead." + $stderr.puts "WARNING: to_scale has been deprecated. Use to_numeric instead." Statsample::Vector.new(self, :numeric, *args) end @@ -79,12 +79,12 @@ class Vector # * :name Name of vector def initialize(data=[], type=:object, opts=Hash.new) if type == :ordinal or type == :scale - puts "WARNING: #{type} has been deprecated. Use :numeric instead." + $stderr.puts "WARNING: #{type} has been deprecated. Use :numeric instead." type = :numeric end if type == :nominal - puts "WARNING: nominal has been deprecated. Use :object instead." + $stderr.puts "WARNING: nominal has been deprecated. Use :object instead." type = :object end @@ -155,7 +155,7 @@ def self.new_numeric(n,val=nil, &block) # Deprecated. Use new_numeric instead. def self.new_scale(n, val=nil,&block) - puts "WARNING: .new_scale has been deprecated. Use .new_numeric instead." + $stderr.puts "WARNING: .new_scale has been deprecated. Use .new_numeric instead." new_numeric n, val, &block end # Creates a duplicate of the Vector. diff --git a/test/test_vector.rb b/test/test_vector.rb index b2df4ca..fcf6e98 100644 --- a/test/test_vector.rb +++ b/test/test_vector.rb @@ -147,19 +147,19 @@ def assert_counting_tokens(b) end should "show a warning when initializing with :nominal, :numeric or :ordinal" do - assert_output("WARNING: nominal has been deprecated. Use :object instead.\n") do + assert_output(nil,"WARNING: nominal has been deprecated. Use :object instead.\n") do Statsample::Vector.new [1,2,3,4,5,nil,'hello'], :nominal end - assert_output("WARNING: scale has been deprecated. Use :numeric instead.\n") do + assert_output(nil,"WARNING: scale has been deprecated. Use :numeric instead.\n") do Statsample::Vector.new [1,2,3,4,nil,5], :scale end - assert_output("WARNING: ordinal has been deprecated. Use :numeric instead.\n") do + assert_output(nil,"WARNING: ordinal has been deprecated. Use :numeric instead.\n") do Statsample::Vector.new [1,2,3,4,5], :ordinal end - assert_output("WARNING: .new_scale has been deprecated. Use .new_numeric instead.\n") do + assert_output(nil,"WARNING: .new_scale has been deprecated. Use .new_numeric instead.\n") do Statsample::Vector.new_scale 10, 1 end end @@ -174,7 +174,7 @@ def assert_counting_tokens(b) end should "test that old shorthands raise warnings" do - assert_output("WARNING: to_scale has been deprecated. Use to_numeric instead.\n") do + assert_output(nil,"WARNING: to_scale has been deprecated. Use to_numeric instead.\n") do [1,2,3,4,nil,5].to_scale end end From aafa990b7f70d32ef9e52b119ad4fd5f281dbbda Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Wed, 20 May 2015 11:59:13 +0530 Subject: [PATCH 036/119] statsample::bivariate has now been ported to daru wip bivariate works with daru --- Gemfile | 3 ++ lib/statsample.rb | 11 ++-- lib/statsample/bivariate.rb | 103 ++++++++++++++++++++---------------- lib/statsample/crosstab.rb | 32 +++++------ lib/statsample/dataset.rb | 2 +- lib/statsample/vector.rb | 4 +- test/test_bivariate.rb | 87 +++++++++++++++--------------- 7 files changed, 131 insertions(+), 111 deletions(-) diff --git a/Gemfile b/Gemfile index 38eb365..53e79c8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,5 @@ source "https://www.rubygems.org" gemspec + +gem 'daru', :path => "/home/sameer/gitrepos/daru/" +gem 'gsl-nmatrix', :path => "/home/sameer/gitrepos/gsl-nmatrix/" \ No newline at end of file diff --git a/lib/statsample.rb b/lib/statsample.rb index 573a5f2..7a0c8e2 100644 --- a/lib/statsample.rb +++ b/lib/statsample.rb @@ -22,6 +22,7 @@ require 'distribution' require 'dirty-memoize' require 'reportbuilder' +require 'daru' class Numeric def square @@ -218,7 +219,7 @@ def vector_cols_matrix(*vs) size = vs[0].size vs.each do |v| - fail ArgumentError, 'Arguments should be Vector' unless v.instance_of? Statsample::Vector + fail ArgumentError, 'Arguments should be Vector' unless v.instance_of? Daru::Vector fail ArgumentError, 'Vectors size should be the same' if v.size != size end @@ -238,16 +239,16 @@ def vector_cols_matrix(*vs) # def only_valid(*vs) i = 1 - h = vs.inject({}) { |acc, v| acc["v#{i}"] = v; i += 1; acc } - ds = Statsample::Dataset.new(h).dup_only_valid - ds.vectors.values + h = vs.inject({}) { |acc, v| acc["v#{i}".to_sym] = v; i += 1; acc } + df = Daru::DataFrame.new(h).dup_only_valid + df.map { |v| v } end # Cheap version of #only_valid. # If any vectors have missing_values, return only valid. # If not, return the vectors itself def only_valid_clone(*vs) - if vs.any?(&:flawed?) + if vs.any?(&:has_missing_data?) only_valid(*vs) else vs diff --git a/lib/statsample/bivariate.rb b/lib/statsample/bivariate.rb index abb9f09..b84f915 100644 --- a/lib/statsample/bivariate.rb +++ b/lib/statsample/bivariate.rb @@ -14,7 +14,7 @@ def covariance(v1,v2) v1a,v2a=Statsample.only_valid_clone(v1,v2) return nil if v1a.size==0 if Statsample.has_gsl? - GSL::Stats::covariance(v1a.gsl, v2a.gsl) + GSL::Stats::covariance(v1a.to_gsl, v2a.to_gsl) else covariance_slow(v1a,v2a) end @@ -34,7 +34,9 @@ def covariance_slow(v1,v2) # :nodoc: sum_of_squares(v1a,v2a) / (v1a.size-1) end def sum_of_squares(v1,v2) - v1a,v2a=Statsample.only_valid_clone(v1,v2) + v1a,v2a=Statsample.only_valid_clone(v1,v2) + v1a.reset_index! + v2a.reset_index! m1=v1a.mean m2=v2a.mean (v1a.size).times.inject(0) {|ac,i| ac+(v1a[i]-m1)*(v2a[i]-m2)} @@ -44,13 +46,14 @@ def pearson(v1,v2) v1a,v2a=Statsample.only_valid_clone(v1,v2) return nil if v1a.size ==0 if Statsample.has_gsl? - GSL::Stats::correlation(v1a.gsl, v2a.gsl) + GSL::Stats::correlation(v1a.to_gsl, v2a.to_gsl) else pearson_slow(v1a,v2a) end end def pearson_slow(v1,v2) # :nodoc: v1a,v2a=Statsample.only_valid_clone(v1,v2) + # Calculate sum of squares ss=sum_of_squares(v1a,v2a) ss.quo(Math::sqrt(v1a.sum_of_squares) * Math::sqrt(v2a.sum_of_squares)) @@ -168,35 +171,39 @@ def covariance_matrix(ds) def covariance_matrix_pairwise(ds) cache={} - matrix=ds.collect_matrix do |row,col| - if (ds[row].type!=:numeric or ds[col].type!=:numeric) - nil - elsif row==col - ds[row].variance - else - if cache[[col,row]].nil? - cov=covariance(ds[row],ds[col]) - cache[[row,col]]=cov - cov + vectors = ds.vectors.to_a + mat_rows = vectors.collect do |row| + vectors.collect do |col| + if (ds[row].type!=:numeric or ds[col].type!=:numeric) + nil + elsif row==col + ds[row].variance else - cache[[col,row]] + if cache[[col,row]].nil? + cov=covariance(ds[row],ds[col]) + cache[[row,col]]=cov + cov + else + cache[[col,row]] + end end end end - matrix + + Matrix.rows mat_rows end # Correlation matrix. # Order of rows and columns depends on Dataset#fields order def correlation_matrix(ds) - vars,cases=ds.fields.size,ds.cases + vars, cases = ds.ncols, ds.nrows if !ds.has_missing_data? and Statsample.has_gsl? and prediction_optimized(vars,cases) < prediction_pairwise(vars,cases) cm=correlation_matrix_optimized(ds) else cm=correlation_matrix_pairwise(ds) end cm.extend(Statsample::CovariateMatrix) - cm.fields=ds.fields + cm.fields = ds.vectors.to_a cm end @@ -212,21 +219,26 @@ def correlation_matrix_optimized(ds) end def correlation_matrix_pairwise(ds) cache={} - cm=ds.collect_matrix do |row,col| - if row==col - 1.0 - elsif (ds[row].type!=:numeric or ds[col].type!=:numeric) - nil - else - if cache[[col,row]].nil? - r=pearson(ds[row],ds[col]) - cache[[row,col]]=r - r + vectors = ds.vectors.to_a + cm = vectors.collect do |row| + vectors.collect do |col| + if row==col + 1.0 + elsif (ds[row].type!=:numeric or ds[col].type!=:numeric) + nil else - cache[[col,row]] - end + if cache[[col,row]].nil? + r=pearson(ds[row],ds[col]) + cache[[row,col]]=r + r + else + cache[[col,row]] + end + end end end + + Matrix.rows cm end # Retrieves the n valid pairwise. @@ -256,27 +268,27 @@ def correlation_probability_matrix(ds, tails=:both) # Spearman ranked correlation coefficient (rho) between 2 vectors def spearman(v1,v2) - v1a,v2a=Statsample.only_valid_clone(v1,v2) - v1r,v2r=v1a.ranked(:numeric),v2a.ranked(:numeric) + v1a,v2a = Statsample.only_valid_clone(v1,v2) + v1r,v2r = v1a.ranked, v2a.ranked pearson(v1r,v2r) end # Calculate Point biserial correlation. Equal to Pearson correlation, with # one dichotomous value replaced by "0" and the other by "1" def point_biserial(dichotomous,continous) - ds={'d'=>dichotomous,'c'=>continous}.to_dataset.dup_only_valid - raise(TypeError, "First vector should be dichotomous") if ds['d'].factors.size!=2 - raise(TypeError, "Second vector should be continous") if ds['c'].type!=:numeric - f0=ds['d'].factors.sort[0] - m0=ds.filter_field('c') {|c| c['d']==f0} - m1=ds.filter_field('c') {|c| c['d']!=f0} - ((m1.mean-m0.mean).to_f / ds['c'].sdp) * Math::sqrt(m0.size*m1.size.to_f / ds.cases**2) + ds = Daru::DataFrame.new({:d=>dichotomous,:c=>continous}).dup_only_valid + raise(TypeError, "First vector should be dichotomous") if ds[:d].factors.size != 2 + raise(TypeError, "Second vector should be continous") if ds[:c].type != :numeric + f0=ds[:d].factors.sort.to_a[0] + m0=ds.filter_vector(:c) {|c| c[:d] == f0} + m1=ds.filter_vector(:c) {|c| c[:d] != f0} + ((m1.mean-m0.mean).to_f / ds[:c].sdp) * Math::sqrt(m0.size*m1.size.to_f / ds.nrows**2) end # Kendall Rank Correlation Coefficient (Tau a) # Based on Hervé Adbi article def tau_a(v1,v2) v1a,v2a=Statsample.only_valid_clone(v1,v2) n=v1.size - v1r,v2r=v1a.ranked(:numeric),v2a.ranked(:numeric) + v1r,v2r=v1a.ranked,v2a.ranked o1=ordered_pairs(v1r) o2=ordered_pairs(v2r) delta= o1.size*2-(o2 & o1).size*2 @@ -348,14 +360,15 @@ def pairs(matrix) } {'P'=>conc,'Q'=>disc,'Y'=>ties_y,'X'=>ties_x} end + def ordered_pairs(vector) - d=vector.data - a=[] - (0...(d.size-1)).each{|i| - ((i+1)...(d.size)).each {|j| + d = vector.to_a + a = [] + (0...(d.size-1)).each do |i| + ((i+1)...(d.size)).each do |j| a.push([d[i],d[j]]) - } - } + end + end a end =begin diff --git a/lib/statsample/crosstab.rb b/lib/statsample/crosstab.rb index 75cf075..981f550 100644 --- a/lib/statsample/crosstab.rb +++ b/lib/statsample/crosstab.rb @@ -10,16 +10,18 @@ class Crosstab def initialize(v1, v2, opts=Hash.new) #raise ArgumentError, "Both arguments should be Vectors" unless v1.is_a? Statsample::Vector and v2.is_a? Statsample::Vector raise ArgumentError, "Vectors should be the same size" unless v1.size==v2.size - @v_rows, @v_cols=Statsample.only_valid_clone(v1.to_vector,v2.to_vector) - @cases=@v_rows.size - @row_label=v1.name - @column_label=v2.name - @name=nil + @v_rows, @v_cols = Statsample.only_valid_clone( + Daru::Vector.new(v1), + Daru::Vector.new(v2)) + @cases = @v_rows.size + @row_label = v1.name + @column_label = v2.name + @name = nil @percentage_row = @percentage_column = @percentage_total=false - opts.each{|k,v| + opts.each do |k,v| self.send("#{k}=",v) if self.respond_to? k - } - @name||=_("Crosstab %s - %s") % [@row_label, @column_label] + end + @name ||= _("Crosstab %s - %s") % [@row_label, @column_label] end def rows_names @v_rows.factors.sort @@ -35,18 +37,18 @@ def cols_total end def frequencies - base=rows_names.inject([]){|s,row| - s+=cols_names.collect{|col| [row,col]} - }.inject({}) {|s,par| + base = rows_names.inject([]) do |s,row| + s += cols_names.collect { |col| [row,col] } + end.inject({}) do |s,par| s[par]=0 s - } + end base.update(Statsample::vector_cols_matrix(@v_rows,@v_cols).to_a.to_vector.frequencies) end def to_matrix - f=frequencies - rn=rows_names - cn=cols_names + f = frequencies + rn = rows_names + cn = cols_names Matrix.rows(rn.collect{|row| cn.collect{|col| f[[row,col]]} }) diff --git a/lib/statsample/dataset.rb b/lib/statsample/dataset.rb index e5e2e6a..2774975 100644 --- a/lib/statsample/dataset.rb +++ b/lib/statsample/dataset.rb @@ -779,7 +779,7 @@ def filter # creates a new vector with the data of a given field which the block returns true def filter_field(field) - a=[] + a = [] each do |c| a.push(c[field]) if yield c end diff --git a/lib/statsample/vector.rb b/lib/statsample/vector.rb index fb5476b..2ebdec7 100644 --- a/lib/statsample/vector.rb +++ b/lib/statsample/vector.rb @@ -5,7 +5,7 @@ module Statsample::VectorShorthands # Creates a new Statsample::Vector object # Argument should be equal to Vector.new def to_vector(*args) - Statsample::Vector.new(self,*args) + Daru::Vector.new(self) end # Creates a new Statsample::Vector object of type :scale. @@ -16,7 +16,7 @@ def to_scale(*args) end def to_numeric(*args) - Statsample::Vector.new(self, :numeric, *args) + Daru::Vector.new(self) end end diff --git a/test/test_bivariate.rb b/test/test_bivariate.rb index 82ea824..f6091f7 100644 --- a/test/test_bivariate.rb +++ b/test/test_bivariate.rb @@ -1,38 +1,38 @@ require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleBivariateTestCase < Minitest::Test should 'method sum of squares should be correct' do - v1 = [1, 2, 3, 4, 5, 6].to_vector(:numeric) - v2 = [6, 2, 4, 10, 12, 8].to_vector(:numeric) + v1 = Daru::Vector.new([1, 2, 3, 4, 5, 6]) + v2 = Daru::Vector.new([6, 2, 4, 10, 12, 8]) assert_equal(23.0, Statsample::Bivariate.sum_of_squares(v1, v2)) end should_with_gsl 'return same covariance with ruby and gls implementation' do - v1 = 20.times.collect { |_a| rand }.to_numeric - v2 = 20.times.collect { |_a| rand }.to_numeric + v1 = Daru::Vector.new(20.times.collect { |_a| rand }) + v2 = Daru::Vector.new(20.times.collect { |_a| rand }) assert_in_delta(Statsample::Bivariate.covariance(v1, v2), Statsample::Bivariate.covariance_slow(v1, v2), 0.001) end should_with_gsl 'return same correlation with ruby and gls implementation' do - v1 = 20.times.collect { |_a| rand }.to_numeric - v2 = 20.times.collect { |_a| rand }.to_numeric + v1 = Daru::Vector.new(20.times.collect { |_a| rand }) + v2 = Daru::Vector.new(20.times.collect { |_a| rand }) - assert_in_delta(GSL::Stats.correlation(v1.gsl, v2.gsl), Statsample::Bivariate.pearson_slow(v1, v2), 1e-10) + assert_in_delta(GSL::Stats.correlation(v1.to_gsl, v2.to_gsl), Statsample::Bivariate.pearson_slow(v1, v2), 1e-10) end should 'return correct pearson correlation' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) + v1 = Daru::Vector.new([6, 5, 4, 7, 8, 4, 3, 2]) + v2 = Daru::Vector.new([2, 3, 7, 8, 6, 4, 3, 2]) assert_in_delta(0.525, Statsample::Bivariate.pearson(v1, v2), 0.001) assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v1, v2), 0.001) - v3 = [6, 2, 1000, 1000, 5, 4, 7, 8, 4, 3, 2, nil].to_vector(:numeric) - v4 = [2, nil, nil, nil, 3, 7, 8, 6, 4, 3, 2, 500].to_vector(:numeric) + v3 = Daru::Vector.new([6, 2, 1000, 1000, 5, 4, 7, 8, 4, 3, 2, nil]) + v4 = Daru::Vector.new([2, nil, nil, nil, 3, 7, 8, 6, 4, 3, 2, 500]) assert_in_delta(0.525, Statsample::Bivariate.pearson(v3, v4), 0.001) # Test ruby method v3a, v4a = Statsample.only_valid v3, v4 assert_in_delta(0.525, Statsample::Bivariate.pearson_slow(v3a, v4a), 0.001) end should 'return correct values for t_pearson and prop_pearson' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) + v1 = Daru::Vector.new([6, 5, 4, 7, 8, 4, 3, 2]) + v2 = Daru::Vector.new([2, 3, 7, 8, 6, 4, 3, 2]) r = Statsample::Bivariate::Pearson.new(v1, v2) assert_in_delta(0.525, r.r, 0.001) assert_in_delta(Statsample::Bivariate.t_pearson(v1, v2), r.t, 0.001) @@ -40,11 +40,11 @@ class StatsampleBivariateTestCase < Minitest::Test assert(r.summary.size > 0) end should 'return correct correlation_matrix with nils values' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) - v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:numeric) - v4 = [2, nil, nil, nil, 3, 7, 8, 6].to_vector(:numeric) - ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4 }.to_dataset + v1 = Daru::Vector.new([6, 5, 4, 7, 8, 4, 3, 2]) + v2 = Daru::Vector.new([2, 3, 7, 8, 6, 4, 3, 2]) + v3 = Daru::Vector.new([6, 2, 1000, 1000, 5, 4, 7, 8]) + v4 = Daru::Vector.new([2, nil, nil, nil, 3, 7, 8, 6]) + ds = Daru::DataFrame.new({ :v1 => v1, :v2 => v2, :v3 => v3, :v4 => v4 }) c = proc { |n1, n2| Statsample::Bivariate.pearson(n1, n2) } expected = Matrix[[c.call(v1, v1), c.call(v1, v2), c.call(v1, v3), c.call(v1, v4)], [c.call(v2, v1), c.call(v2, v2), c.call(v2, v3), c.call(v2, v4)], [c.call(v3, v1), c.call(v3, v2), c.call(v3, v3), c.call(v3, v4)], [c.call(v4, v1), c.call(v4, v2), c.call(v4, v3), c.call(v4, v4)] @@ -61,13 +61,13 @@ class StatsampleBivariateTestCase < Minitest::Test end should_with_gsl 'return same values for optimized and pairwise covariance matrix' do cases = 100 - v1 = Statsample::Vector.new_numeric(cases) { rand } - v2 = Statsample::Vector.new_numeric(cases) { rand } - v3 = Statsample::Vector.new_numeric(cases) { rand } - v4 = Statsample::Vector.new_numeric(cases) { rand } - v5 = Statsample::Vector.new_numeric(cases) { rand } + v1 = Daru::Vector.new_with_size(cases) { rand } + v2 = Daru::Vector.new_with_size(cases) { rand } + v3 = Daru::Vector.new_with_size(cases) { rand } + v4 = Daru::Vector.new_with_size(cases) { rand } + v5 = Daru::Vector.new_with_size(cases) { rand } - ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'v5' => v5 }.to_dataset + ds = Daru::DataFrame.new({ :v1 => v1, :v2 => v2, :v3 => v3, :v4 => v4, :v5 => v5 }) cor_opt = Statsample::Bivariate.covariance_matrix_optimized(ds) @@ -76,13 +76,14 @@ class StatsampleBivariateTestCase < Minitest::Test end should_with_gsl 'return same values for optimized and pairwise correlation matrix' do cases = 100 - v1 = Statsample::Vector.new_numeric(cases) { rand } - v2 = Statsample::Vector.new_numeric(cases) { rand } - v3 = Statsample::Vector.new_numeric(cases) { rand } - v4 = Statsample::Vector.new_numeric(cases) { rand } - v5 = Statsample::Vector.new_numeric(cases) { rand } + v1 = Daru::Vector.new_with_size(cases) { rand } + v2 = Daru::Vector.new_with_size(cases) { rand } + v3 = Daru::Vector.new_with_size(cases) { rand } + v4 = Daru::Vector.new_with_size(cases) { rand } + v5 = Daru::Vector.new_with_size(cases) { rand } - ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'v5' => v5 }.to_dataset + ds = Daru::DataFrame.new({ + :v1 => v1, :v2 => v2, :v3 => v3, :v4 => v4, :v5 => v5 }) cor_opt = Statsample::Bivariate.correlation_matrix_optimized(ds) @@ -90,11 +91,11 @@ class StatsampleBivariateTestCase < Minitest::Test assert_equal_matrix(cor_opt, cor_pw, 1e-15) end should 'return correct correlation_matrix without nils values' do - v1 = [6, 5, 4, 7, 8, 4, 3, 2].to_vector(:numeric) - v2 = [2, 3, 7, 8, 6, 4, 3, 2].to_vector(:numeric) - v3 = [6, 2, 1000, 1000, 5, 4, 7, 8].to_vector(:numeric) - v4 = [2, 4, 6, 7, 3, 7, 8, 6].to_vector(:numeric) - ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4 }.to_dataset + v1 = Daru::Vector.new([6, 5, 4, 7, 8, 4, 3, 2]) + v2 = Daru::Vector.new([2, 3, 7, 8, 6, 4, 3, 2]) + v3 = Daru::Vector.new([6, 2, 1000, 1000, 5, 4, 7, 8]) + v4 = Daru::Vector.new([2, 4, 6, 7, 3, 7, 8, 6]) + ds = Daru::DataFrame.new({ :v1 => v1, :v2 => v2, :v3 => v3, :v4 => v4 }) c = proc { |n1, n2| Statsample::Bivariate.pearson(n1, n2) } expected = Matrix[[c.call(v1, v1), c.call(v1, v2), c.call(v1, v3), c.call(v1, v4)], [c.call(v2, v1), c.call(v2, v2), c.call(v2, v3), c.call(v2, v4)], [c.call(v3, v1), c.call(v3, v2), c.call(v3, v3), c.call(v3, v4)], [c.call(v4, v1), c.call(v4, v2), c.call(v4, v3), c.call(v4, v4)] @@ -129,25 +130,25 @@ class StatsampleBivariateTestCase < Minitest::Test end should "return correct value for Spearman's rho" do - v1 = [86, 97, 99, 100, 101, 103, 106, 110, 112, 113].to_vector(:numeric) - v2 = [0, 20, 28, 27, 50, 29, 7, 17, 6, 12].to_vector(:numeric) + v1 =Daru::Vector.new( [86, 97, 99, 100, 101, 103, 106, 110, 112, 113]) + v2 =Daru::Vector.new( [0, 20, 28, 27, 50, 29, 7, 17, 6, 12]) assert_in_delta(-0.175758, Statsample::Bivariate.spearman(v1, v2), 0.0001) end should 'return correct value for point_biserial correlation' do - c = [1, 3, 5, 6, 7, 100, 200, 300, 400, 300].to_vector(:numeric) - d = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0].to_vector(:numeric) + c = Daru::Vector.new([1, 3, 5, 6, 7, 100, 200, 300, 400, 300]) + d = Daru::Vector.new([1, 1, 1, 1, 1, 0, 0, 0, 0, 0]) assert_raises TypeError do Statsample::Bivariate.point_biserial(c, d) end assert_in_delta(Statsample::Bivariate.point_biserial(d, c), Statsample::Bivariate.pearson(d, c), 0.0001) end should 'return correct value for tau_a and tau_b' do - v1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].to_vector(:numeric) - v2 = [1, 3, 4, 5, 7, 8, 2, 9, 10, 6, 11].to_vector(:numeric) + v1 = Daru::Vector.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + v2 = Daru::Vector.new([1, 3, 4, 5, 7, 8, 2, 9, 10, 6, 11]) assert_in_delta(0.6727, Statsample::Bivariate.tau_a(v1, v2), 0.001) assert_in_delta(0.6727, Statsample::Bivariate.tau_b((Statsample::Crosstab.new(v1, v2).to_matrix)), 0.001) - v1 = [12, 14, 14, 17, 19, 19, 19, 19, 19, 20, 21, 21, 21, 21, 21, 22, 23, 24, 24, 24, 26, 26, 27].to_vector(:numeric) - v2 = [11, 4, 4, 2, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0].to_vector(:numeric) + v1 = Daru::Vector.new([12, 14, 14, 17, 19, 19, 19, 19, 19, 20, 21, 21, 21, 21, 21, 22, 23, 24, 24, 24, 26, 26, 27]) + v2 = Daru::Vector.new([11, 4, 4, 2, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0]) assert_in_delta(-0.376201540231705, Statsample::Bivariate.tau_b(Statsample::Crosstab.new(v1, v2).to_matrix), 0.001) end should 'return correct value for gamma correlation' do From 1b2e8356d67a6a0033517179d901c9d5ea98b79f Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Wed, 20 May 2015 14:33:51 +0530 Subject: [PATCH 037/119] statsample::AnovaWithVectors now works with Daru::Vector and DataFrame --- lib/statsample/anova/oneway.rb | 6 +++--- lib/statsample/test/levene.rb | 39 +++++++++++++++++----------------- test/test_anovawithvectors.rb | 16 +++++++------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/statsample/anova/oneway.rb b/lib/statsample/anova/oneway.rb index c895318..6c095cf 100644 --- a/lib/statsample/anova/oneway.rb +++ b/lib/statsample/anova/oneway.rb @@ -90,10 +90,10 @@ class OneWayWithVectors < OneWay def initialize(*args) if args[0].is_a? Array - @vectors=args.shift + @vectors = args.shift else - @vectors=args.find_all {|v| v.is_a? Statsample::Vector} - opts=args.find {|v| v.is_a? Hash} + @vectors = args.find_all {|v| v.is_a? Daru::Vector} + opts = args.find {|v| v.is_a? Hash} end opts||=Hash.new opts_default={:name=>_("Anova One-Way"), diff --git a/lib/statsample/test/levene.rb b/lib/statsample/test/levene.rb index 4bbcd3f..7054c84 100644 --- a/lib/statsample/test/levene.rb +++ b/lib/statsample/test/levene.rb @@ -29,7 +29,7 @@ class Levene attr_accessor :name # Input could be an array of vectors or a dataset def initialize(input, opts=Hash.new()) - if input.is_a? Statsample::Dataset + if input.is_a? Daru::DataFrame @vectors=input.vectors.values else @vectors=input @@ -52,28 +52,29 @@ def compute zi=@vectors.collect {|vector| mean=vector.mean - vector.collect {|v| (v-mean).abs }.to_numeric + Daru::Vector.new(vector.collect {|v| (v-mean).abs }) } - total_mean=zi.inject([]) {|ac,vector| - ac+vector.valid_data - }.to_numeric.mean + total_mean = Daru::Vector.new( + zi.inject([]) do |ac,vector| + ac + vector.only_valid(:array) + end).mean - k=@vectors.size - - sum_num=zi.inject(0) {|ac,vector| - ac+(vector.size*(vector.mean-total_mean)**2) - } + k = @vectors.size + sum_num = zi.inject(0) do |ac,vector| + ac + (vector.size * (vector.mean - total_mean)**2) + end - sum_den=zi.inject(0) {|ac,vector| - z_mean=vector.mean - ac+vector.valid_data.inject(0) {|acp,zij| - acp+(zij-z_mean)**2 - } - } - @w=((n-k)*sum_num).quo((k-1)*sum_den) - @d1=k-1 - @d2=n-k + sum_den = zi.inject(0) do |ac,vector| + z_mean = vector.mean + ac + vector.only_valid(:array).inject(0) do |acp,zij| + acp + (zij - z_mean)**2 + end + end + + @w = ((n - k) * sum_num).quo((k - 1) * sum_den) + @d1 = k - 1 + @d2 = n - k end private :compute # Probability. diff --git a/test/test_anovawithvectors.rb b/test/test_anovawithvectors.rb index ae38f66..9da0380 100644 --- a/test/test_anovawithvectors.rb +++ b/test/test_anovawithvectors.rb @@ -3,9 +3,9 @@ class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::OneWayWithVectors) do context('when initializing') do setup do - @v1 = 10.times.map { rand(100) }.to_numeric - @v2 = 10.times.map { rand(100) }.to_numeric - @v3 = 10.times.map { rand(100) }.to_numeric + @v1 = Daru::Vector.new(10.times.map { rand(100) }) + @v2 = Daru::Vector.new(10.times.map { rand(100) }) + @v3 = Daru::Vector.new(10.times.map { rand(100) }) end should 'be the same using [] or args*' do a1 = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3) @@ -28,9 +28,9 @@ class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test end end setup do - @v1 = [3, 3, 2, 3, 6].to_vector(:numeric) - @v2 = [7, 6, 5, 6, 7].to_vector(:numeric) - @v3 = [9, 8, 9, 7, 8].to_vector(:numeric) + @v1 = Daru::Vector.new([3, 3, 2, 3, 6]) + @v2 = Daru::Vector.new([7, 6, 5, 6, 7]) + @v3 = Daru::Vector.new([9, 8, 9, 7, 8]) @name = 'Anova testing' @anova = Statsample::Anova::OneWayWithVectors.new(@v1, @v2, @v3, name: @name) end @@ -66,10 +66,10 @@ class StatsampleAnovaOneWayWithVectorsTestCase < Minitest::Test assert_in_delta(@anova.sst, @anova.sswg + @anova.ssbg, 0.00001) end should 'df total equal to number of n-1' do - assert_equal(@v1.n + @v2.n + @v3.n - 1, @anova.df_total) + assert_equal(@v1.size + @v2.size + @v3.size - 1, @anova.df_total) end should 'df wg equal to number of n-k' do - assert_equal(@v1.n + @v2.n + @v3.n - 3, @anova.df_wg) + assert_equal(@v1.size + @v2.size + @v3.size - 3, @anova.df_wg) end should 'df bg equal to number of k-1' do assert_equal(2, @anova.df_bg) From 14afdb518c88465cea6bffe73849c340e6d7651c Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 21 May 2015 00:54:59 +0530 Subject: [PATCH 038/119] Statsample::Multiset now works with Daru::DataFrame and Vector Statsample::Multiset now works with Daru::DataFrame and Vector --- lib/statsample.rb | 1 + lib/statsample/daru.rb | 82 ++++++++++++++++ lib/statsample/multiset.rb | 71 +++++++------- test/test_multiset.rb | 195 ++++++++++++++++++++----------------- 4 files changed, 223 insertions(+), 126 deletions(-) create mode 100644 lib/statsample/daru.rb diff --git a/lib/statsample.rb b/lib/statsample.rb index 7a0c8e2..3e14968 100644 --- a/lib/statsample.rb +++ b/lib/statsample.rb @@ -23,6 +23,7 @@ require 'dirty-memoize' require 'reportbuilder' require 'daru' +require 'statsample/daru' class Numeric def square diff --git a/lib/statsample/daru.rb b/lib/statsample/daru.rb new file mode 100644 index 0000000..029a0fc --- /dev/null +++ b/lib/statsample/daru.rb @@ -0,0 +1,82 @@ +# Opening the Daru::DataFrame class for adding methods to convert from +# data structures to specialized statsample data structues like Multiset. +module Daru + class DataFrame + # Functions for converting to Statsample::Multiset + def to_multiset_by_split(*vecs) + require 'statsample/multiset' + + if vecs.size == 1 + to_multiset_by_split_one_field(vecs[0]) + else + to_multiset_by_split_multiple_fields(*vecs) + end + end + # Creates a Statsample::Multiset, using one field + + def to_multiset_by_split_one_field(field) + raise ArgumentError,"Should use a correct field name" if + !@vectors.include? field + + factors = self[field].factors + ms = Statsample::Multiset.new_empty_vectors(@vectors.to_a, factors) + each_row do |row| + ms[row[field]].add_row(row) + end + #puts "Ingreso a los dataset" + ms.datasets.each do |k,ds| + ds.update + # puts "idx #{self[field].index_of(k)}" + ds.rename self[field].index_of(k) + # ds.vectors.each do |k1,v1| + # v1.type = self[k1].type + # v1.name = self[k1].name + # v1.labels = self[k1].to_hash + # end + end + + ms + end + + def to_multiset_by_split_multiple_fields(*fields) + fields.map!(&:to_sym) + factors_total=nil + fields.each do |f| + if factors_total.nil? + factors_total = self[f].factors.collect { |c| [c] } + else + suma = [] + factors = self[f].factors + factors_total.each do |f1| + factors.each do |f2| + suma.push(f1+[f2]) + end + end + factors_total = suma + end + end + ms = Statsample::Multiset.new_empty_vectors(vectors.to_a, factors_total) + + p1 = eval "Proc.new {|c| ms[["+fields.collect{|f| "c['#{f}'.to_sym]"}.join(",")+"]].add_row(c) }" + each_row { |r| p1.call(r) } + + ms.datasets.each do |k,ds| + ds.update + ds.rename( + fields.size.times.map do |i| + f = fields[i] + sk = k[i] + self[f].index_of(sk) + end.join("-") + ) + + # ds.vectors.each do |k1,v1| + # v1.type = ds[k1].type + # v1.name = ds[k1].name + # v1.labels = ds[k1].to_hash + # end + end + ms + end + end +end \ No newline at end of file diff --git a/lib/statsample/multiset.rb b/lib/statsample/multiset.rb index e7cbe4f..eaf19f7 100644 --- a/lib/statsample/multiset.rb +++ b/lib/statsample/multiset.rb @@ -14,11 +14,12 @@ def initialize(fields) @datasets={} end def self.new_empty_vectors(fields,ds_names) - ms=Multiset.new(fields) - ds_names.each{|d| - ms.add_dataset(d,Dataset.new(fields)) - } - ms + ms = Multiset.new(fields) + ds_names.each do |d| + ms.add_dataset(d, Daru::DataFrame.new({}, order: fields)) + end + + ms end # Generate a new dataset as a union of partial dataset # If block given, this is applied to each dataset before union @@ -29,65 +30,65 @@ def union(&block) labels={} each do |k,ds| if block - ds=ds.dup + ds = ds.dup yield k,ds end @fields.each do |f| - union_field[f]||=Array.new - union_field[f].concat(ds[f].data) - types[f]||=ds[f].type - names[f]||=ds[f].name - labels[f]||=ds[f].labels + union_field[f] ||= Array.new + union_field[f].concat(ds[f].to_a) + types[f] ||= ds[f].type + names[f] ||= ds[f].name + labels[f] ||= ds[f].index.to_a end end @fields.each do |f| - union_field[f]=union_field[f].to_vector(types[f]) - union_field[f].name=names[f] - union_field[f].labels=labels[f] + union_field[f] = Daru::Vector.new(union_field[f]) + union_field[f].rename names[f] end - ds_union=union_field.to_dataset - ds_union.fields=@fields + + ds_union = Daru::DataFrame.new(union_field, order: @fields) ds_union end + def datasets_names - @datasets.keys.sort + @datasets.keys.sort end + def n_datasets - @datasets.size + @datasets.size end + def add_dataset(key,ds) - if(ds.fields!=@fields) - raise ArgumentError, "Dataset(#{ds.fields.to_s})must have the same fields of the Multiset(#{@fields})" + if ds.vectors.to_a != @fields + raise ArgumentError, "Dataset(#{ds.vectors.to_a.to_s})must have the same fields of the Multiset(#{@fields})" else - @datasets[key]=ds + @datasets[key] = ds end end def sum_field(field) @datasets.inject(0) {|a,da| - stratum_name=da[0] - vector=da[1][field] - val=yield stratum_name,vector - a+val + stratum_name = da[0] + vector = da[1][field] + val = yield stratum_name,vector + a + val } end def collect_vector(field) - @datasets.collect {|k,v| - yield k, v[field] - } + @datasets.collect { |k,v| yield k, v[field] } end def each_vector(field) - @datasets.each {|k,v| - yield k, v[field] - } + @datasets.each { |k,v| yield k, v[field] } end - def[](i) + + def [](i) @datasets[i] end + def each(&block) @datasets.each {|k,ds| - next if ds.cases==0 + next if ds.nrows == 0 block.call(k,ds) } end @@ -204,9 +205,9 @@ def initialize(ms,strata_sizes) @ms=ms raise ArgumentError,"You should put a strata size for each dataset" if strata_sizes.keys.sort!=ms.datasets_names @strata_sizes=strata_sizes - @population_size=@strata_sizes.inject(0) {|a,x| a+x[1]} + @population_size=@strata_sizes.inject(0) { |a,x| a+x[1] } @strata_number=@ms.n_datasets - @sample_size=@ms.datasets.inject(0) {|a,x| a+x[1].cases} + @sample_size=@ms.datasets.inject(0) { |a,x| a+x[1].nrows } end # Number of strata def strata_number diff --git a/test/test_multiset.rb b/test/test_multiset.rb index 43c5c27..7543ac0 100644 --- a/test/test_multiset.rb +++ b/test/test_multiset.rb @@ -2,122 +2,135 @@ class StatsampleMultisetTestCase < Minitest::Test def setup - @x = %w(a a a a b b b b).to_vector - @y = [1, 2, 3, 4, 5, 6, 7, 8].to_numeric - @z = [10, 11, 12, 13, 14, 15, 16, 17].to_numeric - @ds = { 'x' => @x, 'y' => @y, 'z' => @z }.to_dataset - @ms = @ds.to_multiset_by_split('x') + @x = Daru::Vector.new(%w(a a a a b b b b)) + @y = Daru::Vector.new([1, 2, 3, 4, 5, 6, 7, 8]) + @z = Daru::Vector.new([10, 11, 12, 13, 14, 15, 16, 17]) + @ds = Daru::DataFrame.new({ :x => @x, :y => @y, :z => @z }) + @ms = @ds.to_multiset_by_split(:x) end def test_creation - v1a = [1, 2, 3, 4, 5].to_vector - v2b = [11, 21, 31, 41, 51].to_vector - v3c = [21, 23, 34, 45, 56].to_vector - ds1 = { 'v1' => v1a, 'v2' => v2b, 'v3' => v3c }.to_dataset - v1b = [15, 25, 35, 45, 55].to_vector - v2b = [11, 21, 31, 41, 51].to_vector - v3b = [21, 23, 34, 45, 56].to_vector - ds2 = { 'v1' => v1b, 'v2' => v2b, 'v3' => v3b }.to_dataset - ms = Statsample::Multiset.new(%w(v1 v2 v3)) - ms.add_dataset('ds1', ds1) - ms.add_dataset('ds2', ds2) - assert_equal(ds1, ms['ds1']) - assert_equal(ds2, ms['ds2']) - assert_equal(v1a, ms['ds1']['v1']) - assert_not_equal(v1b, ms['ds1']['v1']) - ds3 = { 'v1' => v1b, 'v2' => v2b }.to_dataset + v1a = Daru::Vector.new([1, 2, 3, 4, 5]) + v2b = Daru::Vector.new([11, 21, 31, 41, 51]) + v3c = Daru::Vector.new([21, 23, 34, 45, 56]) + ds1 = Daru::DataFrame.new({ :v1 => v1a, :v2 => v2b, :v3 => v3c }) + v1b = Daru::Vector.new([15, 25, 35, 45, 55]) + v2b = Daru::Vector.new([11, 21, 31, 41, 51]) + v3b = Daru::Vector.new([21, 23, 34, 45, 56]) + ds2 = Daru::DataFrame.new({ :v1 => v1b, :v2 => v2b, :v3 => v3b }) + ms = Statsample::Multiset.new([:v1, :v2, :v3]) + ms.add_dataset(:ds1, ds1) + ms.add_dataset(:ds2, ds2) + assert_equal(ds1, ms[:ds1]) + assert_equal(ds2, ms[:ds2]) + assert_equal(v1a, ms[:ds1][:v1]) + assert_not_equal(v1b, ms[:ds1][:v1]) + ds3 = Daru::DataFrame.new({ :v1 => v1b, :v2 => v2b }) assert_raise ArgumentError do ms.add_dataset(ds3) end end def test_creation_empty - ms = Statsample::Multiset.new_empty_vectors(%w(id age name), %w(male female)) - ds_male = { 'id' => [].to_vector, 'age' => [].to_vector, 'name' => [].to_vector }.to_dataset(%w(id age name)) - ds_female = { 'id' => [].to_vector, 'age' => [].to_vector, 'name' => [].to_vector }.to_dataset(%w(id age name)) - ms2 = Statsample::Multiset.new(%w(id age name)) - ms2.add_dataset('male', ds_male) - ms2.add_dataset('female', ds_female) + ms = Statsample::Multiset.new_empty_vectors([:id, :age, :name], [:male, :female]) + ds_male = Daru::DataFrame.new({ + :id => Daru::Vector.new([]), + :age => Daru::Vector.new([]), + :name => Daru::Vector.new([]) + }, order: [:id, :age, :name]) + + ds_female = Daru::DataFrame.new({ + :id => Daru::Vector.new([]), + :age => Daru::Vector.new([]), + :name => Daru::Vector.new([]) + }, order: [:id, :age, :name]) + + ms2 = Statsample::Multiset.new([:id, :age, :name]) + ms2.add_dataset(:male, ds_male) + ms2.add_dataset(:female, ds_female) assert_equal(ms2.fields, ms.fields) - assert_equal(ms2['male'], ms['male']) - assert_equal(ms2['female'], ms['female']) + assert_equal(ms2[:male], ms[:male]) + assert_equal(ms2[:female], ms[:female]) end def test_to_multiset_by_split_one - sex = %w(m m m m m f f f f m).to_vector(:object) - city = %w(London Paris NY London Paris NY London Paris NY Tome).to_vector(:object) - age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:numeric) - ds = { 'sex' => sex, 'city' => city, 'age' => age }.to_dataset - ms = ds.to_multiset_by_split('sex') + sex = Daru::Vector.new(%w(m m m m m f f f f m)) + city = Daru::Vector.new(%w(London Paris NY London Paris NY London Paris NY Tome)) + age = Daru::Vector.new([10, 10, 20, 30, 34, 34, 33, 35, 36, 40]) + ds = Daru::DataFrame.new({ :sex => sex, :city => city, :age => age }) + ms = ds.to_multiset_by_split(:sex) assert_equal(2, ms.n_datasets) assert_equal(%w(f m), ms.datasets.keys.sort) - assert_equal(6, ms['m'].cases) - assert_equal(4, ms['f'].cases) - assert_equal(%w(London Paris NY London Paris Tome), ms['m']['city'].to_a) - assert_equal([34, 33, 35, 36], ms['f']['age'].to_a) + assert_equal(6, ms['m'].nrows) + assert_equal(4, ms['f'].nrows) + assert_equal(%w(London Paris NY London Paris Tome), ms['m'][:city].to_a) + assert_equal([34, 33, 35, 36], ms['f'][:age].to_a) end def test_to_multiset_by_split_multiple - sex = %w(m m m m m m m m m m f f f f f f f f f f).to_vector(:object) - city = %w(London London London Paris Paris London London London Paris Paris London London London Paris Paris London London London Paris Paris).to_vector(:object) - hair = %w(blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black).to_vector(:object) - age = [10, 10, 20, 30, 34, 34, 33, 35, 36, 40, 10, 10, 20, 30, 34, 34, 33, 35, 36, 40].to_vector(:numeric) - ds = { 'sex' => sex, 'city' => city, 'hair' => hair, 'age' => age }.to_dataset(%w(sex city hair age)) - ms = ds.to_multiset_by_split('sex', 'city', 'hair') + sex = Daru::Vector.new(%w(m m m m m m m m m m f f f f f f f f f f)) + city = Daru::Vector.new(%w(London London London Paris Paris London London London Paris Paris London London London Paris Paris London London London Paris Paris)) + hair = Daru::Vector.new(%w(blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black blonde blonde black black)) + age = Daru::Vector.new([10, 10, 20, 30, 34, 34, 33, 35, 36, 40, 10, 10, 20, 30, 34, 34, 33, 35, 36, 40]) + ds = Daru::DataFrame.new({ + :sex => sex, :city => city, :hair => hair, :age => age + }, order: [:sex, :city, :hair, :age]) + ms = ds.to_multiset_by_split(:sex, :city, :hair) assert_equal(8, ms.n_datasets) - assert_equal(3, ms[%w(m London blonde)].cases) - assert_equal(3, ms[%w(m London blonde)].cases) - assert_equal(1, ms[%w(m Paris black)].cases) + assert_equal(3, ms[%w(m London blonde)].nrows) + assert_equal(3, ms[%w(m London blonde)].nrows) + assert_equal(1, ms[%w(m Paris black)].nrows) end def test_stratum_proportion - ds1 = { 'q1' => [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0].to_vector }.to_dataset - ds2 = { 'q1' => [1, 1, 1, 1, 1, 1, 1, 0, 0].to_vector }.to_dataset - assert_equal(5.0 / 12, ds1['q1'].proportion) - assert_equal(7.0 / 9, ds2['q1'].proportion) - ms = Statsample::Multiset.new(['q1']) - ms.add_dataset('d1', ds1) - ms.add_dataset('d2', ds2) - ss = Statsample::StratifiedSample.new(ms, 'd1' => 50, 'd2' => 100) - assert_in_delta(0.655, ss.proportion('q1'), 0.01) - assert_in_delta(0.345, ss.proportion('q1', 0), 0.01) + ds1 = Daru::DataFrame.new({ :q1 => Daru::Vector.new([1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]) }) + ds2 = Daru::DataFrame.new({ :q1 => Daru::Vector.new([1, 1, 1, 1, 1, 1, 1, 0, 0]) }) + assert_equal(5.0 / 12, ds1[:q1].proportion) + assert_equal(7.0 / 9, ds2[:q1].proportion) + ms = Statsample::Multiset.new([:q1]) + ms.add_dataset(:d1, ds1) + ms.add_dataset(:d2, ds2) + ss = Statsample::StratifiedSample.new(ms, :d1 => 50, :d2 => 100) + assert_in_delta(0.655, ss.proportion(:q1), 0.01) + assert_in_delta(0.345, ss.proportion(:q1, 0), 0.01) end def test_stratum_scale - boys = { 'test' => [50, 55, 60, 62, 62, 65, 67, 67, 70, 70, 73, 73, 75, 78, 78, 80, 85, 90].to_vector(:numeric) }.to_dataset - girls = { 'test' => [70, 70, 72, 72, 75, 75, 78, 78, 80, 80, 82, 82, 85, 85, 88, 88, 90, 90].to_vector(:numeric) }.to_dataset - ms = Statsample::Multiset.new(['test']) - ms.add_dataset('boys', boys) - ms.add_dataset('girls', girls) - ss = Statsample::StratifiedSample.new(ms, 'boys' => 10_000, 'girls' => 10_000) + boys = Daru::DataFrame.new({ :test => Daru::Vector.new([50, 55, 60, 62, 62, 65, 67, 67, 70, 70, 73, 73, 75, 78, 78, 80, 85, 90]) }) + girls =Daru::DataFrame.new({ :test => Daru::Vector.new( [70, 70, 72, 72, 75, 75, 78, 78, 80, 80, 82, 82, 85, 85, 88, 88, 90, 90]) }) + ms = Statsample::Multiset.new([:test]) + ms.add_dataset(:boys, boys) + ms.add_dataset(:girls, girls) + ss = Statsample::StratifiedSample.new(ms, :boys => 10_000, :girls => 10_000) assert_equal(2, ss.strata_number) assert_equal(20_000, ss.population_size) - assert_equal(10_000, ss.stratum_size('boys')) - assert_equal(10_000, ss.stratum_size('girls')) + assert_equal(10_000, ss.stratum_size(:boys)) + assert_equal(10_000, ss.stratum_size(:girls)) assert_equal(36, ss.sample_size) - assert_equal(75, ss.mean('test')) - assert_in_delta(1.45, ss.standard_error_wor('test'), 0.01) - assert_in_delta(ss.standard_error_wor('test'), ss.standard_error_wor_2('test'), 0.00001) + assert_equal(75, ss.mean(:test)) + assert_in_delta(1.45, ss.standard_error_wor(:test), 0.01) + assert_in_delta(ss.standard_error_wor(:test), ss.standard_error_wor_2(:test), 0.00001) end def test_each xpe = { - 'a' => %w(a a a a).to_vector, - 'b' => %w(b b b b).to_vector + 'a' => Daru::Vector.new(%w(a a a a)), + 'b' => Daru::Vector.new(%w(b b b b)) } ype = { - 'a' => [1, 2, 3, 4].to_numeric, - 'b' => [5, 6, 7, 8].to_numeric + 'a' => Daru::Vector.new([1, 2, 3, 4]), + 'b' => Daru::Vector.new([5, 6, 7, 8]) } zpe = { - 'a' => [10, 11, 12, 13].to_numeric, - 'b' => [14, 15, 16, 17].to_numeric + 'a' => Daru::Vector.new([10, 11, 12, 13]), + 'b' => Daru::Vector.new([14, 15, 16, 17]) } xp, yp, zp = {}, {}, {} @ms.each {|k, ds| - xp[k] = ds['x'] - yp[k] = ds['y'] - zp[k] = ds['z'] + # puts "k #{k} ds #{ds}" + xp[k] = ds[:x] + yp[k] = ds[:y] + zp[k] = ds[:z] } assert_equal(xpe, xp) assert_equal(ype, yp) @@ -127,38 +140,38 @@ def test_each def test_multiset_union_with_block r1 = rand r2 = rand - ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_numeric + ye = Daru::Vector.new([1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2]) - ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_numeric + ze = Daru::Vector.new([10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2]) ds2 = @ms.union {|k, ds| - ds['y'].recode!{|v| + ds[:y].recode!{|v| k == 'a' ? v * r1 : v * r2 } - ds['z'].recode!{|v| + ds[:z].recode!{|v| k == 'a' ? v * r1 : v * r2 } } - assert_equal(ye, ds2['y']) - assert_equal(ze, ds2['z']) + assert_equal(ye, ds2[:y]) + assert_equal(ze, ds2[:z]) end def test_multiset_union r1 = rand r2 = rand - ye = [1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2].to_numeric + ye = Daru::Vector.new([1 * r1, 2 * r1, 3 * r1, 4 * r1, 5 * r2, 6 * r2, 7 * r2, 8 * r2]) + ze = Daru::Vector.new([10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2]) - ze = [10 * r1, 11 * r1, 12 * r1, 13 * r1, 14 * r2, 15 * r2, 16 * r2, 17 * r2].to_numeric - @ms.each {|k, ds| - ds['y'].recode!{|v| + @ms.each do |k, ds| + ds[:y].recode! { |v| k == 'a' ? v * r1 : v * r2 } - ds['z'].recode!{|v| + ds[:z].recode! {|v| k == 'a' ? v * r1 : v * r2 } - } + end ds2 = @ms.union - assert_equal(ye, ds2['y']) - assert_equal(ze, ds2['z']) + assert_equal(ye, ds2[:y]) + assert_equal(ze, ds2[:z]) end end From 3422d4e83f6e014cc1550bd2eec7e74ad58b88bd Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 21 May 2015 00:55:40 +0530 Subject: [PATCH 039/119] Anova::TwoWayWithVectors now works with daru --- lib/statsample/anova/twoway.rb | 44 +++++++++++++++-------------- test/test_anovatwowaywithdataset.rb | 16 +++++------ 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/lib/statsample/anova/twoway.rb b/lib/statsample/anova/twoway.rb index e8340dd..0b4450b 100644 --- a/lib/statsample/anova/twoway.rb +++ b/lib/statsample/anova/twoway.rb @@ -121,25 +121,26 @@ class TwoWayWithVectors < TwoWay # For now, only equal sample cells allowed def initialize(opts=Hash.new) raise "You should insert at least :a, :b and :dependent" unless [:a, :b, :dependent].all? {|v| opts.has_key? v} - @a_var='a' - @b_var='b' - @dep_var='dependent' - @a_vector, @b_vector, @dep_vector=Statsample.only_valid_clone opts[:a], opts[:b], opts[:dependent] + @a_var = :a + @b_var = :b + @dep_var = :dependent + @a_vector, @b_vector, @dep_vector = + Statsample.only_valid_clone opts[:a], opts[:b], opts[:dependent] - ds={@a_var=>@a_vector, @b_var=>@b_vector, @dep_var=>@dep_vector}.to_dataset - @ds=ds.clone_only_valid - _p=@a_vector.factors.size - _q=@b_vector.factors.size - @x_general=@dep_vector.mean - @axb_means={} - @axb_sd={} - @vectors=[] + ds = Daru::DataFrame.new({@a_var=>@a_vector, @b_var=>@b_vector, @dep_var=>@dep_vector}) + @ds = ds.clone_only_valid + _p = @a_vector.factors.size + _q = @b_vector.factors.size + @x_general = @dep_vector.mean + @axb_means = {} + @axb_sd = {} + @vectors = [] n=nil @ds.to_multiset_by_split(a_var,b_var).each_vector(dep_var) {|k,v| - @axb_means[k]=v.mean - @axb_sd[k]=v.sd + @axb_means[k] = v.mean + @axb_sd[k] = v.sd @vectors << v - n||=v.size + n ||= v.size raise "All cell sizes should be equal" if n!=v.size } @@ -151,20 +152,21 @@ def initialize(opts=Hash.new) @ds.to_multiset_by_split(b_var).each_vector(dep_var) {|k,v| @b_means[k]=v.mean } - ss_a=n*_q*@ds[a_var].factors.inject(0) {|ac,v| - ac+(@a_means[v]-@x_general)**2 + ss_a = n*_q*@ds[a_var].factors.inject(0) {|ac,v| + ac + (@a_means[v]-@x_general)**2 } ss_b=n*_p*@ds[b_var].factors.inject(0) {|ac,v| ac+(@b_means[v]-@x_general)**2 } - ss_within=@ds.collect {|row| + ss_within = @ds.collect(:row) { |row| (row[dep_var]-@axb_means[[row[a_var],row[b_var]]])**2 }.sum - ss_axb=n*@axb_means.inject(0) {|ac,v| + ss_axb = n*@axb_means.inject(0) {|ac,v| j,k=v[0] xjk=v[1] ac+(xjk-@a_means[j]-@b_means[k]+@x_general)**2 } + df_a=_p-1 df_b=_q-1 df_within=(_p*_q)*(n-1) @@ -186,9 +188,9 @@ def levene def report_building(builder) #:nodoc:# builder.section(:name=>@name) do |s| if summary_descriptives - s.table(:header =>['']+@ds[a_var].factors.map {|a| @ds[a_var].labeling(a)}+[_("%s Mean") % @name_b]) do |t| + s.table(:header =>['']+@ds[a_var].factors.map {|a| @ds[a_var].index_of(a)}+[_("%s Mean") % @name_b]) do |t| @ds[b_var].factors.each do |b| - t.row([@ds[b_var].labeling(b)]+@ds[a_var].factors.map {|a| "%0.3f" % @axb_means[[a,b]] } + ["%0.3f" % @b_means[b]]) + t.row([@ds[b_var].index_of(b)]+@ds[a_var].factors.map {|a| "%0.3f" % @axb_means[[a,b]] } + ["%0.3f" % @b_means[b]]) end t.row([_("%s Mean") % @name_a]+@ds[a_var].factors.map {|a| "%0.3f" % @a_means[a]}+ ["%0.3f" % @x_general]) end diff --git a/test/test_anovatwowaywithdataset.rb b/test/test_anovatwowaywithdataset.rb index ccbba17..ee69a3e 100644 --- a/test/test_anovatwowaywithdataset.rb +++ b/test/test_anovatwowaywithdataset.rb @@ -4,14 +4,14 @@ class StatsampleAnovaTwoWayWithVectorsTestCase < Minitest::Test context(Statsample::Anova::TwoWayWithVectors) do setup do - @pa = [5, 4, 3, 4, 2, 18, 19, 14, 12, 15, 6, 7, 5, 8, 4, 6, 9, 5, 9, 3].to_numeric - @pa.name = 'Passive Avoidance' - @a = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1].to_vector - @a.labels = { 0 => '0%', 1 => '35%' } - @a.name = 'Diet' - @b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].to_vector - @b.labels = { 0 => 'Young', 1 => 'Older' } - @b.name = 'Age' + @pa = Daru::Vector.new [5, 4, 3, 4, 2, 18, 19, 14, 12, 15, 6, 7, 5, 8, 4, 6, 9, 5, 9, 3] + @pa.rename 'Passive Avoidance' + @a = Daru::Vector.new [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + # @a.labels = { 0 => '0%', 1 => '35%' } + @a.rename 'Diet' + @b = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + # @b.labels = { 0 => 'Young', 1 => 'Older' } + @b.rename 'Age' @anova = Statsample::Anova::TwoWayWithVectors.new(a: @a, b: @b, dependent: @pa) end should 'Statsample::Anova respond to #twoway_with_vectors' do From 915a1136ff6114b6297411ad7d713a8cdb1de089 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 21 May 2015 11:22:38 +0530 Subject: [PATCH 040/119] Anova::Contrast works with daru --- test/test_anova_contrast.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_anova_contrast.rb b/test/test_anova_contrast.rb index bf87f6f..36ccc60 100644 --- a/test/test_anova_contrast.rb +++ b/test/test_anova_contrast.rb @@ -2,12 +2,12 @@ class StatsampleAnovaContrastTestCase < Minitest::Test context(Statsample::Anova::Contrast) do setup do - constant = [12, 13, 11, 12, 12].to_numeric - frequent = [9, 10, 9, 13, 14].to_numeric - infrequent = [15, 16, 17, 16, 16].to_numeric - never = [17, 18, 12, 18, 20].to_numeric - @vectors = [constant, frequent, infrequent, never] - @c = Statsample::Anova::Contrast.new(vectors: @vectors) + constant = Daru::Vector.new([12, 13, 11, 12, 12]) + frequent = Daru::Vector.new([9, 10, 9, 13, 14]) + infrequent = Daru::Vector.new([15, 16, 17, 16, 16]) + never = Daru::Vector.new([17, 18, 12, 18, 20]) + @vectors = [constant, frequent, infrequent, never] + @c = Statsample::Anova::Contrast.new(vectors: @vectors) end should 'return correct value using c' do @c.c([1, -1.quo(3), -1.quo(3), -1.quo(3)]) From f11d2f4bc31473100cd82dcb366b4807b44fdb7d Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 21 May 2015 13:41:32 +0530 Subject: [PATCH 041/119] Statsample::Regression now works with daru --- lib/statsample/bivariate.rb | 28 ++--- lib/statsample/daru.rb | 12 -- .../regression/multiple/baseengine.rb | 64 +++++----- .../regression/multiple/gslengine.rb | 53 +++++---- .../regression/multiple/matrixengine.rb | 16 ++- .../regression/multiple/rubyengine.rb | 65 +++++------ test/test_multiset.rb | 1 - test/test_regression.rb | 109 +++++++++--------- 8 files changed, 168 insertions(+), 180 deletions(-) diff --git a/lib/statsample/bivariate.rb b/lib/statsample/bivariate.rb index b84f915..d0e1572 100644 --- a/lib/statsample/bivariate.rb +++ b/lib/statsample/bivariate.rb @@ -156,15 +156,14 @@ def covariance_matrix_optimized(ds) # Order of rows and columns depends on Dataset#fields order def covariance_matrix(ds) - vars,cases=ds.fields.size,ds.cases + vars,cases = ds.vectors.size, ds.nrows if !ds.has_missing_data? and Statsample.has_gsl? and prediction_optimized(vars,cases) < prediction_pairwise(vars,cases) cm=covariance_matrix_optimized(ds) else cm=covariance_matrix_pairwise(ds) - end cm.extend(Statsample::CovariateMatrix) - cm.fields=ds.fields + cm.fields=ds.vectors.to_a cm end @@ -243,14 +242,19 @@ def correlation_matrix_pairwise(ds) # Retrieves the n valid pairwise. def n_valid_matrix(ds) - ds.collect_matrix do |row,col| - if row==col - ds[row].valid_data.size - else - rowa,rowb=Statsample.only_valid_clone(ds[row],ds[col]) - rowa.size + vectors = ds.vectors.to_a + m = vectors.collect do |row| + vectors.collect do |col| + if row==col + ds[row].only_valid.size + else + rowa,rowb = Statsample.only_valid_clone(ds[row],ds[col]) + rowa.size + end end end + + Matrix.rows m end # Matrix of correlation probabilities. @@ -384,8 +388,8 @@ def sum_of_codeviated(v1,v2) # Report the minimum number of cases valid of a covariate matrix # based on a dataset def min_n_valid(ds) - min=ds.cases - m=n_valid_matrix(ds) + min = ds.nrows + m = n_valid_matrix(ds) for x in 0...m.row_size for y in 0...m.column_size min=m[x,y] if m[x,y] < min @@ -393,8 +397,6 @@ def min_n_valid(ds) end min end - - end end end diff --git a/lib/statsample/daru.rb b/lib/statsample/daru.rb index 029a0fc..8764b9b 100644 --- a/lib/statsample/daru.rb +++ b/lib/statsample/daru.rb @@ -26,13 +26,7 @@ def to_multiset_by_split_one_field(field) #puts "Ingreso a los dataset" ms.datasets.each do |k,ds| ds.update - # puts "idx #{self[field].index_of(k)}" ds.rename self[field].index_of(k) - # ds.vectors.each do |k1,v1| - # v1.type = self[k1].type - # v1.name = self[k1].name - # v1.labels = self[k1].to_hash - # end end ms @@ -69,12 +63,6 @@ def to_multiset_by_split_multiple_fields(*fields) self[f].index_of(sk) end.join("-") ) - - # ds.vectors.each do |k1,v1| - # v1.type = ds[k1].type - # v1.name = ds[k1].name - # v1.labels = ds[k1].to_hash - # end end ms end diff --git a/lib/statsample/regression/multiple/baseengine.rb b/lib/statsample/regression/multiple/baseengine.rb index 5f287a1..f2fdf82 100644 --- a/lib/statsample/regression/multiple/baseengine.rb +++ b/lib/statsample/regression/multiple/baseengine.rb @@ -19,13 +19,12 @@ def self.univariate? end def initialize(ds, y_var, opts = Hash.new) @ds=ds - @predictors_n=@ds.fields.size-1 - @total_cases=@ds.cases - @cases=@ds.cases + @predictors_n=@ds.vectors.size-1 + @total_cases=@ds.nrows + @cases=@ds.nrows @y_var=y_var @r2=nil - @name=_("Multiple Regression: %s over %s") % [ ds.fields.join(",") , @y_var] - + @name=_("Multiple Regression: %s over %s") % [ ds.vectors.to_a.join(",") , @y_var] opts_default={:digits=>3} @opts=opts_default.merge opts @@ -33,7 +32,6 @@ def initialize(ds, y_var, opts = Hash.new) @opts.each{|k,v| self.send("#{k}=",v) if self.respond_to? k } - end # Calculate F Test def anova @@ -45,15 +43,17 @@ def se_estimate end # Retrieves a vector with predicted values for y def predicted - @total_cases.times.collect { |i| - invalid=false - vect=@dep_columns.collect {|v| invalid=true if v[i].nil?; v[i]} - if invalid - nil - else - process(vect) + Daru::Vector.new( + @total_cases.times.collect do |i| + invalid = false + vect = @dep_columns.collect {|v| invalid = true if v[i].nil?; v[i]} + if invalid + nil + else + process(vect) + end end - }.to_vector(:numeric) + ) end # Retrieves a vector with standarized values for y def standarized_predicted @@ -61,15 +61,17 @@ def standarized_predicted end # Retrieves a vector with residuals values for y def residuals - (0...@total_cases).collect{|i| - invalid=false - vect=@dep_columns.collect{|v| invalid=true if v[i].nil?; v[i]} - if invalid or @ds[@y_var][i].nil? - nil - else - @ds[@y_var][i] - process(vect) + Daru::Vector.new( + (0...@total_cases).collect do |i| + invalid=false + vect=@dep_columns.collect{|v| invalid=true if v[i].nil?; v[i]} + if invalid or @ds[@y_var][i].nil? + nil + else + @ds[@y_var][i] - process(vect) + end end - }.to_vector(:numeric) + ) end # R Multiple def r @@ -131,12 +133,10 @@ def probability # Tolerance for a given variable # http://talkstats.com/showthread.php?t=5056 def tolerance(var) - ds=assign_names(@dep_columns) - ds.each{|k,v| - ds[k]=v.to_vector(:numeric) - } - lr=self.class.new(ds.to_dataset,var) - 1-lr.r2 + ds = assign_names(@dep_columns) + ds.each { |k,v| ds[k] = Daru::Vector.new(v) } + lr = self.class.new(Daru::DataFrame.new(ds),var) + 1 - lr.r2 end # Tolerances for each coefficient def coeffs_tolerances @@ -165,12 +165,12 @@ def se_r2 def estimated_variance_covariance_matrix #mse_p=mse columns=[] - @ds_valid.fields.each{|k| - v=@ds_valid[k] - columns.push(v.data) unless k==@y_var + @ds_valid.vectors.each{|k| + v = @ds_valid[k] + columns.push(v.to_a) unless k == @y_var } columns.unshift([1.0]*@valid_cases) - x=Matrix.columns(columns) + x=::Matrix.columns(columns) matrix=((x.t*x)).inverse * mse matrix.collect {|i| Math::sqrt(i) if i>=0 } end diff --git a/lib/statsample/regression/multiple/gslengine.rb b/lib/statsample/regression/multiple/gslengine.rb index ad6492c..5dc57ca 100644 --- a/lib/statsample/regression/multiple/gslengine.rb +++ b/lib/statsample/regression/multiple/gslengine.rb @@ -19,33 +19,34 @@ module Multiple class GslEngine < BaseEngine def initialize(ds,y_var, opts=Hash.new) super - @ds=ds.dup_only_valid - @ds_valid=@ds - @valid_cases=@ds_valid.cases - @dy=@ds[@y_var] - @ds_indep=ds.dup(ds.fields-[y_var]) + @ds = ds.dup_only_valid + @ds_valid = @ds + @valid_cases = @ds_valid.nrows + @dy = @ds[@y_var] + @ds_indep = ds.dup(ds.vectors.to_a - [y_var]) # Create a custom matrix columns=[] @fields=[] - max_deps = GSL::Matrix.alloc(@ds.cases, @ds.fields.size) - constant_col=@ds.fields.size-1 - for i in 0...@ds.cases + max_deps = GSL::Matrix.alloc(@ds.nrows, @ds.vectors.size) + constant_col=@ds.vectors.size-1 + for i in 0...@ds.nrows max_deps.set(i,constant_col,1) end - j=0 - @ds.fields.each{|f| - if f!=@y_var - @ds[f].each_index{|i1| + j = 0 + @ds.vectors.each do |f| + if f != @y_var + @ds[f].each_index do |i1| max_deps.set(i1,j,@ds[f][i1]) - } + end + columns.push(@ds[f].to_a) @fields.push(f) - j+=1 + j += 1 end - } - @dep_columns=columns.dup - @lr_s=nil - c, @cov, @chisq, @status = GSL::MultiFit.linear(max_deps, @dy.gsl) + end + @dep_columns = columns.dup + @lr_s = nil + c, @cov, @chisq, @status = GSL::MultiFit.linear(max_deps, @dy.to_gsl) @constant=c[constant_col] @coeffs_a=c.to_a.slice(0...constant_col) @coeffs=assign_names(@coeffs_a) @@ -97,7 +98,7 @@ def lr_s @lr_s end def build_standarized - @ds_s=@ds.standarize + @ds_s=@ds.standardize @lr_s=GslEngine.new(@ds_s,@y_var) end def process_s(v) @@ -114,17 +115,15 @@ def standarized_residuals # Standard error for coeffs def coeffs_se - out={} - evcm=estimated_variance_covariance_matrix - @ds_valid.fields.each_with_index do |f,i| - - mi=i+1 - next if f==@y_var - out[f]=evcm[mi,mi] + out = {} + evcm = estimated_variance_covariance_matrix + @ds_valid.vectors.to_a.each_with_index do |f,i| + mi = i+1 + next if f == @y_var + out[f] = evcm[mi,mi] end out end - end end end diff --git a/lib/statsample/regression/multiple/matrixengine.rb b/lib/statsample/regression/multiple/matrixengine.rb index 86ddc52..9c780f3 100644 --- a/lib/statsample/regression/multiple/matrixengine.rb +++ b/lib/statsample/regression/multiple/matrixengine.rb @@ -59,8 +59,6 @@ def initialize(matrix,y_var, opts=Hash.new) @matrix_y = @matrix_cor.submatrix(@fields, [y_var]) @matrix_y_cov = @matrix_cov.submatrix(@fields, [y_var]) - - @y_sd=Math::sqrt(@matrix_cov.submatrix([y_var])[0,0]) @x_sd=@n_predictors.times.inject({}) {|ac,i| @@ -77,14 +75,14 @@ def initialize(matrix,y_var, opts=Hash.new) @y_mean=0.0 @name=_("Multiple reggresion of %s on %s") % [@fields.join(","), @y_var] - opts_default={:digits=>3} - opts=opts_default.merge opts + opts_default = {:digits=>3} + opts = opts_default.merge opts opts.each{|k,v| self.send("#{k}=",v) if self.respond_to? k } result_matrix=@matrix_x_cov.inverse * @matrix_y_cov - if matrix._type==:covariance + if matrix._type == :covariance @coeffs=result_matrix.column(0).to_a @coeffs_stan=coeffs.collect {|k,v| coeffs[k]*@x_sd[k].quo(@y_sd) @@ -116,12 +114,12 @@ def r end # Value of constant def constant - c=coeffs - @y_mean - @fields.inject(0){|a,k| a + (c[k] * @x_mean[k])} + c = coeffs + @y_mean - @fields.inject(0) { |a,k| a + (c[k] * @x_mean[k])} end # Hash of b or raw coefficients def coeffs - assign_names(@coeffs) + assign_names(@coeffs) end # Hash of beta or standarized coefficients @@ -185,7 +183,7 @@ def constant_se sd[:constant]=0 fields=[:constant]+@matrix_cov.fields-[@y_var] # Recreate X'X using the variance-covariance matrix - xt_x=Matrix.rows(fields.collect {|i| + xt_x=::Matrix.rows(fields.collect {|i| fields.collect {|j| if i==:constant or j==:constant cov=0 diff --git a/lib/statsample/regression/multiple/rubyengine.rb b/lib/statsample/regression/multiple/rubyengine.rb index 13c1718..f856aad 100644 --- a/lib/statsample/regression/multiple/rubyengine.rb +++ b/lib/statsample/regression/multiple/rubyengine.rb @@ -17,67 +17,64 @@ module Multiple class RubyEngine < MatrixEngine def initialize(ds,y_var, opts=Hash.new) - matrix=ds.correlation_matrix - fields_indep=ds.fields-[y_var] - default={ - :y_mean=>ds[y_var].mean, - :x_mean=>fields_indep.inject({}) {|ac,f| ac[f]=ds[f].mean; ac}, - :y_sd=>ds[y_var].sd, - :x_sd=>fields_indep.inject({}) {|ac,f| ac[f]=ds[f].sd; ac}, - :cases=>Statsample::Bivariate.min_n_valid(ds) + matrix = Statsample::Bivariate.correlation_matrix ds + fields_indep=ds.vectors.to_a - [y_var] + default= { + :y_mean => ds[y_var].mean, + :x_mean => fields_indep.inject({}) {|ac,f| ac[f]=ds[f].mean; ac}, + :y_sd => ds[y_var].sd, + :x_sd => fields_indep.inject({}) {|ac,f| ac[f]=ds[f].sd; ac}, + :cases => Statsample::Bivariate.min_n_valid(ds) } - opts=opts.merge(default) + opts = opts.merge(default) super(matrix, y_var, opts) - @ds=ds - @dy=ds[@y_var] - @ds_valid=ds.dup_only_valid - @total_cases=@ds.cases - @valid_cases=@ds_valid.cases - @ds_indep = ds.dup(ds.fields-[y_var]) + @ds = ds + @dy = ds[@y_var] + @ds_valid = ds.dup_only_valid + @total_cases = @ds.nrows + @valid_cases = @ds_valid.nrows + @ds_indep = ds.dup(ds.vectors.to_a - [y_var]) set_dep_columns end def set_dep_columns - @dep_columns=[] - @ds_indep.each_vector{|k,v| - @dep_columns.push(v.data_with_nils) - } + @dep_columns = [] + @ds_indep.each_vector { |v| @dep_columns.push(v.to_a) } end def fix_with_mean i=0 - @ds_indep.each do |row| + @ds_indep.each(:row) do |row| empty=[] row.each do |k,v| empty.push(k) if v.nil? end + if empty.size==1 @ds_indep[empty[0]][i]=@ds[empty[0]].mean end - i+=1 + i += 1 end - @ds_indep.update_valid_data + @ds_indep.update set_dep_columns end def fix_with_regression - i=0 - @ds_indep.each{|row| - empty=[] - row.each{|k,v| - empty.push(k) if v.nil? - } + i = 0 + @ds_indep.each(:row) do |row| + empty = [] + row.each { |k,v| empty.push(k) if v.nil? } if empty.size==1 - field=empty[0] - lr=MultipleRegression.new(@ds_indep,field) - fields=[] + field = empty[0] + lr = MultipleRegression.new(@ds_indep,field) + fields = [] @ds_indep.fields.each{|f| - fields.push(row[f]) unless f==field + fields.push(row[f]) unless f == field } @ds_indep[field][i]=lr.process(fields) end i+=1 - } - @ds_indep.update_valid_data + end + @ds_indep.update set_dep_columns end # Standard error for constant diff --git a/test/test_multiset.rb b/test/test_multiset.rb index 7543ac0..0e47477 100644 --- a/test/test_multiset.rb +++ b/test/test_multiset.rb @@ -127,7 +127,6 @@ def test_each } xp, yp, zp = {}, {}, {} @ms.each {|k, ds| - # puts "k #{k} ds #{ds}" xp[k] = ds[:x] yp[k] = ds[:y] zp[k] = ds[:z] diff --git a/test/test_regression.rb b/test/test_regression.rb index d973e79..8c23bc0 100644 --- a/test/test_regression.rb +++ b/test/test_regression.rb @@ -3,21 +3,21 @@ class StatsampleRegressionTestCase < Minitest::Test context 'Example with missing data' do setup do - @x = [0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857].to_numeric + @x = Daru::Vector.new([0.285714285714286, 0.114285714285714, 0.314285714285714, 0.2, 0.2, 0.228571428571429, 0.2, 0.4, 0.714285714285714, 0.285714285714286, 0.285714285714286, 0.228571428571429, 0.485714285714286, 0.457142857142857, 0.257142857142857, 0.228571428571429, 0.285714285714286, 0.285714285714286, 0.285714285714286, 0.142857142857143, 0.285714285714286, 0.514285714285714, 0.485714285714286, 0.228571428571429, 0.285714285714286, 0.342857142857143, 0.285714285714286, 0.0857142857142857]) - @y = [nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil].to_numeric - @ds = { 'x' => @x, 'y' => @y }.to_dataset - @lr = Statsample::Regression::Multiple::RubyEngine.new(@ds, 'y') + @y = Daru::Vector.new([nil, 0.233333333333333, nil, 0.266666666666667, 0.366666666666667, nil, 0.333333333333333, 0.3, 0.666666666666667, 0.0333333333333333, 0.333333333333333, nil, nil, 0.533333333333333, 0.433333333333333, 0.4, 0.4, 0.5, 0.4, 0.266666666666667, 0.166666666666667, 0.666666666666667, 0.433333333333333, 0.166666666666667, nil, 0.4, 0.366666666666667, nil]) + @ds = Daru::DataFrame.new({ :x => @x, :y => @y }) + @lr = Statsample::Regression::Multiple::RubyEngine.new(@ds, :y) end should 'have correct values' do assert_in_delta(0.455, @lr.r2, 0.001) assert_in_delta(0.427, @lr.r2_adjusted, 0.001) assert_in_delta(0.1165, @lr.se_estimate, 0.001) assert_in_delta(15.925, @lr.f, 0.0001) - assert_in_delta(0.675, @lr.standarized_coeffs['x'], 0.001) - assert_in_delta(0.778, @lr.coeffs['x'], 0.001, 'coeff x') + assert_in_delta(0.675, @lr.standarized_coeffs[:x], 0.001) + assert_in_delta(0.778, @lr.coeffs[:x], 0.001, 'coeff x') assert_in_delta(0.132, @lr.constant, 0.001, 'constant') - assert_in_delta(0.195, @lr.coeffs_se['x'], 0.001, 'coeff x se') + assert_in_delta(0.195, @lr.coeffs_se[:x], 0.001, 'coeff x se') assert_in_delta(0.064, @lr.constant_se, 0.001, 'constant se') end end @@ -26,24 +26,24 @@ class StatsampleRegressionTestCase < Minitest::Test a, b = rand, rand - x1 = samples.times.map { rand }.to_numeric - x2 = samples.times.map { rand }.to_numeric - x3 = samples.times.map { |i| x1[i] * (1 + a) + x2[i] * (1 + b) }.to_numeric - y = samples.times.map { |i| x1[i] + x2[i] + x3[i] + rand }.to_numeric + x1 = Daru::Vector.new(samples.times.map { rand }) + x2 = Daru::Vector.new(samples.times.map { rand }) + x3 = Daru::Vector.new(samples.times.map { |i| x1[i] * (1 + a) + x2[i] * (1 + b) }) + y = Daru::Vector.new(samples.times.map { |i| x1[i] + x2[i] + x3[i] + rand }) - ds = { 'x1' => x1, 'x2' => x2, 'x3' => x3, 'y' => y }.to_dataset + ds = Daru::DataFrame.new({ :x1 => x1, :x2 => x2, :x3 => x3, :y => y }) assert_raise(Statsample::Regression::LinearDependency) { - Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') + Statsample::Regression::Multiple::RubyEngine.new(ds, :y) } end def test_parameters - @x = [13, 20, 10, 33, 15].to_vector(:numeric) - @y = [23, 18, 35, 10, 27].to_vector(:numeric) + @x =Daru::Vector.new([13, 20, 10, 33, 15]) + @y =Daru::Vector.new([23, 18, 35, 10, 27]) reg = Statsample::Regression::Simple.new_from_vectors(@x, @y) _test_simple_regression(reg) - ds = { 'x' => @x, 'y' => @y }.to_dataset - reg = Statsample::Regression::Simple.new_from_dataset(ds, 'x', 'y') + ds = Daru::DataFrame.new({ :x => @x, :y => @y }) + reg = Statsample::Regression::Simple.new_from_dataset(ds, :x, :y) _test_simple_regression(reg) reg = Statsample::Regression.simple(@x, @y) _test_simple_regression(reg) @@ -57,11 +57,11 @@ def _test_simple_regression(reg) end def test_summaries - a = 10.times.map { rand(100) }.to_numeric - b = 10.times.map { rand(100) }.to_numeric - y = 10.times.map { rand(100) }.to_numeric - ds = { 'a' => a, 'b' => b, 'y' => y }.to_dataset - lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') + a = Daru::Vector.new(10.times.map { rand(100) }) + b = Daru::Vector.new(10.times.map { rand(100) }) + y = Daru::Vector.new(10.times.map { rand(100) }) + ds = Daru::DataFrame.new({ :a => a, :b => b, :y => y }) + lr = Statsample::Regression::Multiple::RubyEngine.new(ds, :y) assert(lr.summary.size > 0) end @@ -87,12 +87,12 @@ def test_multiple_dependent end def test_multiple_regression_pairwise_2 - @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 3, nil, 3, nil, 3].to_vector(:numeric) - @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4, 2, 2, nil, 6, 2].to_vector(:numeric) - @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100, nil, 3, 7, nil, 7].to_vector(:numeric) - @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 30, 40, nil, 50, nil].to_vector(:numeric) - ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset - lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') + @a =Daru::Vector.new( [1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 3, nil, 3, nil, 3]) + @b =Daru::Vector.new( [3, 3, 4, 4, 5, 5, 6, 6, 4, 4, 2, 2, nil, 6, 2]) + @c =Daru::Vector.new( [11, 22, 30, 40, 50, 65, 78, 79, 99, 100, nil, 3, 7, nil, 7]) + @y =Daru::Vector.new( [3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 30, 40, nil, 50, nil]) + ds = Daru::DataFrame.new({ :a => @a, :b => @b, :c => @c, :y => @y }) + lr = Statsample::Regression::Multiple::RubyEngine.new(ds, :y) assert_in_delta(2407.436, lr.sst, 0.001) assert_in_delta(0.752, lr.r, 0.001, 'pairwise r') assert_in_delta(0.565, lr.r2, 0.001) @@ -103,12 +103,12 @@ def test_multiple_regression_pairwise_2 def test_multiple_regression_gsl if Statsample.has_gsl? - @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:numeric) - @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:numeric) - @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:numeric) - @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:numeric) - ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset - lr = Statsample::Regression::Multiple::GslEngine.new(ds, 'y') + @a =Daru::Vector.new( [1, 3, 2, 4, 3, 5, 4, 6, 5, 7]) + @b =Daru::Vector.new( [3, 3, 4, 4, 5, 5, 6, 6, 4, 4]) + @c =Daru::Vector.new( [11, 22, 30, 40, 50, 65, 78, 79, 99, 100]) + @y =Daru::Vector.new( [3, 4, 5, 6, 7, 8, 9, 10, 20, 30]) + ds = Daru::DataFrame.new({ :a => @a, :b => @b, :c => @c, :y => @y }) + lr = Statsample::Regression::Multiple::GslEngine.new(ds, :y) assert(lr.summary.size > 0) model_test(lr, 'gsl') predicted = [1.7857, 6.0989, 3.2433, 7.2908, 4.9667, 10.3428, 8.8158, 10.4717, 23.6639, 25.3198] @@ -127,8 +127,8 @@ def test_multiple_regression_gsl end def model_test_matrix(lr, name = 'undefined') - stan_coeffs = { 'a' => 0.151, 'b' => -0.547, 'c' => 0.997 } - unstan_coeffs = { 'a' => 0.695, 'b' => -4.286, 'c' => 0.266 } + stan_coeffs = { :a => 0.151, :b => -0.547, :c => 0.997 } + unstan_coeffs = { :a => 0.695, :b => -4.286, :c => 0.266 } unstan_coeffs.each_key{|k| assert_in_delta(unstan_coeffs[k], lr.coeffs[k], 0.001, "b coeffs - #{name}") @@ -145,15 +145,15 @@ def model_test_matrix(lr, name = 'undefined') assert_in_delta(20.908, lr.f, 0.001) assert_in_delta(0.001, lr.probability, 0.001) - assert_in_delta(0.226, lr.tolerance('a'), 0.001) + assert_in_delta(0.226, lr.tolerance(:a), 0.001) - coeffs_se = { 'a' => 1.171, 'b' => 1.129, 'c' => 0.072 } + coeffs_se = { :a => 1.171, :b => 1.129, :c => 0.072 } ccoeffs_se = lr.coeffs_se coeffs_se.each_key{|k| assert_in_delta(coeffs_se[k], ccoeffs_se[k], 0.001) } - coeffs_t = { 'a' => 0.594, 'b' => -3.796, 'c' => 3.703 } + coeffs_t = { :a => 0.594, :b => -3.796, :c => 3.703 } ccoeffs_t = lr.coeffs_t coeffs_t.each_key{|k| assert_in_delta(coeffs_t[k], ccoeffs_t[k], 0.001) @@ -174,32 +174,37 @@ def model_test(lr, name = 'undefined') end def test_regression_matrix - @a = [1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:numeric) - @b = [3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:numeric) - @c = [11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:numeric) - @y = [3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:numeric) - ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset + @a = Daru::Vector.new([1, 3, 2, 4, 3, 5, 4, 6, 5, 7]) + @b = Daru::Vector.new([3, 3, 4, 4, 5, 5, 6, 6, 4, 4]) + @c = Daru::Vector.new([11, 22, 30, 40, 50, 65, 78, 79, 99, 100]) + @y = Daru::Vector.new([3, 4, 5, 6, 7, 8, 9, 10, 20, 30]) + ds = Daru::DataFrame.new({ :a => @a, :b => @b, :c => @c, :y => @y }) cor = Statsample::Bivariate.correlation_matrix(ds) - lr = Statsample::Regression::Multiple::MatrixEngine.new(cor, 'y', y_mean: @y.mean, x_mean: { 'a' => ds['a'].mean, 'b' => ds['b'].mean, 'c' => ds['c'].mean }, cases: @a.size, y_sd: @y.sd, x_sd: { 'a' => @a.sd, 'b' => @b.sd, 'c' => @c.sd }) + lr = Statsample::Regression::Multiple::MatrixEngine.new( + cor, :y, y_mean: @y.mean, + x_mean: { :a => ds[:a].mean, :b => ds[:b].mean, :c => ds[:c].mean }, + cases: @a.size, y_sd: @y.sd, x_sd: { :a => @a.sd, :b => @b.sd, :c => @c.sd }) assert_nil(lr.constant_se) assert_nil(lr.constant_t) model_test_matrix(lr, 'correlation matrix') covariance = Statsample::Bivariate.covariance_matrix(ds) - lr = Statsample::Regression::Multiple::MatrixEngine.new(covariance, 'y', y_mean: @y.mean, x_mean: { 'a' => ds['a'].mean, 'b' => ds['b'].mean, 'c' => ds['c'].mean }, cases: @a.size) + lr = Statsample::Regression::Multiple::MatrixEngine.new( + covariance, :y, y_mean: @y.mean, + x_mean: { :a => ds[:a].mean, :b => ds[:b].mean, :c => ds[:c].mean }, cases: @a.size) assert(lr.summary.size > 0) model_test(lr, 'covariance matrix') end def test_regression_rubyengine - @a = [nil, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7].to_vector(:numeric) - @b = [nil, 3, 3, 4, 4, 5, 5, 6, 6, 4, 4].to_vector(:numeric) - @c = [nil, 11, 22, 30, 40, 50, 65, 78, 79, 99, 100].to_vector(:numeric) - @y = [nil, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30].to_vector(:numeric) - ds = { 'a' => @a, 'b' => @b, 'c' => @c, 'y' => @y }.to_dataset - lr = Statsample::Regression::Multiple::RubyEngine.new(ds, 'y') + @a = Daru::Vector.new([nil, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7]) + @b = Daru::Vector.new([nil, 3, 3, 4, 4, 5, 5, 6, 6, 4, 4]) + @c = Daru::Vector.new([nil, 11, 22, 30, 40, 50, 65, 78, 79, 99, 100]) + @y = Daru::Vector.new([nil, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]) + ds = Daru::DataFrame.new({ :a => @a, :b => @b, :c => @c, :y => @y }) + lr = Statsample::Regression::Multiple::RubyEngine.new(ds, :y) assert_equal(11, lr.total_cases) assert_equal(10, lr.valid_cases) model_test(lr, 'rubyengine with missing data') From 6361621d9c85097822ef5b50b17f737033c25198 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Thu, 21 May 2015 16:45:20 +0530 Subject: [PATCH 042/119] Statsample::Factor now works with daru factor wip factor wip wip factor wip wip wip wip wip wip wip wip wip --- lib/statsample/bivariate.rb | 5 ++- lib/statsample/converters.rb | 17 +++----- lib/statsample/factor/pca.rb | 31 +++++++------- lib/statsample/matrix.rb | 32 +++++++------- test/helpers_tests.rb | 2 +- test/test_factor.rb | 82 ++++++++++++++++++++---------------- 6 files changed, 88 insertions(+), 81 deletions(-) diff --git a/lib/statsample/bivariate.rb b/lib/statsample/bivariate.rb index d0e1572..b02a81e 100644 --- a/lib/statsample/bivariate.rb +++ b/lib/statsample/bivariate.rb @@ -12,6 +12,7 @@ class << self # Covariance between two vectors def covariance(v1,v2) v1a,v2a=Statsample.only_valid_clone(v1,v2) + return nil if v1a.size==0 if Statsample.has_gsl? GSL::Stats::covariance(v1a.to_gsl, v2a.to_gsl) @@ -156,14 +157,14 @@ def covariance_matrix_optimized(ds) # Order of rows and columns depends on Dataset#fields order def covariance_matrix(ds) - vars,cases = ds.vectors.size, ds.nrows + vars,cases = ds.ncols, ds.nrows if !ds.has_missing_data? and Statsample.has_gsl? and prediction_optimized(vars,cases) < prediction_pairwise(vars,cases) cm=covariance_matrix_optimized(ds) else cm=covariance_matrix_pairwise(ds) end cm.extend(Statsample::CovariateMatrix) - cm.fields=ds.vectors.to_a + cm.fields = ds.vectors.to_a cm end diff --git a/lib/statsample/converters.rb b/lib/statsample/converters.rb index 3869262..4440d97 100644 --- a/lib/statsample/converters.rb +++ b/lib/statsample/converters.rb @@ -121,18 +121,15 @@ def convert_to_numeric_and_date(ds,fields) class PlainText < SpreadsheetBase class << self def read(filename, fields) - ds=Statsample::Dataset.new(fields) - fp=File.open(filename,"r") + ds = Daru::DataFrame.new({}, order: fields) + fp = File.open(filename,"r") fp.each_line do |line| - row=process_row(line.strip.split(/\s+/),[""]) - next if row==["\x1A"] - ds.add_case_array(row) + row = process_row(line.strip.split(/\s+/),[""]) + next if row == ["\x1A"] + ds.add_row(row) end - convert_to_numeric_and_date(ds,fields) - ds.update_valid_data - fields.each {|f| - ds[f].name=f - } + ds.update + fields.each { |f| ds[f].rename f } ds end end diff --git a/lib/statsample/factor/pca.rb b/lib/statsample/factor/pca.rb index e3ffcef..c918617 100644 --- a/lib/statsample/factor/pca.rb +++ b/lib/statsample/factor/pca.rb @@ -52,11 +52,13 @@ class PCA attr_accessor :rotation_type attr_accessor :matrix_type def initialize(matrix, opts=Hash.new) - @use_gsl=nil + @use_gsl = opts[:use_gsl] + opts.delete :use_gsl + @name=_("Principal Component Analysis") @matrix=matrix @n_variables=@matrix.column_size - @variables_names=(@matrix.respond_to? :fields) ? @matrix.fields : @n_variables.times.map {|i| _("VAR_%d") % (i+1)} + @variables_names=(@matrix.respond_to? :fields) ? @matrix.fields : @n_variables.times.map {|i| "VAR_#{i+1}".to_sym } @matrix_type = @matrix.respond_to?(:_type) ? @matrix._type : :correlation @@ -67,13 +69,14 @@ def initialize(matrix, opts=Hash.new) opts.each{|k,v| self.send("#{k}=",v) if self.respond_to? k } + if @use_gsl.nil? @use_gsl=Statsample.has_gsl? end if @matrix.respond_to? :fields @variables_names=@matrix.fields else - @variables_names=@n_variables.times.map {|i| "V#{i+1}"} + @variables_names=@n_variables.times.map {|i| "V#{i+1}".to_sym} end calculate_eigenpairs @@ -81,7 +84,6 @@ def initialize(matrix, opts=Hash.new) # Set number of factors with eigenvalues > 1 @m=@eigenpairs.find_all {|ev,ec| ev>=1.0}.size end - end def rotation @rotation_type.new(component_matrix) @@ -92,10 +94,10 @@ def total_eigenvalues def create_centered_ds h={} @original_ds.factors.each {|f| - mean=@original_ds[f].mean - h[f]=@original_ds[f].recode {|c| c-mean} + mean = @original_ds[f].mean + h[f] = @original_ds[f].recode {|c| c-mean} } - @ds=h.to_dataset + @ds = Daru::DataFrame.new(h) end # Feature matrix for +m+ factors @@ -137,8 +139,8 @@ def principal_components(input, m=nil) pcs=(fv.transpose*data_matrix.transpose).transpose pcs.extend Statsample::NamedMatrix - pcs.fields_y=m.times.map {|i| "PC_%d" % (i+1)} - pcs.to_dataset + pcs.fields_y = m.times.map { |i| "PC_#{i+1}".to_sym } + pcs.to_dataframe end def component_matrix(m=nil) var="component_matrix_#{matrix_type}" @@ -159,7 +161,7 @@ def component_matrix_covariance(m=nil) cm.extend NamedMatrix cm.name=_("Component matrix (from covariance)") cm.fields_x = @variables_names - cm.fields_y = m.times.map {|i| "PC_%d" % (i+1)} + cm.fields_y = m.times.map {|i| "PC_#{i+1}".to_sym } cm end @@ -180,17 +182,16 @@ def component_matrix_correlation(m=nil) cm.extend CovariateMatrix cm.name=_("Component matrix") cm.fields_x = @variables_names - cm.fields_y = m.times.map {|i| "PC_%d" % (i+1)} + cm.fields_y = m.times.map { |i| "PC_#{i+1}".to_sym } cm end def communalities(m=nil) - m||=@m h=[] @n_variables.times do |i| sum=0 m.times do |j| - sum+=(@eigenpairs[j][0].abs*@eigenpairs[j][1][i]**2) + sum += (@eigenpairs[j][0].abs*@eigenpairs[j][1][i]**2) end h.push(sum) end @@ -202,11 +203,11 @@ def eigenvalues end def eigenvectors @eigenpairs.collect {|c| - @use_gsl ? c[1].to_gsl : c[1].to_vector + @use_gsl ? c[1].to_gsl : Daru::Vector.new(c[1]) } end def calculate_eigenpairs - @eigenpairs= @use_gsl ? @matrix.to_gsl.eigenpairs : @matrix.to_matrix.eigenpairs_ruby + @eigenpairs= @use_gsl ? @matrix.to_gsl.eigenpairs : @matrix.to_matrix.eigenpairs_ruby end diff --git a/lib/statsample/matrix.rb b/lib/statsample/matrix.rb index 7c94ed5..5bcd389 100644 --- a/lib/statsample/matrix.rb +++ b/lib/statsample/matrix.rb @@ -11,18 +11,18 @@ def to_matrix self end - def to_dataset - f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| _("VAR_%d") % (i+1) } - ds=Statsample::Dataset.new(f) + def to_dataframe + f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| "VAR_#{i+1}".to_sym } + f = [f] unless f.is_a?(Array) + ds= Daru::DataFrame.new({}, order: f) f.each do |ff| - ds[ff].type=:numeric - ds[ff].name=ff + ds[ff].rename ff end row_size.times {|i| - ds.add_case_array(self.row(i).to_a) + ds.add_row(self.row(i).to_a) } - ds.update_valid_data - ds.name=self.name if self.respond_to? :name + ds.update + ds.rename(self.name) if self.respond_to? :name ds end @@ -83,18 +83,18 @@ def to_gsl self end - def to_dataset - f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| _("VAR_%d") % (i+1) } - ds=Statsample::Dataset.new(f) + def to_dataframe + f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| "VAR_#{i+1}".to_sym } + ds=Daru::DataFrame.new({}, order: f) f.each do |ff| - ds[ff].type=:numeric - ds[ff].name=ff + ds[ff].rename ff end + row_size.times {|i| - ds.add_case_array(self.row(i).to_a) + ds.add_row(self.row(i).to_a) } - ds.update_valid_data - ds.name=self.name if self.respond_to? :name + ds.update + ds.rename(self.name) if self.respond_to? :name ds end diff --git a/test/helpers_tests.rb b/test/helpers_tests.rb index 83a40a0..cae0dfc 100644 --- a/test/helpers_tests.rb +++ b/test/helpers_tests.rb @@ -33,7 +33,7 @@ module Assertions def assert_similar_vector(exp, obs, delta = 1e-10, msg = nil) msg ||= "Different vectors #{exp} - #{obs}" assert_equal(exp.size, obs.size) - exp.data_with_nils.each_with_index {|v, i| + exp.to_a.each_with_index {|v, i| assert_in_delta(v, obs[i], delta) } end diff --git a/test/test_factor.rb b/test/test_factor.rb index 9494f2c..4f9a691 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -7,26 +7,32 @@ class StatsampleFactorTestCase < Minitest::Test # Based on Hardle and Simar def setup @fixtures_dir = File.expand_path(File.dirname(__FILE__) + '/fixtures') + Daru.lazy_update = true + end + + def teardown + Daru.lazy_update = false end # Based on Hurdle example def test_covariance_matrix - ds = Statsample::PlainText.read(@fixtures_dir + '/bank2.dat', %w(v1 v2 v3 v4 v5 v6)) - ds.fields.each {|f| - ds[f] = ds[f].centered + ds = Statsample::PlainText.read(@fixtures_dir + '/bank2.dat', [:v1,:v2,:v3,:v4,:v5,:v6]) + ds.vectors.each {|f| + ds[f] = ds[f].center } - cm = ds.covariance_matrix + ds.update + cm = Statsample::Bivariate.covariance_matrix ds pca = Statsample::Factor::PCA.new(cm, m: 6) # puts pca.summary # puts pca.feature_matrix - exp_eig = [2.985, 0.931, 0.242, 0.194, 0.085, 0.035].to_numeric - assert_similar_vector(exp_eig, pca.eigenvalues.to_numeric, 0.1) + exp_eig = Daru::Vector.new([2.985, 0.931, 0.242, 0.194, 0.085, 0.035]) + assert_similar_vector(exp_eig, Daru::Vector.new(pca.eigenvalues), 0.1) pcs = pca.principal_components(ds) k = 6 comp_matrix = pca.component_matrix k.times {|i| - pc_id = "PC_#{i + 1}" + pc_id = "PC_#{i + 1}".to_sym k.times {|j| # variable - ds_id = "v#{j + 1}" + ds_id = "v#{j + 1}".to_sym r = Statsample::Bivariate.correlation(ds[ds_id], pcs[pc_id]) assert_in_delta(r, comp_matrix[j, i]) } @@ -42,13 +48,13 @@ def test_principalcomponents_ruby_gsl samples = 20 [3, 5, 7].each {|k| v = {} - v['x0'] = samples.times.map { ran.call }.to_numeric.centered - (1...k).each {|i| - v["x#{i}"] = samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}"][ii] * 0.5 }.to_numeric.centered + v[:x0] = Daru::Vector.new(samples.times.map { ran.call }).center + (1...k).each { |i| + v["x#{i}".to_sym] = Daru::Vector.new(samples.times.map { |ii| ran.call * 0.5 + v["x#{i - 1}".to_sym][ii] * 0.5 }).center } - ds = v.to_dataset - cm = ds.covariance_matrix + ds = Daru::DataFrame.new(v) + cm = Statsample::Bivariate.covariance_matrix ds # @r.assign('ds',ds) # @r.eval('cm<-cor(ds);sm<-eigen(cm, sym=TRUE);v<-sm$vectors') # puts "eigenvalues" @@ -61,14 +67,14 @@ def test_principalcomponents_ruby_gsl cm_ruby = pca_ruby.component_matrix # puts cm_ruby.summary k.times {|i| - pc_id = "PC_#{i + 1}" + pc_id = "PC_#{i + 1}".to_sym assert_in_delta(pca_ruby.eigenvalues[i], pca_gsl.eigenvalues[i], 1e-10) # Revert gsl component values pc_gsl_data = (pc_gsl[pc_id][0] - pc_ruby[pc_id][0]).abs > 1e-6 ? pc_gsl[pc_id].recode(&:-@) : pc_gsl[pc_id] assert_similar_vector(pc_gsl_data, pc_ruby[pc_id], 1e-6, "PC for #{k} variables") if false k.times {|j| # variable - ds_id = "x#{j}" + ds_id = "x#{j}".to_sym r = Statsample::Bivariate.correlation(ds[ds_id], pc_ruby[pc_id]) puts "#{pc_id}-#{ds_id}:#{r}" } @@ -80,18 +86,22 @@ def test_principalcomponents_ruby_gsl end def test_principalcomponents - principalcomponents(true) if Statsample.has_gsl? + if Statsample.has_gsl? + principalcomponents(true) + else + skip "Require GSL" + end principalcomponents(false) end def principalcomponents(gsl) ran = Distribution::Normal.rng samples = 50 - x1 = samples.times.map { ran.call }.to_numeric - x2 = samples.times.map { |i| ran.call * 0.5 + x1[i] * 0.5 }.to_numeric - ds = { 'x1' => x1, 'x2' => x2 }.to_dataset + x1 = Daru::Vector.new(samples.times.map { ran.call }) + x2 = Daru::Vector.new(samples.times.map { |i| ran.call * 0.5 + x1[i] * 0.5 }) + ds = Daru::DataFrame.new({ :x1 => x1, :x2 => x2 }) - cm = ds.correlation_matrix + cm = Statsample::Bivariate.correlation_matrix ds r = cm[0, 1] pca = Statsample::Factor::PCA.new(cm, m: 2, use_gsl: gsl) assert_in_delta(1 + r, pca.eigenvalues[0], 1e-10) @@ -103,14 +113,14 @@ def principalcomponents(gsl) assert_equal_vector(hs * m_1, pca.eigenvectors[1]) pcs = pca.principal_components(ds) - exp_pc_1 = ds.collect_with_index {|row, _i| - hs * (row['x1'] + row['x2']) + exp_pc_1 = ds.collect_row_with_index {|row, _i| + hs * (row[:x1] + row[:x2]) } - exp_pc_2 = ds.collect_with_index {|row, _i| - gsl ? hs * (row['x2'] - row['x1']) : hs * (row['x1'] - row['x2']) + exp_pc_2 = ds.collect_row_with_index {|row, _i| + gsl ? hs * (row[:x2] - row[:x1]) : hs * (row[:x1] - row[:x2]) } - assert_similar_vector(exp_pc_1, pcs['PC_1']) - assert_similar_vector(exp_pc_2, pcs['PC_2']) + assert_similar_vector(exp_pc_1, pcs[:PC_1]) + assert_similar_vector(exp_pc_2, pcs[:PC_2]) end def test_antiimage @@ -121,11 +131,11 @@ def test_antiimage end def test_kmo - @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_numeric - @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_numeric - @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_numeric + @v1 = Daru::Vector.new([1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70]) + @v2 = Daru::Vector.new([5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0]) + @v3 = Daru::Vector.new([10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4]) # KMO: 0.490 - ds = { 'v1' => @v1, 'v2' => @v2, 'v3' => @v3 }.to_dataset + ds = Daru::DataFrame.new({ :v1 => @v1, :v2 => @v2, :v3 => @v3 }) cor = Statsample::Bivariate.correlation_matrix(ds) kmo = Statsample::Factor.kmo(cor) assert_in_delta(0.667, kmo, 0.001) @@ -142,11 +152,11 @@ def test_kmo_univariate # Tested with SPSS and R def test_pca - a = [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1].to_numeric - b = [2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9].to_numeric - a.recode! { |c| c - a.mean } - b.recode! { |c| c - b.mean } - ds = { 'a' => a, 'b' => b }.to_dataset + a = Daru::Vector.new([2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1], dtype: :gsl) + b = Daru::Vector.new([2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9], dtype: :gsl) + a = a - a.mean + b = b - b.mean + ds = Daru::DataFrame.new({ :a => a, :b => b }) cov_matrix = Statsample::Bivariate.covariance_matrix(ds) if Statsample.has_gsl? @@ -160,8 +170,6 @@ def test_pca end def pca_set(pca, _type) - - expected_eigenvalues = [1.284, 0.0490] expected_eigenvalues.each_with_index{|ev, i| assert_in_delta(ev, pca.eigenvalues[i], 0.001) From a0c15bd84e298395b6cacc2f85cc7f668038035a Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Fri, 22 May 2015 15:56:57 +0530 Subject: [PATCH 043/119] Statsample::Factor::ParallelAnalysis now works with daru wip statsample parallel analysis works --- lib/statsample/factor/parallelanalysis.rb | 34 +++++++++++----------- statsample.gemspec | 1 + test/test_factor_pa.rb | 35 ++++++++++++++--------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/lib/statsample/factor/parallelanalysis.rb b/lib/statsample/factor/parallelanalysis.rb index a4c4a38..5cfafa9 100644 --- a/lib/statsample/factor/parallelanalysis.rb +++ b/lib/statsample/factor/parallelanalysis.rb @@ -24,8 +24,8 @@ class ParallelAnalysis def self.with_random_data(cases,vars,opts=Hash.new) require 'ostruct' ds=OpenStruct.new - ds.fields=vars.times.map {|i| "v#{i+1}"} - ds.cases=cases + ds.vectors=vars.times.map {|i| "v#{i+1}".to_sym} + ds.nrows=cases opts=opts.merge({:bootstrap_method=> :random, :no_data=>true}) new(ds, opts) end @@ -61,9 +61,9 @@ def self.with_random_data(cases,vars,opts=Hash.new) attr_accessor :use_gsl def initialize(ds, opts=Hash.new) @ds=ds - @fields=@ds.fields + @fields=@ds.vectors.to_a @n_variables=@fields.size - @n_cases=ds.cases + @n_cases=ds.nrows opts_default={ :name=>_("Parallel Analysis"), :iterations=>50, # See Liu and Rijmen (2008) @@ -82,7 +82,7 @@ def initialize(ds, opts=Hash.new) # Number of factor to retent def number_of_factors total=0 - ds_eigenvalues.fields.each_with_index do |f,i| + ds_eigenvalues.vectors.to_a.each_with_index do |f,i| if (@original[i]>0 and @original[i]>ds_eigenvalues[f].percentil(percentil)) total+=1 else @@ -101,7 +101,7 @@ def report_building(g) #:nodoc: s.text _("Number of iterations: %d") % @iterations if @no_data s.table(:name=>_("Eigenvalues"), :header=>[_("n"), _("generated eigenvalue"), "p.#{percentil}"]) do |t| - ds_eigenvalues.fields.each_with_index do |f,i| + ds_eigenvalues.vectors.to_a.each_with_index do |f,i| v=ds_eigenvalues[f] t.row [i+1, "%0.4f" % v.mean, "%0.4f" % v.percentil(percentil), ] end @@ -109,7 +109,7 @@ def report_building(g) #:nodoc: else s.text _("Number or factors to preserve: %d") % number_of_factors s.table(:name=>_("Eigenvalues"), :header=>[_("n"), _("data eigenvalue"), _("generated eigenvalue"),"p.#{percentil}",_("preserve?")]) do |t| - ds_eigenvalues.fields.each_with_index do |f,i| + ds_eigenvalues.vectors.to_a.each_with_index do |f,i| v=ds_eigenvalues[f] t.row [i+1, "%0.4f" % @original[i], "%0.4f" % v.mean, "%0.4f" % v.percentil(percentil), (v.percentil(percentil)>0 and @original[i] > v.percentil(percentil)) ? "Yes":""] end @@ -120,11 +120,9 @@ def report_building(g) #:nodoc: end # Perform calculation. Shouldn't be called directly for the user def compute - - - @original=Statsample::Bivariate.send(matrix_method, @ds).eigenvalues unless no_data - @ds_eigenvalues=Statsample::Dataset.new((1..@n_variables).map{|v| "ev_%05d" % v}) - @ds_eigenvalues.fields.each {|f| @ds_eigenvalues[f].type=:numeric} + @original=Statsample::Bivariate.send(matrix_method, @ds).eigenvalues unless no_data + @ds_eigenvalues=Daru::DataFrame.new({}, order: (1..@n_variables).map{|v| ("ev_%05d" % v).to_sym}) + # @ds_eigenvalues.vectors.each {|f| @ds_eigenvalues[f].type = :numeric} if bootstrap_method==:parameter or bootstrap_method==:random rng = Distribution::Normal.rng end @@ -133,18 +131,18 @@ def compute begin puts "#{@name}: Iteration #{i}" if $DEBUG or debug # Create a dataset of dummy values - ds_bootstrap=Statsample::Dataset.new(@ds.fields) + ds_bootstrap = Daru::DataFrame.new({}, order: @ds.vectors, index: @n_cases) @fields.each do |f| if bootstrap_method==:random - ds_bootstrap[f]=@n_cases.times.map {|c| rng.call}.to_numeric + ds_bootstrap[f] = Daru::Vector.new(@n_cases.times.map {|c| rng.call}) elsif bootstrap_method==:data - ds_bootstrap[f]=ds[f].sample_with_replacement(@n_cases) + ds_bootstrap[f] = ds[f].sample_with_replacement(@n_cases) else raise "bootstrap_method doesn't recogniced" end end - ds_bootstrap.update_valid_data + ds_bootstrap.update matrix=Statsample::Bivariate.send(matrix_method, ds_bootstrap) matrix=matrix.to_gsl if @use_gsl @@ -155,13 +153,13 @@ def compute end end ev=matrix.eigenvalues - @ds_eigenvalues.add_case_array(ev) + @ds_eigenvalues.add_row(ev) rescue Statsample::Bivariate::Tetrachoric::RequerimentNotMeet => e puts "Error: #{e}" if $DEBUG redo end end - @ds_eigenvalues.update_valid_data + @ds_eigenvalues.update end dirty_memoize :number_of_factors, :ds_eigenvalues dirty_writer :iterations, :bootstrap_method, :percentil, :smc diff --git a/statsample.gemspec b/statsample.gemspec index 91a50d4..42f8fee 100644 --- a/statsample.gemspec +++ b/statsample.gemspec @@ -85,4 +85,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'minitest', '~> 5.5' s.add_development_dependency 'gettext', '~> 3.1' s.add_development_dependency 'mocha', '~> 1.1' + s.add_development_dependency 'statsample-bivariate-extension' end diff --git a/test/test_factor_pa.rb b/test/test_factor_pa.rb index 8d7cbbf..2fe9499 100644 --- a/test/test_factor_pa.rb +++ b/test/test_factor_pa.rb @@ -7,6 +7,11 @@ class StatsampleFactorTestCase < Minitest::Test # Based on Hardle and Simar def setup @fixtures_dir = File.expand_path(File.dirname(__FILE__) + '/fixtures') + Daru.lazy_update = true + end + + def teardown + Daru.lazy_update = false end def test_parallelanalysis_with_data @@ -15,26 +20,30 @@ def test_parallelanalysis_with_data variables = 10 iterations = 50 rng = Distribution::Normal.rng - f1 = samples.times.collect { rng.call }.to_numeric - f2 = samples.times.collect { rng.call }.to_numeric + f1 = Daru::Vector.new(samples.times.collect { rng.call }) + f2 = Daru::Vector.new(samples.times.collect { rng.call }) vectors = {} variables.times do |i| if i < 5 - vectors["v#{i}"] = samples.times.collect {|nv| - f1[nv] * 5 + f2[nv] * 2 + rng.call - }.to_numeric + Daru::Vector.new( + vectors["v#{i}".to_sym] = samples.times.collect { |nv| + f1[nv] * 5 + f2[nv] * 2 + rng.call + } + ) else - vectors["v#{i}"] = samples.times.collect {|nv| - f2[nv] * 5 + f1[nv] * 2 + rng.call - }.to_numeric + Daru::Vector.new( + vectors["v#{i}".to_sym] = samples.times.collect { |nv| + f2[nv] * 5 + f1[nv] * 2 + rng.call + } + ) end end - ds = vectors.to_dataset + ds = Daru::DataFrame.new(vectors) pa1 = Statsample::Factor::ParallelAnalysis.new(ds, bootstrap_method: :data, iterations: iterations) pa2 = Statsample::Factor::ParallelAnalysis.with_random_data(samples, variables, iterations: iterations, percentil: 95) 3.times do |n| - var = "ev_0000#{n + 1}" + var = "ev_0000#{n + 1}".to_sym assert_in_delta(pa1.ds_eigenvalues[var].mean, pa2.ds_eigenvalues[var].mean, 0.05) end else @@ -44,9 +53,9 @@ def test_parallelanalysis_with_data def test_parallelanalysis pa = Statsample::Factor::ParallelAnalysis.with_random_data(305, 8, iterations: 100, percentil: 95) - assert_in_delta(1.2454, pa.ds_eigenvalues['ev_00001'].mean, 0.01) - assert_in_delta(1.1542, pa.ds_eigenvalues['ev_00002'].mean, 0.01) - assert_in_delta(1.0836, pa.ds_eigenvalues['ev_00003'].mean, 0.01) + assert_in_delta(1.2454, pa.ds_eigenvalues[:ev_00001].mean, 0.01) + assert_in_delta(1.1542, pa.ds_eigenvalues[:ev_00002].mean, 0.01) + assert_in_delta(1.0836, pa.ds_eigenvalues[:ev_00003].mean, 0.01) assert(pa.summary.size > 0) end end From 6adbdcf2ae24128b08ea1af360bc863515d3d5da Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 23 May 2015 10:23:13 +0530 Subject: [PATCH 044/119] Statsample::Codification works with daru --- lib/statsample/codification.rb | 59 +++++++++++++++++++-------------- lib/statsample/converters.rb | 54 ++++++++++++++---------------- test/test_codification.rb | 60 +++++++++++++++++----------------- 3 files changed, 90 insertions(+), 83 deletions(-) diff --git a/lib/statsample/codification.rb b/lib/statsample/codification.rb index bf76ef0..bbc1b86 100644 --- a/lib/statsample/codification.rb +++ b/lib/statsample/codification.rb @@ -34,15 +34,24 @@ class << self # will be hashes, with keys = values, for recodification def create_hash(dataset, vectors, sep=Statsample::SPLIT_TOKEN) raise ArgumentError,"Array should't be empty" if vectors.size==0 - pro_hash=vectors.inject({}){|h,v_name| - raise Exception, "Vector #{v_name} doesn't exists on Dataset" if !dataset.fields.include? v_name - v=dataset[v_name] - split_data=v.splitted(sep).flatten.collect {|c| c.to_s}.find_all {|c| !c.nil?} + pro_hash = vectors.inject({}) do |h,v_name| + v_name = v_name.is_a?(Numeric) ? v_name : v_name.to_sym + raise Exception, "Vector #{v_name} doesn't exists on Dataset" if + !dataset.vectors.include?(v_name) + v = dataset[v_name] + split_data = v.splitted(sep) + .flatten + .collect { |c| c.to_s } + .find_all{ |c| !c.nil? } - factors=split_data.uniq.compact.sort.inject({}) {|ac,val| ac[val]=val;ac } - h[v_name]=factors + factors = split_data.uniq + .compact + .sort + .inject({}) { |ac,val| ac[val] = val; ac } + h[v_name] = factors h - } + end + pro_hash end # Create a yaml to create a dictionary, based on vectors @@ -69,16 +78,17 @@ def create_excel(dataset, vectors, filename, sep=Statsample::SPLIT_TOKEN) if File.exist?(filename) raise "Exists a file named #{filename}. Delete ir before overwrite." end - book = Spreadsheet::Workbook.new + book = Spreadsheet::Workbook.new sheet = book.create_worksheet - sheet.row(0).concat(%w{field original recoded}) - i=1 + sheet.row(0).concat(%w(field original recoded)) + i = 1 create_hash(dataset, vectors, sep).sort.each do |field, inner_hash| inner_hash.sort.each do |k,v| - sheet.row(i).concat([field.dup,k.dup,v.dup]) - i+=1 + sheet.row(i).concat([field.to_s,k.to_s,v.to_s]) + i += 1 end end + book.write(filename) end # From a excel generates a dictionary hash @@ -91,10 +101,11 @@ def excel_to_recoded_hash(filename) sheet= book.worksheet 0 row_i=0 sheet.each do |row| - row_i+=1 - next if row_i==1 or row[0].nil? or row[1].nil? or row[2].nil? - h[row[0]]={} if h[row[0]].nil? - h[row[0]][row[1]]=row[2] + row_i += 1 + next if row_i == 1 or row[0].nil? or row[1].nil? or row[2].nil? + key = row[0].to_sym + h[key] ||= {} + h[key][row[1]] = row[2] end h end @@ -110,12 +121,12 @@ def inverse_hash(h, sep=Statsample::SPLIT_TOKEN) end def dictionary(h, sep=Statsample::SPLIT_TOKEN) - h.inject({}) {|a,v| a[v[0]]=v[1].split(sep); a } + h.inject({}) { |a,v| a[v[0]]=v[1].split(sep); a } end def recode_vector(v,h,sep=Statsample::SPLIT_TOKEN) - dict=dictionary(h,sep) - new_data=v.splitted(sep) + dict = dictionary(h,sep) + new_data = v.splitted(sep) new_data.collect do |c| if c.nil? nil @@ -134,20 +145,20 @@ def recode_dataset_split!(dataset, dictionary_hash, sep=Statsample::SPLIT_TOKEN) def _recode_dataset(dataset, h , sep=Statsample::SPLIT_TOKEN, split=false) v_names||=h.keys v_names.each do |v_name| - raise Exception, "Vector #{v_name} doesn't exists on Dataset" if !dataset.fields.include? v_name - recoded=recode_vector(dataset[v_name], h[v_name],sep).collect { |c| + raise Exception, "Vector #{v_name} doesn't exists on Dataset" if !dataset.vectors.include? v_name + recoded = recode_vector(dataset[v_name], h[v_name],sep).collect { |c| if c.nil? nil else c.join(sep) end }.to_vector - if(split) + if split recoded.split_by_separator(sep).each {|k,v| - dataset[v_name+"_"+k]=v + dataset[(v_name.to_s + "_" + k).to_sym] = v } else - dataset[v_name+"_recoded"]=recoded + dataset[(v_name.to_s + "_recoded").to_sym] = recoded end end end diff --git a/lib/statsample/converters.rb b/lib/statsample/converters.rb index 4440d97..5b34511 100644 --- a/lib/statsample/converters.rb +++ b/lib/statsample/converters.rb @@ -182,24 +182,24 @@ def preprocess_row(row, dates) def read(filename, opts=Hash.new) require 'spreadsheet' raise "options should be Hash" unless opts.is_a? Hash - opts_default={ - :worksheet_id=>0, - :ignore_lines=>0, - :empty=>[''] + opts_default = { + :worksheet_id => 0, + :ignore_lines => 0, + :empty => [''] } - opts=opts_default.merge opts + opts = opts_default.merge opts - worksheet_id=opts[:worksheet_id] - ignore_lines=opts[:ignore_lines] - empty=opts[:empty] + worksheet_id = opts[:worksheet_id] + ignore_lines = opts[:ignore_lines] + empty = opts[:empty] - first_row=true - fields=[] - ds=nil - line_number=0 - book = Spreadsheet.open filename - sheet= book.worksheet worksheet_id + first_row = true + fields = [] + ds = nil + line_number = 0 + book = Spreadsheet.open filename + sheet = book.worksheet worksheet_id sheet.each do |row| begin dates=[] @@ -208,32 +208,28 @@ def read(filename, opts=Hash.new) dates.push(i) end } - line_number+=1 - next if(line_number<=ignore_lines) + line_number+=1 + next if line_number <= ignore_lines preprocess_row(row,dates) + if first_row - fields=extract_fields(row) - ds=Statsample::Dataset.new(fields) + fields = extract_fields(row) + ds = Daru::DataFrame.new({}, order: fields) first_row=false else - rowa=process_row(row,empty) - (fields.size - rowa.size).times { - rowa << nil - } - ds.add_case(rowa,false) + rowa = process_row(row,empty) + (fields.size - rowa.size).times { rowa << nil } + ds.add_row rowa end rescue => e error="#{e.to_s}\nError on Line # #{line_number}:#{row.join(",")}" raise end end - convert_to_numeric_and_date(ds, fields) - ds.update_valid_data - fields.each {|f| - ds[f].name=f - } - ds.name=filename + ds.update + fields.map(&:to_sym).each { |f| ds[f].rename f } + ds.rename filename ds end end diff --git a/test/test_codification.rb b/test/test_codification.rb index fe5f491..f6699aa 100644 --- a/test/test_codification.rb +++ b/test/test_codification.rb @@ -3,31 +3,31 @@ class StatsampleCodificationTestCase < Minitest::Test def initialize(*args) v1 = %w(run walk,run walking running sleep sleeping,dreaming sleep,dream).to_vector @dict = { 'run' => 'r', 'walk' => 'w', 'walking' => 'w', 'running' => 'r', 'sleep' => 's', 'sleeping' => 's', 'dream' => 'd', 'dreaming' => 'd' } - @ds = { 'v1' => v1 }.to_dataset + @ds = Daru::DataFrame.new({ :v1 => v1 }) super end def test_create_hash expected_keys_v1 = %w(run walk walking running sleep sleeping dream dreaming).sort - hash = Statsample::Codification.create_hash(@ds, ['v1']) - assert_equal(['v1'], hash.keys) - assert_equal(expected_keys_v1, hash['v1'].keys.sort) - assert_equal(expected_keys_v1, hash['v1'].values.sort) + hash = Statsample::Codification.create_hash(@ds, [:v1]) + assert_equal([:v1], hash.keys) + assert_equal(expected_keys_v1, hash[:v1].keys.sort) + assert_equal(expected_keys_v1, hash[:v1].values.sort) end def test_create_excel filename = Dir.tmpdir + '/test_excel' + Time.now.to_s + '.xls' # filename = Tempfile.new("test_codification_"+Time.now().to_s) Statsample::Codification.create_excel(@ds, ['v1'], filename) - field = (['v1'] * 8).to_vector - keys = %w(dream dreaming run running sleep sleeping walk walking).to_vector + field = Daru::Vector.new(['v1'] * 8, name: :field) + keys = Daru::Vector.new(%w(dream dreaming run running sleep sleeping walk walking)) ds = Statsample::Excel.read(filename) - assert_equal(field, ds['field']) - assert_equal(keys, ds['original']) - assert_equal(keys, ds['recoded']) + assert_equal(field, ds[:field]) + assert_equal(keys, ds[:original]) + assert_equal(keys, ds[:recoded]) hash = Statsample::Codification.excel_to_recoded_hash(filename) - assert_equal(keys.data, hash['v1'].keys.sort) - assert_equal(keys.data, hash['v1'].values.sort) + assert_equal(keys.to_a, hash[:v1].keys.sort) + assert_equal(keys.to_a, hash[:v1].values.sort) end def test_create_yaml @@ -35,44 +35,44 @@ def test_create_yaml Statsample::Codification.create_yaml(@ds, []) end expected_keys_v1 = %w(run walk walking running sleep sleeping dream dreaming).sort - yaml_hash = Statsample::Codification.create_yaml(@ds, ['v1']) + yaml_hash = Statsample::Codification.create_yaml(@ds, [:v1]) h = YAML.load(yaml_hash) - assert_equal(['v1'], h.keys) - assert_equal(expected_keys_v1, h['v1'].keys.sort) + assert_equal([:v1], h.keys) + assert_equal(expected_keys_v1, h[:v1].keys.sort) tf = Tempfile.new('test_codification') - yaml_hash = Statsample::Codification.create_yaml(@ds, ['v1'], tf, Statsample::SPLIT_TOKEN) + yaml_hash = Statsample::Codification.create_yaml(@ds, [:v1], tf, Statsample::SPLIT_TOKEN) tf.close tf.open h = YAML.load(tf) - assert_equal(['v1'], h.keys) - assert_equal(expected_keys_v1, h['v1'].keys.sort) + assert_equal([:v1], h.keys) + assert_equal(expected_keys_v1, h[:v1].keys.sort) tf.close(true) end def test_recodification expected = [['r'], %w(w r), ['w'], ['r'], ['s'], %w(s d), %w(s d)] - assert_equal(expected, Statsample::Codification.recode_vector(@ds['v1'], @dict)) - v2 = ['run', 'walk,dreaming', nil, 'walk,dream,dreaming,walking'].to_vector + assert_equal(expected, Statsample::Codification.recode_vector(@ds[:v1], @dict)) + v2 = Daru::Vector.new(['run', 'walk,dreaming', nil, 'walk,dream,dreaming,walking']) expected = [['r'], %w(w d), nil, %w(w d)] assert_equal(expected, Statsample::Codification.recode_vector(v2, @dict)) end def test_recode_dataset_simple - Statsample::Codification.recode_dataset_simple!(@ds, 'v1' => @dict) + Statsample::Codification.recode_dataset_simple!(@ds, :v1 => @dict) expected_vector = ['r', 'w,r', 'w', 'r', 's', 's,d', 's,d'].to_vector - assert_not_equal(expected_vector, @ds['v1']) - assert_equal(expected_vector, @ds['v1_recoded']) + assert_not_equal(expected_vector, @ds[:v1]) + assert_equal(expected_vector, @ds[:v1_recoded]) end def test_recode_dataset_split - Statsample::Codification.recode_dataset_split!(@ds, 'v1' => @dict) + Statsample::Codification.recode_dataset_split!(@ds, :v1 => @dict) e = {} - e['r'] = [1, 1, 0, 1, 0, 0, 0].to_vector - e['w'] = [0, 1, 1, 0, 0, 0, 0].to_vector - e['s'] = [0, 0, 0, 0, 1, 1, 1].to_vector - e['d'] = [0, 0, 0, 0, 0, 1, 1].to_vector - e.each{|k, expected| - assert_equal(expected, @ds['v1_' + k], "Error on key #{k}") + e['r'] = Daru::Vector.new([1, 1, 0, 1, 0, 0, 0]) + e['w'] = Daru::Vector.new([0, 1, 1, 0, 0, 0, 0]) + e['s'] = Daru::Vector.new([0, 0, 0, 0, 1, 1, 1]) + e['d'] = Daru::Vector.new([0, 0, 0, 0, 0, 1, 1]) + e.each { |k, expected| + assert_equal(expected, @ds[('v1_' + k).to_sym], "Error on key #{k}") } end end From 3e4ce539d423e3a8b7de9d46137ad6a0c492cf4e Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 23 May 2015 11:28:00 +0530 Subject: [PATCH 045/119] removed rserve extension and ported to daru (https://github.com/v0dro/daru/commit/75f4505a5c0f1ea4bc730ae066835c163b8997d1) --- lib/statsample/rserve_extension.rb | 20 -------------- test/test_rserve_extension.rb | 42 ------------------------------ 2 files changed, 62 deletions(-) delete mode 100644 lib/statsample/rserve_extension.rb delete mode 100644 test/test_rserve_extension.rb diff --git a/lib/statsample/rserve_extension.rb b/lib/statsample/rserve_extension.rb deleted file mode 100644 index d439c91..0000000 --- a/lib/statsample/rserve_extension.rb +++ /dev/null @@ -1,20 +0,0 @@ -# Several additions to Statsample objects, to support -# rserve-client - -module Statsample - class Vector - def to_REXP - Rserve::REXP::Wrapper.wrap(data_with_nils) - end - end - class Dataset - def to_REXP - names=@fields - data=@fields.map {|f| - Rserve::REXP::Wrapper.wrap(@vectors[f].data_with_nils) - } - l=Rserve::Rlist.new(data,names) - Rserve::REXP.create_data_frame(l) - end - end -end \ No newline at end of file diff --git a/test/test_rserve_extension.rb b/test/test_rserve_extension.rb deleted file mode 100644 index 1c7ed6d..0000000 --- a/test/test_rserve_extension.rb +++ /dev/null @@ -1,42 +0,0 @@ -require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) -begin - require 'rserve' - require 'statsample/rserve_extension' - - class StatsampleRserveExtensionTestCase < Minitest::Test - context 'Statsample Rserve extensions' do - setup do - @r = Rserve::Connection.new - end - teardown do - @r.close - end - should 'return a valid rexp for numeric vector' do - a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric - rexp = a.to_REXP - assert(rexp.is_a? Rserve::REXP::Double) - assert_equal(rexp.to_ruby, a.data_with_nils) - @r.assign 'a', rexp - assert_equal(a.data_with_nils, @r.eval('a').to_ruby) - end - should 'return a valid rserve dataframe for statsample datasets' do - a = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric - b = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric - c = 100.times.map { |i| rand > 0.9 ? nil : i + rand }.to_numeric - ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset - rexp = ds.to_REXP - assert(rexp.is_a? Rserve::REXP::GenericVector) - ret = rexp.to_ruby - assert_equal(a.data_with_nils, ret['a']) - @r.assign 'df', rexp - out_df = @r.eval('df').to_ruby - assert_equal('data.frame', out_df.attributes['class']) - assert_equal(%w(a b c), out_df.attributes['names']) - assert_equal(a.data_with_nils, out_df['a']) - end - end - end - -rescue LoadError - puts 'Require rserve extension' -end From c88baf5c0540d26b9c6138a32731bc1a84e90660 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 23 May 2015 15:27:05 +0530 Subject: [PATCH 046/119] Statsample::Test now working with daru --- lib/statsample/test/levene.rb | 16 ++++----- lib/statsample/test/umannwhitney.rb | 54 ++++++++++++++--------------- test/test_reliability_icc.rb | 2 +- test/test_stest.rb | 18 +++++----- test/test_test_t.rb | 24 ++++++------- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/lib/statsample/test/levene.rb b/lib/statsample/test/levene.rb index 7054c84..0a12cad 100644 --- a/lib/statsample/test/levene.rb +++ b/lib/statsample/test/levene.rb @@ -30,9 +30,9 @@ class Levene # Input could be an array of vectors or a dataset def initialize(input, opts=Hash.new()) if input.is_a? Daru::DataFrame - @vectors=input.vectors.values + @vectors = input.to_hash.values else - @vectors=input + @vectors = input end @name=_("Levene Test") opts.each{|k,v| @@ -48,17 +48,18 @@ def report_building(builder) # :nodoc: builder.text "%s : F(%d, %d) = %0.4f , p = %0.4f" % [@name, @d1, @d2, f, probability] end def compute - n=@vectors.inject(0) {|ac,v| ac+v.n_valid} + n=@vectors.inject(0) { |ac,v| ac + v.n_valid} - zi=@vectors.collect {|vector| + zi=@vectors.collect do |vector| mean=vector.mean - Daru::Vector.new(vector.collect {|v| (v-mean).abs }) - } + Daru::Vector.new(vector.collect { |v| (v - mean).abs }) + end total_mean = Daru::Vector.new( zi.inject([]) do |ac,vector| ac + vector.only_valid(:array) - end).mean + end + ).mean k = @vectors.size sum_num = zi.inject(0) do |ac,vector| @@ -82,7 +83,6 @@ def compute def probability p_using_cdf(Distribution::F.cdf(f, @d1, @d2), :right) end - end end end diff --git a/lib/statsample/test/umannwhitney.rb b/lib/statsample/test/umannwhitney.rb index 7d02e38..cd5de1d 100644 --- a/lib/statsample/test/umannwhitney.rb +++ b/lib/statsample/test/umannwhitney.rb @@ -116,33 +116,33 @@ def self.distribution_permutations(n1,n2) # Params: Two Statsample::Vectors # def initialize(v1,v2, opts=Hash.new) - @v1=v1 - @v2=v2 - @n1=v1.valid_data.size - @n2=v2.valid_data.size - data=(v1.valid_data+v2.valid_data).to_numeric - groups=(([0]*@n1)+([1]*@n2)).to_vector - ds={'g'=>groups, 'data'=>data}.to_dataset - @t=nil - @ties=data.data.size!=data.data.uniq.size - if(@ties) - adjust_for_ties(ds['data']) + @v1 = v1 + @v2 = v2 + v1_valid = v1.only_valid.reset_index! + v2_valid = v2.only_valid.reset_index! + @n1 = v1_valid.size + @n2 = v2_valid.size + data = Daru::Vector.new(v1_valid.to_a + v2_valid.to_a) + groups = Daru::Vector.new(([0] * @n1) + ([1] * @n2)) + ds = Daru::DataFrame.new({:g => groups, :data => data}) + @t = nil + @ties = data.to_a.size != data.to_a.uniq.size + if @ties + adjust_for_ties(ds[:data]) end - ds['ranked']=ds['data'].ranked(:numeric) - - @n=ds.cases + ds[:ranked] = ds[:data].ranked + @n = ds.nrows - @r1=ds.filter{|r| r['g']==0}['ranked'].sum - @r2=((ds.cases*(ds.cases+1)).quo(2))-r1 - @u1=r1-((@n1*(@n1+1)).quo(2)) - @u2=r2-((@n2*(@n2+1)).quo(2)) - @u=(u1_("Mann-Whitney's U")} - @opts=opts_default.merge(opts) + @r1 = ds.filter_rows { |r| r[:g] == 0}[:ranked].sum + @r2 = ((ds.nrows * (ds.nrows + 1)).quo(2)) - r1 + @u1 = r1 - ((@n1 * (@n1 + 1)).quo(2)) + @u2 = r2 - ((@n2 * (@n2 + 1)).quo(2)) + @u = (u1 < u2) ? u1 : u2 + opts_default = { :name=>_("Mann-Whitney's U") } + @opts = opts_default.merge(opts) opts_default.keys.each {|k| send("#{k}=", @opts[k]) - } - + } end def report_building(generator) # :nodoc: generator.section(:name=>@name) do |s| @@ -160,8 +160,8 @@ def report_building(generator) # :nodoc: # Exact probability of finding values of U lower or equal to sample on U distribution. Use with caution with m*n>100000. # Uses u_sampling_distribution_as62 def probability_exact - dist=UMannWhitney.u_sampling_distribution_as62(@n1,@n2) - sum=0 + dist = UMannWhitney.u_sampling_distribution_as62(@n1,@n2) + sum = 0 (0..@u.to_i).each {|i| sum+=dist[i] } @@ -172,8 +172,8 @@ def probability_exact # == Reference: # * http://europe.isixsigma.com/library/content/c080806a.asp def adjust_for_ties(data) - @t=data.frequencies.find_all{|k,v| v>1}.inject(0) {|a,v| - a+(v[1]**3-v[1]).quo(12) + @t = data.frequencies.find_all { |k,v| v > 1 }.inject(0) { |a,v| + a + (v[1]**3 - v[1]).quo(12) } end diff --git a/test/test_reliability_icc.rb b/test/test_reliability_icc.rb index a252791..7b5668f 100644 --- a/test/test_reliability_icc.rb +++ b/test/test_reliability_icc.rb @@ -114,7 +114,7 @@ class StatsampleReliabilityIccTestCase < Minitest::Test begin require 'rserve' - require 'statsample/rserve_extension' + require 'daru/extensions/rserve' context 'McGraw and Wong' do teardown do @r = $reliability_icc[:r].close unless $reliability_icc[:r].nil? diff --git a/test/test_stest.rb b/test/test_stest.rb index 298a825..cfcfc58 100644 --- a/test/test_stest.rb +++ b/test/test_stest.rb @@ -24,26 +24,26 @@ def test_chi_square_matrix_only_observed end def test_u_mannwhitney - a = [1, 2, 3, 4, 5, 6].to_numeric - b = [0, 5, 7, 9, 10, 11].to_numeric + a = Daru::Vector.new([1, 2, 3, 4, 5, 6]) + b = Daru::Vector.new([0, 5, 7, 9, 10, 11]) assert_equal(7.5, Statsample::Test.u_mannwhitney(a, b).u) assert_equal(7.5, Statsample::Test.u_mannwhitney(b, a).u) - a = [1, 7, 8, 9, 10, 11].to_numeric - b = [2, 3, 4, 5, 6, 12].to_numeric + a = Daru::Vector.new([1, 7, 8, 9, 10, 11]) + b = Daru::Vector.new([2, 3, 4, 5, 6, 12]) assert_equal(11, Statsample::Test.u_mannwhitney(a, b).u) end def test_levene - a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_numeric - b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_numeric + a = Daru::Vector.new([1, 2, 3, 4, 5, 6, 7, 8, 100, 10]) + b = Daru::Vector.new([30, 40, 50, 60, 70, 80, 90, 100, 110, 120]) levene = Statsample::Test::Levene.new([a, b]) assert_levene(levene) end def test_levene_dataset - a = [1, 2, 3, 4, 5, 6, 7, 8, 100, 10].to_numeric - b = [30, 40, 50, 60, 70, 80, 90, 100, 110, 120].to_numeric - ds = { 'a' => a, 'b' => b }.to_dataset + a = Daru::Vector.new([1, 2, 3, 4, 5, 6, 7, 8, 100, 10]) + b = Daru::Vector.new([30, 40, 50, 60, 70, 80, 90, 100, 110, 120]) + ds = Daru::DataFrame.new({ :a => a, :b => b }) levene = Statsample::Test::Levene.new(ds) assert_levene(levene) end diff --git a/test/test_test_t.rb b/test/test_test_t.rb index e42ef5d..3b8cce6 100644 --- a/test/test_test_t.rb +++ b/test/test_test_t.rb @@ -4,24 +4,24 @@ class StatsampleTestTTestCase < Minitest::Test include Math context T do setup do - @a = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99].to_numeric - @b = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98].to_numeric + @a = Daru::Vector.new([30.02, 29.99, 30.11, 29.97, 30.01, 29.99]) + @b = Daru::Vector.new([29.89, 29.93, 29.72, 29.98, 30.02, 29.98]) @x1 = @a.mean @x2 = @b.mean @s1 = @a.sd @s2 = @b.sd - @n1 = @a.n - @n2 = @b.n + @n1 = @a.size + @n2 = @b.size end should 'calculate correctly standard t' do - t = Statsample::Test::T.new(@x1, @s1.quo(Math.sqrt(@a.n)), @a.n - 1) - assert_equal((@x1).quo(@s1.quo(Math.sqrt(@a.n))), t.t) - assert_equal(@a.n - 1, t.df) + t = Statsample::Test::T.new(@x1, @s1.quo(Math.sqrt(@a.size)), @a.size - 1) + assert_equal((@x1).quo(@s1.quo(Math.sqrt(@a.size))), t.t) + assert_equal(@a.size - 1, t.df) assert(t.summary.size > 0) end should 'calculate correctly t for one sample' do - t1 = [6, 4, 6, 7, 4, 5, 5, 12, 6, 1].to_numeric - t2 = [9, 6, 5, 10, 10, 8, 7, 10, 6, 5].to_numeric + t1 = Daru::Vector.new([6, 4, 6, 7, 4, 5, 5, 12, 6, 1]) + t2 = Daru::Vector.new([9, 6, 5, 10, 10, 8, 7, 10, 6, 5]) d = t1 - t2 t = Statsample::Test::T::OneSample.new(d) assert_in_delta(-2.631, t.t, 0.001) @@ -48,14 +48,14 @@ class StatsampleTestTTestCase < Minitest::Test assert_in_delta(0.09095, t.probability_not_equal_variance, 0.001) end should 'be the same using shorthand' do - v = 100.times.map { rand(100) }.to_numeric + v = Daru::Vector.new(100.times.map { rand(100) }) assert_equal(Statsample::Test.t_one_sample(v).t, T::OneSample.new(v).t) end should 'calculate all values for one sample T test' do u = @a.mean + (1 - rand * 2) tos = T::OneSample.new(@a, u: u) - assert_equal((@a.mean - u).quo(@a.sd.quo(sqrt(@a.n))), tos.t) - assert_equal(@a.n - 1, tos.df) + assert_equal((@a.mean - u).quo(@a.sd.quo(sqrt(@a.size))), tos.t) + assert_equal(@a.size - 1, tos.df) assert(tos.summary.size > 0) end end From 37fd97bcd3a813f28c5a0e92f5a8fc1f3a444f89 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 23 May 2015 15:54:24 +0530 Subject: [PATCH 047/119] matrix and csv read/write working fine now --- lib/statsample/converter/csv.rb | 34 ++++++++++++++--------------- lib/statsample/dataset.rb | 4 ++-- lib/statsample/matrix.rb | 2 +- test/test_csv.rb | 38 ++++++++++++++++----------------- test/test_matrix.rb | 26 +++++++++++----------- 5 files changed, 51 insertions(+), 53 deletions(-) diff --git a/lib/statsample/converter/csv.rb b/lib/statsample/converter/csv.rb index b611fa1..eeef01d 100644 --- a/lib/statsample/converter/csv.rb +++ b/lib/statsample/converter/csv.rb @@ -14,33 +14,28 @@ class << self # USE: # ds = Statsample::CSV.read('test_csv.csv') def read(filename, empty = [''], ignore_lines = 0, opts = {}) - first_row = true - fields = [] - ds = nil + first_row = true + fields = [] + ds = nil line_number = 0 - options = DEFAULT_OPTIONS.merge(opts) - - csv = ::CSV.open(filename, 'rb', options) + options = DEFAULT_OPTIONS.merge(opts) + csv = ::CSV.open(filename, 'rb', options) csv.each do |row| line_number += 1 - - if (line_number <= ignore_lines) - next - end + next if line_number <= ignore_lines if first_row fields = extract_fields(row) - ds = Statsample::Dataset.new(fields) + ds = Daru::DataFrame.new({}, order: fields) first_row = false else rowa = process_row(row, empty) - ds.add_case(rowa, false) + ds.add_row(rowa) end end - convert_to_numeric_and_date(ds, fields) - ds.update_valid_data + ds.update ds end @@ -52,11 +47,14 @@ def write(dataset, filename, convert_comma = false, opts = {}) options = DEFAULT_OPTIONS.merge(opts) writer = ::CSV.open(filename, 'w', options) - writer << dataset.fields + writer << dataset.vectors.to_a - dataset.each_array do |row| - row.collect! { |v| v.to_s.gsub('.', ',') } if convert_comma - writer << row + dataset.each_row do |row| + if convert_comma + writer << row.map { |v| v.to_s.gsub('.', ',') } + else + writer << row.to_a + end end writer.close diff --git a/lib/statsample/dataset.rb b/lib/statsample/dataset.rb index 2774975..1df9eff 100644 --- a/lib/statsample/dataset.rb +++ b/lib/statsample/dataset.rb @@ -2,8 +2,8 @@ class Hash # Creates a Statsample::Dataset based on a Hash - def to_dataset(*args) - Statsample::Dataset.new(self, *args) + def to_dataframe(*args) + Daru::DataFrame.new(self, *args) end end diff --git a/lib/statsample/matrix.rb b/lib/statsample/matrix.rb index 5bcd389..87d226b 100644 --- a/lib/statsample/matrix.rb +++ b/lib/statsample/matrix.rb @@ -14,7 +14,7 @@ def to_matrix def to_dataframe f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| "VAR_#{i+1}".to_sym } f = [f] unless f.is_a?(Array) - ds= Daru::DataFrame.new({}, order: f) + ds = Daru::DataFrame.new({}, order: f) f.each do |ff| ds[ff].rename ff end diff --git a/test/test_csv.rb b/test/test_csv.rb index ea92025..615f166 100644 --- a/test/test_csv.rb +++ b/test/test_csv.rb @@ -1,4 +1,4 @@ -require 'helpers_tests.rb' +require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleCSVTestCase < Minitest::Test def setup @@ -6,21 +6,21 @@ def setup end def test_read - header = %w(id name age city a1) + header = [:id, :name, :age, :city, :a1] data = { - 'id' => [1, 2, 3, 4, 5, 6].to_vector(:numeric), - 'name' => %w(Alex Claude Peter Franz George Fernand).to_vector(:object), - 'age' => [20, 23, 25, 27, 5.5, nil].to_vector(:numeric), - 'city' => ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:object), - 'a1' => ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:object) + :id => Daru::Vector.new([1, 2, 3, 4, 5, 6]), + :name => Daru::Vector.new(%w(Alex Claude Peter Franz George Fernand)), + :age => Daru::Vector.new([20, 23, 25, 27, 5.5, nil]), + :city => Daru::Vector.new(['New York', 'London', 'London', 'Paris', 'Tome', nil]), + :a1 => Daru::Vector.new(['a,b', 'b,c', 'a', nil, 'a,b,c', nil]) } - ds_exp = Statsample::Dataset.new(data, header) + ds_exp = Daru::DataFrame.new(data, order: header) - assert_equal(6, @ds.cases) - assert_equal(header, @ds.fields) + assert_equal(6, @ds.nrows) + assert_equal(header, @ds.vectors.to_a) - ds_exp.fields.each do |f| + ds_exp.vectors.each do |f| assert_equal(ds_exp[f], @ds[f]) end @@ -28,22 +28,22 @@ def test_read end def test_nil - assert_equal(nil, @ds['age'][5]) + assert_equal(nil, @ds[:age][5]) end def test_repeated ds = Statsample::CSV.read('test/fixtures/repeated_fields.csv') - assert_equal(%w(id name_1 age_1 city a1 name_2 age_2), ds.fields) - age = [3, 4, 5, 6, nil, 8].to_vector(:numeric) - assert_equal(age, ds['age_2']) + assert_equal([:id, :name_1, :age_1, :city, :a1, :name_2, :age_2], ds.vectors.to_a) + age = Daru::Vector.new([3, 4, 5, 6, nil, 8]) + assert_equal(age, ds[:age_2]) end # Testing fix for SciRuby/statsample#19. def test_accept_scientific_notation_as_float ds = Statsample::CSV.read('test/fixtures/scientific_notation.csv') - assert_equal(%w(x y), ds.fields) + assert_equal([:x, :y], ds.vectors.to_a) y = [9.629587310436753e+127, 1.9341543147883677e+129, 3.88485279048245e+130] - y.zip(ds['y']).each do |y_expected, y_ds| + y.zip(ds[:y]).each do |y_expected, y_ds| assert_in_delta(y_expected, y_ds) end @@ -55,8 +55,8 @@ def test_write ds2 = Statsample::CSV.read(filename.path) i = 0 - ds2.each_array do |row| - assert_equal(@ds.case_as_array(i), row) + ds2.each_row do |row| + assert_equal(@ds.row[i], row) i += 1 end end diff --git a/test/test_matrix.rb b/test/test_matrix.rb index ac99f0d..59008bf 100644 --- a/test/test_matrix.rb +++ b/test/test_matrix.rb @@ -4,17 +4,17 @@ class StatsampleMatrixTestCase < Minitest::Test def test_to_dataset m = Matrix[[1, 4], [2, 5], [3, 6]] m.extend Statsample::NamedMatrix - m.fields_y = %w(x1 x2) + m.fields_y = [:x1, :x2] m.name = 'test' samples = 100 - x1 = [1, 2, 3].to_numeric - x2 = [4, 5, 6].to_numeric - ds = { 'x1' => x1, 'x2' => x2 }.to_dataset - ds.name = 'test' - obs = m.to_dataset - assert_equal(ds['x1'], obs['x1']) - assert_equal(ds['x2'], obs['x2']) - assert_equal(ds['x1'].mean, obs['x1'].mean) + x1 =Daru::Vector.new([1, 2, 3]) + x2 =Daru::Vector.new([4, 5, 6]) + ds = Daru::DataFrame.new({ :x1 => x1, :x2 => x2 }) + ds.rename 'test' + obs = m.to_dataframe + assert_equal(ds[:x1], obs[:x1]) + assert_equal(ds[:x2], obs[:x2]) + assert_equal(ds[:x1].mean, obs[:x1].mean) end def test_covariate @@ -33,10 +33,10 @@ def test_covariate assert_equal(:covariance, a._type) - a = 50.times.collect { rand }.to_numeric - b = 50.times.collect { rand }.to_numeric - c = 50.times.collect { rand }.to_numeric - ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset + a = Daru::Vector.new(50.times.collect { rand }) + b = Daru::Vector.new(50.times.collect { rand }) + c = Daru::Vector.new(50.times.collect { rand }) + ds = Daru::DataFrame.new({ :a => a, :b => b, :c => c }) corr = Statsample::Bivariate.correlation_matrix(ds) real = Statsample::Bivariate.covariance_matrix(ds).correlation corr.row_size.times do |i| From 72a7ed47f8716166aba57603263c31fc35bbd121 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 23 May 2015 16:05:07 +0530 Subject: [PATCH 048/119] Statsample::GGobi now working with daru --- lib/statsample/converters.rb | 6 +++--- test/test_ggobi.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/statsample/converters.rb b/lib/statsample/converters.rb index 5b34511..02b4c8f 100644 --- a/lib/statsample/converters.rb +++ b/lib/statsample/converters.rb @@ -338,7 +338,7 @@ def values_definition(c,missing) # nickname = nickname def variable_definition(carrier,v,name,nickname=nil) nickname = (nickname.nil? ? "" : "nickname=\"#{nickname}\"" ) - if v.type==:object or v.data.find {|d| d.is_a? String } + if v.type==:object or v.to_a.find {|d| d.is_a? String } carrier.categorials.push(name) carrier.conversions[name]={} factors=v.factors @@ -346,11 +346,11 @@ def variable_definition(carrier,v,name,nickname=nil) out << "\n" out << (1..factors.size).to_a.collect{|i| carrier.conversions[name][factors[i-1]]=i - "#{v.labeling(factors[i-1])}" + "#{(v.labels[factors[i-1]] || factors[i-1])}" }.join("\n") out << "\n\n" out - elsif v.data.find {|d| d.is_a? Float} + elsif v.to_a.find {|d| d.is_a? Float} "" else "" diff --git a/test/test_ggobi.rb b/test/test_ggobi.rb index 1d67fb2..6f1724a 100644 --- a/test/test_ggobi.rb +++ b/test/test_ggobi.rb @@ -2,11 +2,11 @@ require 'ostruct' class StatsampleGGobiTestCase < Minitest::Test def setup - v1 = ([10.2, 20.3, 10, 20, 30, 40, 30, 20, 30, 40] * 10).to_vector(:numeric) - @v2 = (%w(a b c a a a b b c d) * 10).to_vector(:object) + v1 = Daru::Vector.new([10.2, 20.3, 10, 20, 30, 40, 30, 20, 30, 40] * 10) + @v2 = Daru::Vector.new(%w(a b c a a a b b c d) * 10) @v2.labels = { 'a' => 'letter a', 'd' => 'letter d' } - v3 = ([1, 2, 3, 4, 5, 4, 3, 2, 1, 2] * 10).to_vector(:numeric) - @ds = { 'v1' => v1, 'v2' => @v2, 'v3' => v3 }.to_dataset + v3 = Daru::Vector.new([1, 2, 3, 4, 5, 4, 3, 2, 1, 2] * 10) + @ds = Daru::DataFrame.new({ :v1 => v1, :v2 => @v2, :v3 => v3 }) end def test_values_definition From 6025ec95cdbbe96fbe60dd8d71856506936cad0d Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sat, 23 May 2015 16:29:46 +0530 Subject: [PATCH 049/119] Statsample::Reliability::ICC now works with daru --- lib/statsample/reliability/icc.rb | 6 +++--- test/test_reliability_icc.rb | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/statsample/reliability/icc.rb b/lib/statsample/reliability/icc.rb index 1f8160c..af6cf02 100644 --- a/lib/statsample/reliability/icc.rb +++ b/lib/statsample/reliability/icc.rb @@ -97,9 +97,9 @@ class ICC attr_accessor :name def initialize(ds, opts=Hash.new) @ds=ds.dup_only_valid - @vectors=@ds.vectors.values - @n=@ds.cases - @k=@ds.fields.size + @vectors=@ds.map { |e| e } + @n=@ds.nrows + @k=@ds.ncols compute @g_rho=0 @alpha=0.05 diff --git a/test/test_reliability_icc.rb b/test/test_reliability_icc.rb index 7b5668f..25f5e2a 100644 --- a/test/test_reliability_icc.rb +++ b/test/test_reliability_icc.rb @@ -5,11 +5,11 @@ class StatsampleReliabilityIccTestCase < Minitest::Test context Statsample::Reliability::ICC do setup do - a = [9, 6, 8, 7, 10, 6].to_numeric - b = [2, 1, 4, 1, 5, 2].to_numeric - c = [5, 3, 6, 2, 6, 4].to_numeric - d = [8, 2, 8, 6, 9, 7].to_numeric - @ds = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset + a = Daru::Vector.new([9, 6, 8, 7, 10, 6]) + b = Daru::Vector.new([2, 1, 4, 1, 5, 2]) + c = Daru::Vector.new([5, 3, 6, 2, 6, 4]) + d = Daru::Vector.new([8, 2, 8, 6, 9, 7]) + @ds = Daru::DataFrame.new({ :a => a, :b => b, :c => c, :d => d }) @icc = Statsample::Reliability::ICC.new(@ds) end should 'basic method be correct' do @@ -122,11 +122,11 @@ class StatsampleReliabilityIccTestCase < Minitest::Test setup do if $reliability_icc.nil? size = 100 - a = size.times.map { rand(10) }.to_numeric + a = Daru::Vector.new(size.times.map { rand(10) }) b = a.recode { |i| i + rand(4) - 2 } c = a.recode { |i| i + rand(4) - 2 } d = a.recode { |i| i + rand(4) - 2 } - @ds = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset + @ds = Daru::DataFrame.new({ :a => a, :b => b, :c => c, :d => d }) @icc = Statsample::Reliability::ICC.new(@ds) @r = Rserve::Connection.new From 6ef74f5c8b0785471ba17794a9a05a6e7a701780 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Sun, 24 May 2015 00:34:57 +0530 Subject: [PATCH 050/119] All Statsample::Reliability modules working with daru wip wip wip done --- lib/statsample/daru.rb | 25 + lib/statsample/graph/histogram.rb | 2 +- lib/statsample/histogram.rb | 6 +- lib/statsample/reliability.rb | 48 +- .../reliability/multiscaleanalysis.rb | 13 +- lib/statsample/reliability/scaleanalysis.rb | 101 +- .../reliability/skillscaleanalysis.rb | 63 +- test/test_dataset.rb | 958 ++++++------ test/test_gsl.rb | 8 +- test/test_reliability.rb | 94 +- test/test_reliability_skillscale.rb | 50 +- test/test_vector.rb | 1374 ++++++++--------- 12 files changed, 1395 insertions(+), 1347 deletions(-) diff --git a/lib/statsample/daru.rb b/lib/statsample/daru.rb index 8764b9b..4a0b936 100644 --- a/lib/statsample/daru.rb +++ b/lib/statsample/daru.rb @@ -1,6 +1,31 @@ # Opening the Daru::DataFrame class for adding methods to convert from # data structures to specialized statsample data structues like Multiset. module Daru + class Vector + def histogram(bins=10) + type == :numeric or raise TypeError, "Only numeric Vectors can do this operation." + + if bins.is_a? Array + h = Statsample::Histogram.alloc(bins) + else + # ugly patch. The upper limit for a bin has the form + # x < range + #h=Statsample::Histogram.new(self, bins) + valid = only_valid + min,max=Statsample::Util.nice(valid.min,valid.max) + # fix last data + if max == valid.max + max += 1e-10 + end + h = Statsample::Histogram.alloc(bins,[min,max]) + # Fix last bin + end + + h.increment(valid) + h + end + end + class DataFrame # Functions for converting to Statsample::Multiset def to_multiset_by_split(*vecs) diff --git a/lib/statsample/graph/histogram.rb b/lib/statsample/graph/histogram.rb index ef6da76..15d48c1 100644 --- a/lib/statsample/graph/histogram.rb +++ b/lib/statsample/graph/histogram.rb @@ -70,7 +70,7 @@ def pre_vis # :nodoc: @hist=@data @mean=@hist.estimated_mean @sd=@hist.estimated_standard_deviation - elsif @data.is_a? Statsample::Vector + elsif @data.is_a? Daru::Vector @mean=@data.mean @sd=@data.sd @bins||=Math::sqrt(@data.size).floor diff --git a/lib/statsample/histogram.rb b/lib/statsample/histogram.rb index be6564e..32562e0 100644 --- a/lib/statsample/histogram.rb +++ b/lib/statsample/histogram.rb @@ -95,9 +95,9 @@ def increment(x, w=1) if x.respond_to? :each x.each{|y| increment(y,w) } elsif x.is_a? Numeric - (range.size-1).times do |i| - if x>=range[i] and x= range[i] and x < range[i+1] + @bin[i] += w break end end diff --git a/lib/statsample/reliability.rb b/lib/statsample/reliability.rb index dde4c76..0597f17 100644 --- a/lib/statsample/reliability.rb +++ b/lib/statsample/reliability.rb @@ -4,30 +4,30 @@ class << self # Calculate Chonbach's alpha for a given dataset. # only uses tuples without missing data def cronbach_alpha(ods) - ds=ods.dup_only_valid - n_items=ds.fields.size - return nil if n_items<=1 - s2_items=ds.vectors.inject(0) {|ac,v| - ac+v[1].variance } - total=ds.vector_sum + ds = ods.dup_only_valid + n_items = ds.ncols + return nil if n_items <= 1 + s2_items = ds.to_hash.values.inject(0) { |ac,v| + ac + v.variance } + total = ds.vector_sum - (n_items.quo(n_items-1)) * (1-(s2_items.quo(total.variance))) + (n_items.quo(n_items - 1)) * (1 - (s2_items.quo(total.variance))) end # Calculate Chonbach's alpha for a given dataset # using standarized values for every vector. # Only uses tuples without missing data # Return nil if one or more vectors has 0 variance def cronbach_alpha_standarized(ods) + ds = ods.dup_only_valid + return nil if ds.any? { |v| v.variance==0} - ds=ods.dup_only_valid - - return nil if ds.vectors.any? {|k,v| v.variance==0} - - ds=ds.fields.inject({}){|a,f| - a[f]=ods[f].standarized; - a - }.to_dataset - + ds = Daru::DataFrame.new( + ds.vectors.to_a.inject({}) { |a,i| + a[i] = ods[i].standardize + a + } + ) + cronbach_alpha(ds) end # Predicted reliability of a test by replicating @@ -54,10 +54,10 @@ def cronbach_alpha_from_n_s2_cov(n,s2,cov) end # Get Cronbach's alpha from a covariance matrix def cronbach_alpha_from_covariance_matrix(cov) - n=cov.row_size + n = cov.row_size raise "covariance matrix should have at least 2 variables" if n < 2 - s2=n.times.inject(0) {|ac,i| ac+cov[i,i]} - (n.quo(n-1))*(1-(s2.quo(cov.total_sum))) + s2 = n.times.inject(0) { |ac,i| ac + cov[i,i] } + (n.quo(n - 1)) * (1 - (s2.quo(cov.total_sum))) end # Returns n necessary to obtain specific alpha # given variance and covariance mean of items @@ -82,8 +82,6 @@ def n_for_desired_alpha(alpha,s2,cov) end c_a=cronbach_alpha_from_n_s2_cov(n,s2,cov) dif=c_a - alpha - #puts "#{n} , #{c_a}" - end n end @@ -110,20 +108,20 @@ class ItemCharacteristicCurve attr_reader :totals, :counts, :vector_total def initialize (ds, vector_total=nil) vector_total||=ds.vector_sum - raise ArgumentError, "Total size != Dataset size" if vector_total.size!=ds.cases + raise ArgumentError, "Total size != Dataset size" if vector_total.size != ds.nrows @vector_total=vector_total @ds=ds @totals={} - @counts=@ds.fields.inject({}) {|a,v| a[v]={};a} + @counts=@ds.vectors.to_a.inject({}) {|a,v| a[v]={};a} process end def process i=0 - @ds.each do |row| + @ds.each_row do |row| tot=@vector_total[i] @totals[tot]||=0 @totals[tot]+=1 - @ds.fields.each do |f| + @ds.vectors.each do |f| item=row[f].to_s @counts[f][tot]||={} @counts[f][tot][item]||=0 diff --git a/lib/statsample/reliability/multiscaleanalysis.rb b/lib/statsample/reliability/multiscaleanalysis.rb index 3bc4cf2..83ebb85 100644 --- a/lib/statsample/reliability/multiscaleanalysis.rb +++ b/lib/statsample/reliability/multiscaleanalysis.rb @@ -91,6 +91,8 @@ def initialize(opts=Hash.new, &block) # If second parameters is empty, returns the ScaleAnalysis # code. def scale(code, ds=nil, opts=nil) + require 'awesome_print' + # ap @scales if ds.nil? @scales[code] else @@ -98,6 +100,7 @@ def scale(code, ds=nil, opts=nil) @scales_keys.push(code) @scales[code]=ScaleAnalysis.new(ds, opts) end + # ap @scales end # Delete ScaleAnalysis named code def delete_scale(code) @@ -123,14 +126,15 @@ def principal_axis_analysis(opts=nil) Statsample::Factor::PrincipalAxis.new(correlation_matrix, opts) end def dataset_from_scales - ds=Dataset.new(@scales_keys) + ds = Daru::DataFrame.new({}, order: @scales_keys.map(&:to_sym)) @scales.each_pair do |code,scale| - ds[code.to_s]=scale.ds.vector_sum - ds[code.to_s].name=scale.name + ds[code.to_sym] = scale.ds.vector_sum end - ds.update_valid_data + + ds.update ds end + def parallel_analysis(opts=nil) opts||=parallel_analysis_options Statsample::Factor::ParallelAnalysis.new(dataset_from_scales, opts) @@ -140,6 +144,7 @@ def parallel_analysis(opts=nil) def correlation_matrix Statsample::Bivariate.correlation_matrix(dataset_from_scales) end + def report_building(b) # :nodoc: b.section(:name=>name) do |s| s.section(:name=>_("Reliability analysis of scales")) do |s2| diff --git a/lib/statsample/reliability/scaleanalysis.rb b/lib/statsample/reliability/scaleanalysis.rb index 86c76a4..54d78fe 100644 --- a/lib/statsample/reliability/scaleanalysis.rb +++ b/lib/statsample/reliability/scaleanalysis.rb @@ -16,40 +16,40 @@ class ScaleAnalysis attr_accessor :name attr_accessor :summary_histogram def initialize(ds, opts=Hash.new) - @dumped=ds.fields.find_all {|f| - ds[f].variance==0 + @dumped=ds.vectors.to_a.find_all {|f| + ds[f].variance == 0 } - @ods=ds - @ds=ds.dup_only_valid(ds.fields - @dumped) - @ds.name=ds.name + @ods = ds + @ds = ds.dup_only_valid(ds.vectors.to_a - @dumped) + @ds.rename ds.name - @k=@ds.fields.size - @total=@ds.vector_sum + @k = @ds.ncols + @total = @ds.vector_sum @o_total=@dumped.size > 0 ? @ods.vector_sum : nil - @vector_mean=@ds.vector_mean - @item_mean=@vector_mean.mean - @item_sd=@vector_mean.sd + @vector_mean = @ds.vector_mean + @item_mean = @vector_mean.mean + @item_sd = @vector_mean.sd - @mean=@total.mean - @median=@total.median - - @skew=@total.skew - @kurtosis=@total.kurtosis - @sd = @total.sd - @variance=@total.variance - @valid_n = @total.size - opts_default={ - :name=>_("Reliability Analysis"), - :summary_histogram=>true + @mean = @total.mean + @median = @total.median + @skew = @total.skew + @kurtosis = @total.kurtosis + @sd = @total.sd + @variance = @total.variance + @valid_n = @total.size + + opts_default = { + :name => _("Reliability Analysis"), + :summary_histogram => true } - @opts=opts_default.merge(opts) - @opts.each{|k,v| self.send("#{k}=",v) if self.respond_to? k } + @opts = opts_default.merge(opts) + @opts.each{ |k,v| self.send("#{k}=",v) if self.respond_to? k } @cov_m=Statsample::Bivariate.covariance_matrix(@ds) # Mean for covariances and variances - @variances=@k.times.map {|i| @cov_m[i,i]}.to_numeric + @variances = Daru::Vector.new(@k.times.map { |i| @cov_m[i,i] }) @variances_mean=@variances.mean @covariances_mean=(@variance-@variances.sum).quo(@k**2-@k) #begin @@ -87,12 +87,15 @@ def item_characteristic_curve # Adjusted RPB(Point biserial-correlation) for each item # def item_total_correlation - @itc||=@ds.fields.inject({}) do |a,v| - vector=@ds[v].clone - ds2=@ds.clone - ds2.delete_vector(v) - total=ds2.vector_sum - a[v]=Statsample::Bivariate.pearson(vector,total) + vecs = @ds.vectors.to_a + @itc||=vecs.inject({}) do |a,v| + # vector=@ds[v].clone + # puts "#{vector.inspect}" + # ds2=@ds.dup + # puts "#{ds2.inspect}" + # ds2.delete_vector(v) + total=@ds.vector_sum(vecs - [v]) + a[v]=Statsample::Bivariate.pearson(@ds[v],total) a end end @@ -100,7 +103,7 @@ def mean_rpb item_total_correlation.values.to_numeric.mean end def item_statistics - @is||=@ds.fields.inject({}) do |a,v| + @is||=@ds.vectors.to_a.inject({}) do |a,v| a[v]={:mean=>@ds[v].mean, :sds=>Math::sqrt(@cov_m.variance(v))} a end @@ -110,20 +113,20 @@ def item_statistics def item_difficulty_analysis dif={} - @ds.fields.each{|f| dif[f]=@ds[f].mean } - dif_sort=dif.sort{|a,b| -(a[1]<=>b[1])} + @ds.vectors.each{|f| dif[f]=@ds[f].mean } + dif_sort = dif.sort { |a,b| -(a[1]<=>b[1]) } scores_sort={} scores=@ds.vector_mean - scores.each_index{|i| scores_sort[i]=scores[i] } + scores.each_index{ |i| scores_sort[i]=scores[i] } scores_sort=scores_sort.sort{|a,b| a[1]<=>b[1]} - ds_new=Statsample::Dataset.new(['case','score'] + dif_sort.collect{|a,b| a}) + ds_new = Daru::DataFrame.new({}, order: ([:case,:score] + dif_sort.collect{|a,b| a.to_sym})) scores_sort.each do |i,score| - row=[i, score] - case_row=@ds.case_as_hash(i) - dif_sort.each{|variable,dif_value| row.push(case_row[variable]) } - ds_new.add_case_array(row) + row = [i, score] + case_row = @ds.row[i].to_hash + dif_sort.each{ |variable,dif_value| row.push(case_row[variable]) } + ds_new.add_row(row) end - ds_new.update_valid_data + ds_new.update ds_new end @@ -132,9 +135,10 @@ def stats_if_deleted end def stats_if_deleted_intern # :nodoc: - return Hash.new if @ds.fields.size==1 - @ds.fields.inject({}) do |a,v| - cov_2=@cov_m.submatrix(@ds.fields-[v]) + return Hash.new if @ds.ncols == 1 + vecs = @ds.vectors.to_a + vecs.inject({}) do |a,v| + cov_2=@cov_m.submatrix(vecs - [v]) #ds2=@ds.clone #ds2.delete_vector(v) #total=ds2.vector_sum @@ -151,11 +155,10 @@ def stats_if_deleted_intern # :nodoc: def report_building(builder) #:nodoc: builder.section(:name=>@name) do |s| - if @dumped.size>0 s.section(:name=>"Items with variance=0") do |s1| s.table(:name=>_("Summary for %s with all items") % @name) do |t| - t.row [_("Items"), @ods.fields.size] + t.row [_("Items"), @ods.ncols] t.row [_("Sum mean"), "%0.4f" % @o_total.mean] t.row [_("S.d. mean"), "%0.4f" % @o_total.sd] end @@ -170,7 +173,7 @@ def report_building(builder) #:nodoc: s.table(:name=>_("Summary for %s") % @name) do |t| - t.row [_("Valid Items"), @ds.fields.size] + t.row [_("Valid Items"), @ds.ncols] t.row [_("Valid cases"), @valid_n] t.row [_("Sum mean"), "%0.4f" % @mean] @@ -193,8 +196,8 @@ def report_building(builder) #:nodoc: end if (@alpha) - s.text _("Items for obtain alpha(0.8) : %d" % Statsample::Reliability::n_for_desired_reliability(@alpha, 0.8, @ds.fields.size)) - s.text _("Items for obtain alpha(0.9) : %d" % Statsample::Reliability::n_for_desired_reliability(@alpha, 0.9, @ds.fields.size)) + s.text _("Items for obtain alpha(0.8) : %d" % Statsample::Reliability::n_for_desired_reliability(@alpha, 0.8, @ds.ncols)) + s.text _("Items for obtain alpha(0.9) : %d" % Statsample::Reliability::n_for_desired_reliability(@alpha, 0.9, @ds.ncols)) end @@ -203,7 +206,7 @@ def report_building(builder) #:nodoc: itc=item_total_correlation s.table(:name=>_("Items report for %s") % @name, :header=>["item","mean","sd", "mean if deleted", "var if deleted", "sd if deleted"," item-total correl.", "alpha if deleted"]) do |t| - @ds.fields.each do |f| + @ds.vectors.each do |f| row=["#{@ds[f].name}(#{f})"] if is[f] row+=[sprintf("%0.5f",is[f][:mean]), sprintf("%0.5f", is[f][:sds])] diff --git a/lib/statsample/reliability/skillscaleanalysis.rb b/lib/statsample/reliability/skillscaleanalysis.rb index c1eff12..c89b77b 100644 --- a/lib/statsample/reliability/skillscaleanalysis.rb +++ b/lib/statsample/reliability/skillscaleanalysis.rb @@ -4,11 +4,11 @@ module Reliability # Given a dataset with results and a correct answers hash, # generates a ScaleAnalysis # == Usage - # x1=%{a b b c}.to_vector - # x2=%{b a b c}.to_vector - # x3=%{a c b a}.to_vector - # ds={'x1'=>@x1,'x2'=>@x2,'x3'=>@x3}.to_dataset - # key={'x1'=>'a','x2'=>'b','x3'=>'a'} + # x1 = Daru::Vector.new(%{a b b c}) + # x2 = Daru::Vector.new(%{b a b c}) + # x3 = Daru::Vector.new(%{a c b a}) + # ds = Daru::DataFrame.new({:x1 => @x1, :x2 => @x2, :x3 => @x3}) + # key={ :x1 => 'a',:x2 => 'b', :x3 => 'a'} # ssa=Statsample::Reliability::SkillScaleAnalysis.new(ds,key) # puts ssa.summary class SkillScaleAnalysis @@ -30,53 +30,59 @@ def initialize(ds,key,opts=Hash.new) end # Dataset only corrected vectors def corrected_dataset_minimal - cds=corrected_dataset - dsm=@key.keys.inject({}) {|ac,v| ac[v]=cds[v];ac}.to_dataset - @key.keys.each do |k| - dsm[k].name=_("%s(corrected)") % @ds[k].name - dsm[k].labels=@ds[k].labels - end + cds = corrected_dataset + dsm = Daru::DataFrame.new( + @key.keys.inject({}) do |ac,v| + ac[v] = cds[v] + ac + end + ) - dsm.name=_("Corrected dataset from %s") % @ds.name + dsm.rename _("Corrected dataset from %s") % @ds.name dsm end + def vector_sum corrected_dataset_minimal.vector_sum end + def vector_mean corrected_dataset_minimal.vector_mean end + def scale_analysis - sa=ScaleAnalysis.new(corrected_dataset_minimal) + sa = ScaleAnalysis.new(corrected_dataset_minimal) sa.name=_("%s (Scale Analysis)") % @name sa end + def corrected_dataset if @cds.nil? - @cds=@ds.dup_empty - @key.keys.each {|k| @cds[k].type=:numeric; @cds[k].name=@ds[k].name} - @ds.each do |row| - out={} - row.each do |k,v| - if @key.keys.include? k - if @ds[k].is_valid? v - out[k]= @key[k]==v ? 1 : 0 + @cds = Daru::DataFrame.new({}, order: @ds.vectors, name: @ds.name) + @ds.each_row do |row| + out = {} + row.each_with_index do |v, k| + if @key.has_key? k + if @ds[k].exists? v + out[k]= @key[k] == v ? 1 : 0 else - out[k]=nil + out[k] = nil end else - out[k]=v + out[k] = v end end - @cds.add_case(out,false) + + @cds.add_row(Daru::Vector.new(out)) end - @cds.update_valid_data + @cds.update end @cds end + def report_building(builder) builder.section(:name=>@name) do |s| - sa=scale_analysis + sa = scale_analysis s.parse_element(sa) if summary_show_problematic_items s.section(:name=>_("Problematic Items")) do |spi| @@ -91,17 +97,16 @@ def report_building(builder) spi.table(:name=>"Proportions",:header=>[_("Value"), _("%")]) do |table| props.each do |k1,v| - table.row [ @ds[k].labeling(k1), "%0.3f" % v] + table.row [ @ds[k].index_of(k1), "%0.3f" % v] end end - end end end + spi.text _("No problematic items") if count==0 end end - end end end diff --git a/test/test_dataset.rb b/test/test_dataset.rb index 6e1b240..9041725 100644 --- a/test/test_dataset.rb +++ b/test/test_dataset.rb @@ -1,479 +1,479 @@ -require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) -class StatsampleDatasetTestCase < Minitest::Test - def setup - @ds = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([1, 2, 3, 4, 5]), 'name' => Statsample::Vector.new(%w(Alex Claude Peter Franz George)), 'age' => Statsample::Vector.new([20, 23, 25, 27, 5]), - 'city' => Statsample::Vector.new(['New York', 'London', 'London', 'Paris', 'Tome']), - 'a1' => Statsample::Vector.new(['a,b', 'b,c', 'a', nil, 'a,b,c']) }, %w(id name age city a1)) - end - - def test_nest - ds = { - 'a' => %w(a a a b b b).to_vector, - 'b' => %w(c c d d e e).to_vector, - 'c' => %w(f g h i j k).to_vector - }.to_dataset - nest = ds.nest('a', 'b') - assert_equal([{ 'c' => 'f' }, { 'c' => 'g' }], nest['a']['c']) - assert_equal([{ 'c' => 'h' }], nest['a']['d']) - assert_equal([{ 'c' => 'j' }, { 'c' => 'k' }], nest['b']['e']) - end - - def test_should_have_summary - assert(@ds.summary.size > 0) - end - - def test_basic - assert_equal(5, @ds.cases) - assert_equal(%w(id name age city a1), @ds.fields) - end - - def test_saveload - outfile = Tempfile.new('dataset.ds') - @ds.save(outfile.path) - a = Statsample.load(outfile.path) - assert_equal(@ds, a) - end - - def test_gsl - if Statsample.has_gsl? - matrix = GSL::Matrix[[1, 2], [3, 4], [5, 6]] - ds = Statsample::Dataset.new('v1' => [1, 3, 5].to_vector, 'v2' => [2, 4, 6].to_vector) - assert_equal(matrix, ds.to_gsl) - else - skip('Gsl needed') - end - end - - def test_matrix - matrix = Matrix[[1, 2], [3, 4], [5, 6]] - ds = Statsample::Dataset.new('v1' => [1, 3, 5].to_vector, 'v2' => [2, 4, 6].to_vector) - assert_equal(matrix, ds.to_matrix) - end - - def test_fields - @ds.fields = %w(name a1 id age city) - assert_equal(%w(name a1 id age city), @ds.fields) - @ds.fields = %w(id name age) - assert_equal(%w(id name age a1 city), @ds.fields) - end - - def test_merge - a = [1, 2, 3].to_numeric - b = [3, 4, 5].to_vector - c = [4, 5, 6].to_numeric - d = [7, 8, 9].to_vector - e = [10, 20, 30].to_vector - ds1 = { 'a' => a, 'b' => b }.to_dataset - ds2 = { 'c' => c, 'd' => d }.to_dataset - exp = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset - - assert_equal(exp, ds1.merge(ds2)) - exp.fields = %w(c d a b) - assert_equal(exp, ds2.merge(ds1)) - ds3 = { 'a' => e }.to_dataset - exp = { 'a_1' => a, 'b' => b, 'a_2' => e }.to_dataset - exp.fields = %w(a_1 b a_2) - assert_equal(exp, ds1.merge(ds3)) - end - - def test_each_vector - a = [1, 2, 3].to_vector - b = [3, 4, 5].to_vector - fields = %w(a b) - ds = Statsample::Dataset.new({ 'a' => a, 'b' => b }, fields) - res = [] - ds.each_vector{|k, v| - res.push([k, v]) - } - assert_equal([['a', a], ['b', b]], res) - ds.fields = %w(b a) - res = [] - ds.each_vector{|k, v| - res.push([k, v]) - } - assert_equal([['b', b], ['a', a]], res) - end - - def test_equality - v1 = [1, 2, 3, 4].to_vector - v2 = [5, 6, 7, 8].to_vector - ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) - v3 = [1, 2, 3, 4].to_vector - v4 = [5, 6, 7, 8].to_vector - ds2 = Statsample::Dataset.new({ 'v1' => v3, 'v2' => v4 }, %w(v2 v1)) - assert_equal(ds1, ds2) - ds2.fields = %w(v1 v2) - assert_not_equal(ds1, ds2) - end - - def test_add_vector - v = Statsample::Vector.new(%w(a b c d e)) - @ds.add_vector('new', v) - assert_equal(%w(id name age city a1 new), @ds.fields) - x = Statsample::Vector.new(%w(a b c d e f g)) - assert_raise ArgumentError do - @ds.add_vector('new2', x) - end - end - - def test_vector_by_calculation - a1 = [1, 2, 3, 4, 5, 6, 7].to_vector(:numeric) - a2 = [10, 20, 30, 40, 50, 60, 70].to_vector(:numeric) - a3 = [100, 200, 300, 400, 500, 600, 700].to_vector(:numeric) - ds = { 'a1' => a1, 'a2' => a2, 'a3' => a3 }.to_dataset - total = ds.vector_by_calculation {|row| - row['a1'] + row['a2'] + row['a3'] - } - expected = [111, 222, 333, 444, 555, 666, 777].to_vector(:numeric) - assert_equal(expected, total) - end - - def test_vector_sum - a1 = [1, 2, 3, 4, 5, nil].to_vector(:numeric) - a2 = [10, 10, 20, 20, 20, 30].to_vector(:numeric) - b1 = [nil, 1, 1, 1, 1, 2].to_vector(:numeric) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) - ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2 }.to_dataset - total = ds.vector_sum - a = ds.vector_sum(%w(a1 a2)) - b = ds.vector_sum(%w(b1 b2)) - expected_a = [11, 12, 23, 24, 25, nil].to_vector(:numeric) - expected_b = [nil, 3, 3, nil, 3, 5].to_vector(:numeric) - expected_total = [nil, 15, 26, nil, 28, nil].to_vector(:numeric) - assert_equal(expected_a, a) - assert_equal(expected_b, b) - assert_equal(expected_total, total) - end - - def test_vector_missing_values - a1 = [1, nil, 3, 4, 5, nil].to_vector(:numeric) - a2 = [10, nil, 20, 20, 20, 30].to_vector(:numeric) - b1 = [nil, nil, 1, 1, 1, 2].to_vector(:numeric) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) - c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) - ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset - mva = [2, 3, 0, 1, 0, 1].to_vector(:numeric) - assert_equal(mva, ds.vector_missing_values) - end - - def test_has_missing_values - a1 = [1, nil, 3, 4, 5, nil].to_vector(:numeric) - a2 = [10, nil, 20, 20, 20, 30].to_vector(:numeric) - b1 = [nil, nil, 1, 1, 1, 2].to_vector(:numeric) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) - c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) - ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset - assert(ds.has_missing_data?) - clean = ds.dup_only_valid - assert(!clean.has_missing_data?) - end - - def test_vector_count_characters - a1 = [1, 'abcde', 3, 4, 5, nil].to_vector(:numeric) - a2 = [10, 20.3, 20, 20, 20, 30].to_vector(:numeric) - b1 = [nil, '343434', 1, 1, 1, 2].to_vector(:numeric) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) - c = [nil, 2, 'This is a nice example', 2, 2, 2].to_vector(:numeric) - ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset - exp = [4, 17, 27, 5, 6, 5].to_vector(:numeric) - assert_equal(exp, ds.vector_count_characters) - end - - def test_vector_mean - a1 = [1, 2, 3, 4, 5, nil].to_vector(:numeric) - a2 = [10, 10, 20, 20, 20, 30].to_vector(:numeric) - b1 = [nil, 1, 1, 1, 1, 2].to_vector(:numeric) - b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) - c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) - ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset - total = ds.vector_mean - a = ds.vector_mean(%w(a1 a2), 1) - b = ds.vector_mean(%w(b1 b2), 1) - c = ds.vector_mean(%w(b1 b2 c), 1) - expected_a = [5.5, 6, 11.5, 12, 12.5, 30].to_vector(:numeric) - expected_b = [2, 1.5, 1.5, 1, 1.5, 2.5].to_vector(:numeric) - expected_c = [nil, 5.0 / 3, 7.0 / 3, 1.5, 5.0 / 3, 7.0 / 3].to_vector(:numeric) - expected_total = [nil, 3.4, 6, nil, 6.0, nil].to_vector(:numeric) - assert_equal(expected_a, a) - assert_equal(expected_b, b) - assert_equal(expected_c, c) - assert_equal(expected_total, total) - end - - def test_each_array - expected = [[1, 'Alex', 20, 'New York', 'a,b'], [2, 'Claude', 23, 'London', 'b,c'], [3, 'Peter', 25, 'London', 'a'], [4, 'Franz', 27, 'Paris', nil], [5, 'George', 5, 'Tome', 'a,b,c']] - out = [] - @ds.each_array{ |a| - out.push(a) - } - assert_equal(expected, out) - end - - def test_recode - @ds['age'].type = :numeric - @ds.recode!('age') { |c| c['id'] * 2 } - expected = [2, 4, 6, 8, 10].to_vector(:numeric) - assert_equal(expected, @ds['age']) - end - - def test_case_as - assert_equal({ 'id' => 1, 'name' => 'Alex', 'city' => 'New York', 'age' => 20, 'a1' => 'a,b' }, @ds.case_as_hash(0)) - assert_equal([5, 'George', 5, 'Tome', 'a,b,c'], @ds.case_as_array(4)) - # Native methods - assert_equal({ 'id' => 1, 'name' => 'Alex', 'city' => 'New York', 'age' => 20, 'a1' => 'a,b' }, @ds._case_as_hash(0)) - assert_equal([5, 'George', 5, 'Tome', 'a,b,c'], @ds._case_as_array(4)) - end - - def test_delete_vector - @ds.delete_vector('name') - assert_equal(%w(id age city a1), @ds.fields) - assert_equal(%w(a1 age city id), @ds.vectors.keys.sort) - end - - def test_change_type - @ds.col('age').type = :numeric - assert_equal(:numeric, @ds.col('age').type) - end - - def test_split_by_separator_recode - @ds.add_vectors_by_split_recode('a1', '_') - assert_equal(%w(id name age city a1 a1_1 a1_2 a1_3), @ds.fields) - assert_equal([1, 0, 1, nil, 1], @ds.col('a1_1').to_a) - assert_equal([1, 1, 0, nil, 1], @ds.col('a1_2').to_a) - assert_equal([0, 1, 0, nil, 1], @ds.col('a1_3').to_a) - { 'a1_1' => 'a1:a', 'a1_2' => 'a1:b', 'a1_3' => 'a1:c' }.each do |k, v| - assert_equal(v, @ds[k].name) - end - end - - def test_split_by_separator - @ds.add_vectors_by_split('a1', '_') - assert_equal(%w(id name age city a1 a1_a a1_b a1_c), @ds.fields) - assert_equal([1, 0, 1, nil, 1], @ds.col('a1_a').to_a) - assert_equal([1, 1, 0, nil, 1], @ds.col('a1_b').to_a) - assert_equal([0, 1, 0, nil, 1], @ds.col('a1_c').to_a) - end - - def test_percentiles - v1 = (1..100).to_a.to_numeric - assert_equal(50.5, v1.median) - assert_equal(25.5, v1.percentil(25)) - v2 = (1..99).to_a.to_numeric - assert_equal(50, v2.median) - assert_equal(25, v2.percentil(25)) - v3 = (1..50).to_a.to_numeric - assert_equal(25.5, v3.median) - assert_equal(13, v3.percentil(25)) - end - - def test_add_case - ds = Statsample::Dataset.new('a' => [].to_vector, 'b' => [].to_vector, 'c' => [].to_vector) - ds.add_case([1, 2, 3]) - ds.add_case('a' => 4, 'b' => 5, 'c' => 6) - ds.add_case([[7, 8, 9], %w(a b c)]) - assert_equal({ 'a' => 1, 'b' => 2, 'c' => 3 }, ds.case_as_hash(0)) - assert_equal([4, 5, 6], ds.case_as_array(1)) - assert_equal([7, 8, 9], ds.case_as_array(2)) - assert_equal(%w(a b c), ds.case_as_array(3)) - ds.add_case_array([6, 7, 1]) - ds.update_valid_data - assert_equal([6, 7, 1], ds.case_as_array(4)) - end - - def test_marshaling - ds_marshal = Marshal.load(Marshal.dump(@ds)) - assert_equal(ds_marshal, @ds) - end - - def test_range - v1 = [1, 2, 3, 4].to_vector - v2 = [5, 6, 7, 8].to_vector - v3 = [9, 10, 11, 12].to_vector - ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2, 'v3' => v3 }, %w(v3 v2 v1)) - assert_same(v1, ds1['v1']) - ds2 = ds1['v2'..'v1'] - assert_equal(%w(v2 v1), ds2.fields) - assert_same(ds1['v1'], ds2['v1']) - assert_same(ds1['v2'], ds2['v2']) - end - - def test_clone - v1 = [1, 2, 3, 4].to_vector - v2 = [5, 6, 7, 8].to_vector - ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) - ds2 = ds1.clone - assert_equal(ds1, ds2) - assert_not_same(ds1, ds2) - assert_equal(ds1['v1'], ds2['v1']) - assert_same(ds1['v1'], ds2['v1']) - assert_equal(ds1.fields, ds2.fields) - assert_not_same(ds1.fields, ds2.fields) - assert_equal(ds1.cases, ds2.cases) - - # partial clone - ds3 = ds1.clone('v1') - ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) - assert_equal(ds_exp, ds3) - assert_not_same(ds_exp, ds3) - assert_equal(ds3['v1'], ds_exp['v1']) - assert_same(ds3['v1'], ds_exp['v1']) - assert_equal(ds3.fields, ds_exp.fields) - assert_equal(ds3.cases, ds_exp.cases) - - assert_not_same(ds3.fields, ds_exp.fields) - end - - def test_dup - v1 = [1, 2, 3, 4].to_vector - v2 = [5, 6, 7, 8].to_vector - ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) - ds2 = ds1.dup - assert_equal(ds1, ds2) - assert_not_same(ds1, ds2) - assert_equal(ds1['v1'], ds2['v1']) - assert_not_same(ds1['v1'], ds2['v1']) - assert_equal(ds1.cases, ds2.cases) - - assert_equal(ds1.fields, ds2.fields) - assert_not_same(ds1.fields, ds2.fields) - ds1['v1'].type = :numeric - # dup partial - ds3 = ds1.dup('v1') - ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) - assert_equal(ds_exp, ds3) - assert_not_same(ds_exp, ds3) - assert_equal(ds3['v1'], ds_exp['v1']) - assert_not_same(ds3['v1'], ds_exp['v1']) - assert_equal(ds3.fields, ds_exp.fields) - assert_equal(ds3.cases, ds_exp.cases) - - assert_not_same(ds3.fields, ds_exp.fields) - - # empty - ds3 = ds1.dup_empty - assert_not_equal(ds1, ds3) - assert_not_equal(ds1['v1'], ds3['v1']) - assert_equal([], ds3['v1'].data) - assert_equal([], ds3['v2'].data) - assert_equal(:numeric, ds3['v1'].type) - assert_equal(ds1.fields, ds2.fields) - assert_not_same(ds1.fields, ds2.fields) - end - - def test_from_to - assert_equal(%w(name age city), @ds.from_to('name', 'city')) - assert_raise ArgumentError do - @ds.from_to('name', 'a2') - end - end - - def test_each_array_with_nils - v1 = [1, -99, 3, 4, 'na'].to_vector(:numeric, missing_values: [-99, 'na']) - v2 = [5, 6, -99, 8, 20].to_vector(:numeric, missing_values: [-99]) - v3 = [9, 10, 11, 12, 20].to_vector(:numeric, missing_values: [-99]) - ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) - ds2 = ds1.dup_empty - ds1.each_array_with_nils {|row| - ds2.add_case_array(row) - } - ds2.update_valid_data - assert_equal([1, nil, 3, 4, nil], ds2['v1'].data) - assert_equal([5, 6, nil, 8, 20], ds2['v2'].data) - end - - def test_dup_only_valid - v1 = [1, nil, 3, 4].to_vector(:numeric) - v2 = [5, 6, nil, 8].to_vector(:numeric) - v3 = [9, 10, 11, 12].to_vector(:numeric) - ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) - ds2 = ds1.dup_only_valid - expected = Statsample::Dataset.new('v1' => [1, 4].to_vector(:numeric), 'v2' => [5, 8].to_vector(:numeric), 'v3' => [9, 12].to_vector(:numeric)) - assert_equal(expected, ds2) - assert_equal(expected.vectors.values, Statsample.only_valid(v1, v2, v3)) - expected_partial = Statsample::Dataset.new('v1' => [1, 3, 4].to_vector(:numeric), 'v3' => [9, 11, 12].to_vector(:numeric)) - assert_equal(expected_partial, ds1.dup_only_valid(%w(v1 v3))) - end - - def test_filter - @ds['age'].type = :numeric - filtered = @ds.filter { |c| c['id'] == 2 or c['id'] == 4 } - expected = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([2, 4]), 'name' => Statsample::Vector.new(%w(Claude Franz)), 'age' => Statsample::Vector.new([23, 27], :numeric), - 'city' => Statsample::Vector.new(%w(London Paris)), - 'a1' => Statsample::Vector.new(['b,c', nil]) }, %w(id name age city a1)) - assert_equal(expected, filtered) - end - - def test_filter_field - @ds['age'].type = :numeric - filtered = @ds.filter_field('id') { |c| c['id'] == 2 or c['id'] == 4 } - expected = [2, 4].to_vector - assert_equal(expected, filtered) - end - - def test_verify - name = %w(r1 r2 r3 r4).to_vector(:object) - v1 = [1, 2, 3, 4].to_vector(:numeric) - v2 = [4, 3, 2, 1].to_vector(:numeric) - v3 = [10, 20, 30, 40].to_vector(:numeric) - v4 = %w(a b a b).to_vector(:object) - ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'id' => name }.to_dataset - ds.fields = %w(v1 v2 v3 v4 id) - # Correct - t1 = create_test('If v4=a, v1 odd') { |r| r['v4'] == 'b' or (r['v4'] == 'a' and r['v1'].odd?) } - t2 = create_test('v3=v1*10') { |r| r['v3'] == r['v1'] * 10 } - # Fail! - t3 = create_test("v4='b'") { |r| r['v4'] == 'b' } - exp1 = ["1 [1]: v4='b'", "3 [3]: v4='b'"] - exp2 = ["1 [r1]: v4='b'", "3 [r3]: v4='b'"] - res = ds.verify(t3, t1, t2) - assert_equal(exp1, res) - res = ds.verify('id', t1, t2, t3) - assert_equal(exp2, res) - end - - def test_compute_operation - v1 = [1, 2, 3, 4].to_vector(:numeric) - v2 = [4, 3, 2, 1].to_vector(:numeric) - v3 = [10, 20, 30, 40].to_vector(:numeric) - vnumeric = [1.quo(2), 1, 3.quo(2), 2].to_vector(:numeric) - vsum = [1 + 4 + 10.0, 2 + 3 + 20.0, 3 + 2 + 30.0, 4 + 1 + 40.0].to_vector(:numeric) - vmult = [1 * 4, 2 * 3, 3 * 2, 4 * 1].to_vector(:numeric) - ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3 }.to_dataset - assert_equal(vnumeric, ds.compute('v1/2')) - assert_equal(vsum, ds.compute('v1+v2+v3')) - assert_equal(vmult, ds.compute('v1*v2')) - end - - def test_crosstab_with_asignation - v1 = %w(a a a b b b c c c).to_vector - v2 = %w(a b c a b c a b c).to_vector - v3 = %w(0 1 0 0 1 1 0 0 1).to_numeric - ds = Statsample::Dataset.crosstab_by_asignation(v1, v2, v3) - assert_equal(:object, ds['_id'].type) - assert_equal(:numeric, ds['a'].type) - assert_equal(:numeric, ds['b'].type) - ev_id = %w(a b c).to_vector - ev_a = %w(0 0 0).to_numeric - ev_b = %w(1 1 0).to_numeric - ev_c = %w(0 1 1).to_numeric - ds2 = { '_id' => ev_id, 'a' => ev_a, 'b' => ev_b, 'c' => ev_c }.to_dataset - assert_equal(ds, ds2) - end - - def test_one_to_many - cases = [ - ['1', 'george', 'red', 10, 'blue', 20, nil, nil], - ['2', 'fred', 'green', 15, 'orange', 30, 'white', 20], - ['3', 'alfred', nil, nil, nil, nil, nil, nil] - ] - ds = Statsample::Dataset.new(%w(id name car_color1 car_value1 car_color2 car_value2 car_color3 car_value3)) - cases.each { |c| ds.add_case_array c } - ds.update_valid_data - ids = %w(1 1 2 2 2).to_vector - colors = %w(red blue green orange white).to_vector - values = [10, 20, 15, 30, 20].to_vector - col_ids = [1, 2, 1, 2, 3].to_numeric - ds_expected = { 'id' => ids, '_col_id' => col_ids, 'color' => colors, 'value' => values }.to_dataset(%w(id _col_id color value)) - assert_equal(ds_expected, ds.one_to_many(%w(id), 'car_%v%n')) - end -end +# require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) +# class StatsampleDatasetTestCase +# def setup +# @ds = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([1, 2, 3, 4, 5]), 'name' => Statsample::Vector.new(%w(Alex Claude Peter Franz George)), 'age' => Statsample::Vector.new([20, 23, 25, 27, 5]), +# 'city' => Statsample::Vector.new(['New York', 'London', 'London', 'Paris', 'Tome']), +# 'a1' => Statsample::Vector.new(['a,b', 'b,c', 'a', nil, 'a,b,c']) }, %w(id name age city a1)) +# end + +# def test_nest +# ds = { +# 'a' => %w(a a a b b b).to_vector, +# 'b' => %w(c c d d e e).to_vector, +# 'c' => %w(f g h i j k).to_vector +# }.to_dataset +# nest = ds.nest('a', 'b') +# assert_equal([{ 'c' => 'f' }, { 'c' => 'g' }], nest['a']['c']) +# assert_equal([{ 'c' => 'h' }], nest['a']['d']) +# assert_equal([{ 'c' => 'j' }, { 'c' => 'k' }], nest['b']['e']) +# end + +# def test_should_have_summary +# assert(@ds.summary.size > 0) +# end + +# def test_basic +# assert_equal(5, @ds.cases) +# assert_equal(%w(id name age city a1), @ds.fields) +# end + +# def test_saveload +# outfile = Tempfile.new('dataset.ds') +# @ds.save(outfile.path) +# a = Statsample.load(outfile.path) +# assert_equal(@ds, a) +# end + +# def test_gsl +# if Statsample.has_gsl? +# matrix = GSL::Matrix[[1, 2], [3, 4], [5, 6]] +# ds = Statsample::Dataset.new('v1' => [1, 3, 5].to_vector, 'v2' => [2, 4, 6].to_vector) +# assert_equal(matrix, ds.to_gsl) +# else +# skip('Gsl needed') +# end +# end + +# def test_matrix +# matrix = Matrix[[1, 2], [3, 4], [5, 6]] +# ds = Statsample::Dataset.new('v1' => [1, 3, 5].to_vector, 'v2' => [2, 4, 6].to_vector) +# assert_equal(matrix, ds.to_matrix) +# end + +# def test_fields +# @ds.fields = %w(name a1 id age city) +# assert_equal(%w(name a1 id age city), @ds.fields) +# @ds.fields = %w(id name age) +# assert_equal(%w(id name age a1 city), @ds.fields) +# end + +# def test_merge +# a = [1, 2, 3].to_numeric +# b = [3, 4, 5].to_vector +# c = [4, 5, 6].to_numeric +# d = [7, 8, 9].to_vector +# e = [10, 20, 30].to_vector +# ds1 = { 'a' => a, 'b' => b }.to_dataset +# ds2 = { 'c' => c, 'd' => d }.to_dataset +# exp = { 'a' => a, 'b' => b, 'c' => c, 'd' => d }.to_dataset + +# assert_equal(exp, ds1.merge(ds2)) +# exp.fields = %w(c d a b) +# assert_equal(exp, ds2.merge(ds1)) +# ds3 = { 'a' => e }.to_dataset +# exp = { 'a_1' => a, 'b' => b, 'a_2' => e }.to_dataset +# exp.fields = %w(a_1 b a_2) +# assert_equal(exp, ds1.merge(ds3)) +# end + +# def test_each_vector +# a = [1, 2, 3].to_vector +# b = [3, 4, 5].to_vector +# fields = %w(a b) +# ds = Statsample::Dataset.new({ 'a' => a, 'b' => b }, fields) +# res = [] +# ds.each_vector{|k, v| +# res.push([k, v]) +# } +# assert_equal([['a', a], ['b', b]], res) +# ds.fields = %w(b a) +# res = [] +# ds.each_vector{|k, v| +# res.push([k, v]) +# } +# assert_equal([['b', b], ['a', a]], res) +# end + +# def test_equality +# v1 = [1, 2, 3, 4].to_vector +# v2 = [5, 6, 7, 8].to_vector +# ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) +# v3 = [1, 2, 3, 4].to_vector +# v4 = [5, 6, 7, 8].to_vector +# ds2 = Statsample::Dataset.new({ 'v1' => v3, 'v2' => v4 }, %w(v2 v1)) +# assert_equal(ds1, ds2) +# ds2.fields = %w(v1 v2) +# assert_not_equal(ds1, ds2) +# end + +# def test_add_vector +# v = Statsample::Vector.new(%w(a b c d e)) +# @ds.add_vector('new', v) +# assert_equal(%w(id name age city a1 new), @ds.fields) +# x = Statsample::Vector.new(%w(a b c d e f g)) +# assert_raise ArgumentError do +# @ds.add_vector('new2', x) +# end +# end + +# def test_vector_by_calculation +# a1 = [1, 2, 3, 4, 5, 6, 7].to_vector(:numeric) +# a2 = [10, 20, 30, 40, 50, 60, 70].to_vector(:numeric) +# a3 = [100, 200, 300, 400, 500, 600, 700].to_vector(:numeric) +# ds = { 'a1' => a1, 'a2' => a2, 'a3' => a3 }.to_dataset +# total = ds.vector_by_calculation {|row| +# row['a1'] + row['a2'] + row['a3'] +# } +# expected = [111, 222, 333, 444, 555, 666, 777].to_vector(:numeric) +# assert_equal(expected, total) +# end + +# def test_vector_sum +# a1 = [1, 2, 3, 4, 5, nil].to_vector(:numeric) +# a2 = [10, 10, 20, 20, 20, 30].to_vector(:numeric) +# b1 = [nil, 1, 1, 1, 1, 2].to_vector(:numeric) +# b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) +# ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2 }.to_dataset +# total = ds.vector_sum +# a = ds.vector_sum(%w(a1 a2)) +# b = ds.vector_sum(%w(b1 b2)) +# expected_a = [11, 12, 23, 24, 25, nil].to_vector(:numeric) +# expected_b = [nil, 3, 3, nil, 3, 5].to_vector(:numeric) +# expected_total = [nil, 15, 26, nil, 28, nil].to_vector(:numeric) +# assert_equal(expected_a, a) +# assert_equal(expected_b, b) +# assert_equal(expected_total, total) +# end + +# def test_vector_missing_values +# a1 = [1, nil, 3, 4, 5, nil].to_vector(:numeric) +# a2 = [10, nil, 20, 20, 20, 30].to_vector(:numeric) +# b1 = [nil, nil, 1, 1, 1, 2].to_vector(:numeric) +# b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) +# c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) +# ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset +# mva = [2, 3, 0, 1, 0, 1].to_vector(:numeric) +# assert_equal(mva, ds.vector_missing_values) +# end + +# def test_has_missing_values +# a1 = [1, nil, 3, 4, 5, nil].to_vector(:numeric) +# a2 = [10, nil, 20, 20, 20, 30].to_vector(:numeric) +# b1 = [nil, nil, 1, 1, 1, 2].to_vector(:numeric) +# b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) +# c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) +# ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset +# assert(ds.has_missing_data?) +# clean = ds.dup_only_valid +# assert(!clean.has_missing_data?) +# end + +# def test_vector_count_characters +# a1 = [1, 'abcde', 3, 4, 5, nil].to_vector(:numeric) +# a2 = [10, 20.3, 20, 20, 20, 30].to_vector(:numeric) +# b1 = [nil, '343434', 1, 1, 1, 2].to_vector(:numeric) +# b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) +# c = [nil, 2, 'This is a nice example', 2, 2, 2].to_vector(:numeric) +# ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset +# exp = [4, 17, 27, 5, 6, 5].to_vector(:numeric) +# assert_equal(exp, ds.vector_count_characters) +# end + +# def test_vector_mean +# a1 = [1, 2, 3, 4, 5, nil].to_vector(:numeric) +# a2 = [10, 10, 20, 20, 20, 30].to_vector(:numeric) +# b1 = [nil, 1, 1, 1, 1, 2].to_vector(:numeric) +# b2 = [2, 2, 2, nil, 2, 3].to_vector(:numeric) +# c = [nil, 2, 4, 2, 2, 2].to_vector(:numeric) +# ds = { 'a1' => a1, 'a2' => a2, 'b1' => b1, 'b2' => b2, 'c' => c }.to_dataset +# total = ds.vector_mean +# a = ds.vector_mean(%w(a1 a2), 1) +# b = ds.vector_mean(%w(b1 b2), 1) +# c = ds.vector_mean(%w(b1 b2 c), 1) +# expected_a = [5.5, 6, 11.5, 12, 12.5, 30].to_vector(:numeric) +# expected_b = [2, 1.5, 1.5, 1, 1.5, 2.5].to_vector(:numeric) +# expected_c = [nil, 5.0 / 3, 7.0 / 3, 1.5, 5.0 / 3, 7.0 / 3].to_vector(:numeric) +# expected_total = [nil, 3.4, 6, nil, 6.0, nil].to_vector(:numeric) +# assert_equal(expected_a, a) +# assert_equal(expected_b, b) +# assert_equal(expected_c, c) +# assert_equal(expected_total, total) +# end + +# def test_each_array +# expected = [[1, 'Alex', 20, 'New York', 'a,b'], [2, 'Claude', 23, 'London', 'b,c'], [3, 'Peter', 25, 'London', 'a'], [4, 'Franz', 27, 'Paris', nil], [5, 'George', 5, 'Tome', 'a,b,c']] +# out = [] +# @ds.each_array{ |a| +# out.push(a) +# } +# assert_equal(expected, out) +# end + +# def test_recode +# @ds['age'].type = :numeric +# @ds.recode!('age') { |c| c['id'] * 2 } +# expected = [2, 4, 6, 8, 10].to_vector(:numeric) +# assert_equal(expected, @ds['age']) +# end + +# def test_case_as +# assert_equal({ 'id' => 1, 'name' => 'Alex', 'city' => 'New York', 'age' => 20, 'a1' => 'a,b' }, @ds.case_as_hash(0)) +# assert_equal([5, 'George', 5, 'Tome', 'a,b,c'], @ds.case_as_array(4)) +# # Native methods +# assert_equal({ 'id' => 1, 'name' => 'Alex', 'city' => 'New York', 'age' => 20, 'a1' => 'a,b' }, @ds._case_as_hash(0)) +# assert_equal([5, 'George', 5, 'Tome', 'a,b,c'], @ds._case_as_array(4)) +# end + +# def test_delete_vector +# @ds.delete_vector('name') +# assert_equal(%w(id age city a1), @ds.fields) +# assert_equal(%w(a1 age city id), @ds.vectors.keys.sort) +# end + +# def test_change_type +# @ds.col('age').type = :numeric +# assert_equal(:numeric, @ds.col('age').type) +# end + +# def test_split_by_separator_recode +# @ds.add_vectors_by_split_recode('a1', '_') +# assert_equal(%w(id name age city a1 a1_1 a1_2 a1_3), @ds.fields) +# assert_equal([1, 0, 1, nil, 1], @ds.col('a1_1').to_a) +# assert_equal([1, 1, 0, nil, 1], @ds.col('a1_2').to_a) +# assert_equal([0, 1, 0, nil, 1], @ds.col('a1_3').to_a) +# { 'a1_1' => 'a1:a', 'a1_2' => 'a1:b', 'a1_3' => 'a1:c' }.each do |k, v| +# assert_equal(v, @ds[k].name) +# end +# end + +# def test_split_by_separator +# @ds.add_vectors_by_split('a1', '_') +# assert_equal(%w(id name age city a1 a1_a a1_b a1_c), @ds.fields) +# assert_equal([1, 0, 1, nil, 1], @ds.col('a1_a').to_a) +# assert_equal([1, 1, 0, nil, 1], @ds.col('a1_b').to_a) +# assert_equal([0, 1, 0, nil, 1], @ds.col('a1_c').to_a) +# end + +# def test_percentiles +# v1 = (1..100).to_a.to_numeric +# assert_equal(50.5, v1.median) +# assert_equal(25.5, v1.percentil(25)) +# v2 = (1..99).to_a.to_numeric +# assert_equal(50, v2.median) +# assert_equal(25, v2.percentil(25)) +# v3 = (1..50).to_a.to_numeric +# assert_equal(25.5, v3.median) +# assert_equal(13, v3.percentil(25)) +# end + +# def test_add_case +# ds = Statsample::Dataset.new('a' => [].to_vector, 'b' => [].to_vector, 'c' => [].to_vector) +# ds.add_case([1, 2, 3]) +# ds.add_case('a' => 4, 'b' => 5, 'c' => 6) +# ds.add_case([[7, 8, 9], %w(a b c)]) +# assert_equal({ 'a' => 1, 'b' => 2, 'c' => 3 }, ds.case_as_hash(0)) +# assert_equal([4, 5, 6], ds.case_as_array(1)) +# assert_equal([7, 8, 9], ds.case_as_array(2)) +# assert_equal(%w(a b c), ds.case_as_array(3)) +# ds.add_case_array([6, 7, 1]) +# ds.update_valid_data +# assert_equal([6, 7, 1], ds.case_as_array(4)) +# end + +# def test_marshaling +# ds_marshal = Marshal.load(Marshal.dump(@ds)) +# assert_equal(ds_marshal, @ds) +# end + +# def test_range +# v1 = [1, 2, 3, 4].to_vector +# v2 = [5, 6, 7, 8].to_vector +# v3 = [9, 10, 11, 12].to_vector +# ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2, 'v3' => v3 }, %w(v3 v2 v1)) +# assert_same(v1, ds1['v1']) +# ds2 = ds1['v2'..'v1'] +# assert_equal(%w(v2 v1), ds2.fields) +# assert_same(ds1['v1'], ds2['v1']) +# assert_same(ds1['v2'], ds2['v2']) +# end + +# def test_clone +# v1 = [1, 2, 3, 4].to_vector +# v2 = [5, 6, 7, 8].to_vector +# ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) +# ds2 = ds1.clone +# assert_equal(ds1, ds2) +# assert_not_same(ds1, ds2) +# assert_equal(ds1['v1'], ds2['v1']) +# assert_same(ds1['v1'], ds2['v1']) +# assert_equal(ds1.fields, ds2.fields) +# assert_not_same(ds1.fields, ds2.fields) +# assert_equal(ds1.cases, ds2.cases) + +# # partial clone +# ds3 = ds1.clone('v1') +# ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) +# assert_equal(ds_exp, ds3) +# assert_not_same(ds_exp, ds3) +# assert_equal(ds3['v1'], ds_exp['v1']) +# assert_same(ds3['v1'], ds_exp['v1']) +# assert_equal(ds3.fields, ds_exp.fields) +# assert_equal(ds3.cases, ds_exp.cases) + +# assert_not_same(ds3.fields, ds_exp.fields) +# end + +# def test_dup +# v1 = [1, 2, 3, 4].to_vector +# v2 = [5, 6, 7, 8].to_vector +# ds1 = Statsample::Dataset.new({ 'v1' => v1, 'v2' => v2 }, %w(v2 v1)) +# ds2 = ds1.dup +# assert_equal(ds1, ds2) +# assert_not_same(ds1, ds2) +# assert_equal(ds1['v1'], ds2['v1']) +# assert_not_same(ds1['v1'], ds2['v1']) +# assert_equal(ds1.cases, ds2.cases) + +# assert_equal(ds1.fields, ds2.fields) +# assert_not_same(ds1.fields, ds2.fields) +# ds1['v1'].type = :numeric +# # dup partial +# ds3 = ds1.dup('v1') +# ds_exp = Statsample::Dataset.new({ 'v1' => v1 }, %w(v1)) +# assert_equal(ds_exp, ds3) +# assert_not_same(ds_exp, ds3) +# assert_equal(ds3['v1'], ds_exp['v1']) +# assert_not_same(ds3['v1'], ds_exp['v1']) +# assert_equal(ds3.fields, ds_exp.fields) +# assert_equal(ds3.cases, ds_exp.cases) + +# assert_not_same(ds3.fields, ds_exp.fields) + +# # empty +# ds3 = ds1.dup_empty +# assert_not_equal(ds1, ds3) +# assert_not_equal(ds1['v1'], ds3['v1']) +# assert_equal([], ds3['v1'].data) +# assert_equal([], ds3['v2'].data) +# assert_equal(:numeric, ds3['v1'].type) +# assert_equal(ds1.fields, ds2.fields) +# assert_not_same(ds1.fields, ds2.fields) +# end + +# def test_from_to +# assert_equal(%w(name age city), @ds.from_to('name', 'city')) +# assert_raise ArgumentError do +# @ds.from_to('name', 'a2') +# end +# end + +# def test_each_array_with_nils +# v1 = [1, -99, 3, 4, 'na'].to_vector(:numeric, missing_values: [-99, 'na']) +# v2 = [5, 6, -99, 8, 20].to_vector(:numeric, missing_values: [-99]) +# v3 = [9, 10, 11, 12, 20].to_vector(:numeric, missing_values: [-99]) +# ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) +# ds2 = ds1.dup_empty +# ds1.each_array_with_nils {|row| +# ds2.add_case_array(row) +# } +# ds2.update_valid_data +# assert_equal([1, nil, 3, 4, nil], ds2['v1'].data) +# assert_equal([5, 6, nil, 8, 20], ds2['v2'].data) +# end + +# def test_dup_only_valid +# v1 = [1, nil, 3, 4].to_vector(:numeric) +# v2 = [5, 6, nil, 8].to_vector(:numeric) +# v3 = [9, 10, 11, 12].to_vector(:numeric) +# ds1 = Statsample::Dataset.new('v1' => v1, 'v2' => v2, 'v3' => v3) +# ds2 = ds1.dup_only_valid +# expected = Statsample::Dataset.new('v1' => [1, 4].to_vector(:numeric), 'v2' => [5, 8].to_vector(:numeric), 'v3' => [9, 12].to_vector(:numeric)) +# assert_equal(expected, ds2) +# assert_equal(expected.vectors.values, Statsample.only_valid(v1, v2, v3)) +# expected_partial = Statsample::Dataset.new('v1' => [1, 3, 4].to_vector(:numeric), 'v3' => [9, 11, 12].to_vector(:numeric)) +# assert_equal(expected_partial, ds1.dup_only_valid(%w(v1 v3))) +# end + +# def test_filter +# @ds['age'].type = :numeric +# filtered = @ds.filter { |c| c['id'] == 2 or c['id'] == 4 } +# expected = Statsample::Dataset.new({ 'id' => Statsample::Vector.new([2, 4]), 'name' => Statsample::Vector.new(%w(Claude Franz)), 'age' => Statsample::Vector.new([23, 27], :numeric), +# 'city' => Statsample::Vector.new(%w(London Paris)), +# 'a1' => Statsample::Vector.new(['b,c', nil]) }, %w(id name age city a1)) +# assert_equal(expected, filtered) +# end + +# def test_filter_field +# @ds['age'].type = :numeric +# filtered = @ds.filter_field('id') { |c| c['id'] == 2 or c['id'] == 4 } +# expected = [2, 4].to_vector +# assert_equal(expected, filtered) +# end + +# def test_verify +# name = %w(r1 r2 r3 r4).to_vector(:object) +# v1 = [1, 2, 3, 4].to_vector(:numeric) +# v2 = [4, 3, 2, 1].to_vector(:numeric) +# v3 = [10, 20, 30, 40].to_vector(:numeric) +# v4 = %w(a b a b).to_vector(:object) +# ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3, 'v4' => v4, 'id' => name }.to_dataset +# ds.fields = %w(v1 v2 v3 v4 id) +# # Correct +# t1 = create_test('If v4=a, v1 odd') { |r| r['v4'] == 'b' or (r['v4'] == 'a' and r['v1'].odd?) } +# t2 = create_test('v3=v1*10') { |r| r['v3'] == r['v1'] * 10 } +# # Fail! +# t3 = create_test("v4='b'") { |r| r['v4'] == 'b' } +# exp1 = ["1 [1]: v4='b'", "3 [3]: v4='b'"] +# exp2 = ["1 [r1]: v4='b'", "3 [r3]: v4='b'"] +# res = ds.verify(t3, t1, t2) +# assert_equal(exp1, res) +# res = ds.verify('id', t1, t2, t3) +# assert_equal(exp2, res) +# end + +# def test_compute_operation +# v1 = [1, 2, 3, 4].to_vector(:numeric) +# v2 = [4, 3, 2, 1].to_vector(:numeric) +# v3 = [10, 20, 30, 40].to_vector(:numeric) +# vnumeric = [1.quo(2), 1, 3.quo(2), 2].to_vector(:numeric) +# vsum = [1 + 4 + 10.0, 2 + 3 + 20.0, 3 + 2 + 30.0, 4 + 1 + 40.0].to_vector(:numeric) +# vmult = [1 * 4, 2 * 3, 3 * 2, 4 * 1].to_vector(:numeric) +# ds = { 'v1' => v1, 'v2' => v2, 'v3' => v3 }.to_dataset +# assert_equal(vnumeric, ds.compute('v1/2')) +# assert_equal(vsum, ds.compute('v1+v2+v3')) +# assert_equal(vmult, ds.compute('v1*v2')) +# end + +# def test_crosstab_with_asignation +# v1 = %w(a a a b b b c c c).to_vector +# v2 = %w(a b c a b c a b c).to_vector +# v3 = %w(0 1 0 0 1 1 0 0 1).to_numeric +# ds = Statsample::Dataset.crosstab_by_asignation(v1, v2, v3) +# assert_equal(:object, ds['_id'].type) +# assert_equal(:numeric, ds['a'].type) +# assert_equal(:numeric, ds['b'].type) +# ev_id = %w(a b c).to_vector +# ev_a = %w(0 0 0).to_numeric +# ev_b = %w(1 1 0).to_numeric +# ev_c = %w(0 1 1).to_numeric +# ds2 = { '_id' => ev_id, 'a' => ev_a, 'b' => ev_b, 'c' => ev_c }.to_dataset +# assert_equal(ds, ds2) +# end + +# def test_one_to_many +# cases = [ +# ['1', 'george', 'red', 10, 'blue', 20, nil, nil], +# ['2', 'fred', 'green', 15, 'orange', 30, 'white', 20], +# ['3', 'alfred', nil, nil, nil, nil, nil, nil] +# ] +# ds = Statsample::Dataset.new(%w(id name car_color1 car_value1 car_color2 car_value2 car_color3 car_value3)) +# cases.each { |c| ds.add_case_array c } +# ds.update_valid_data +# ids = %w(1 1 2 2 2).to_vector +# colors = %w(red blue green orange white).to_vector +# values = [10, 20, 15, 30, 20].to_vector +# col_ids = [1, 2, 1, 2, 3].to_numeric +# ds_expected = { 'id' => ids, '_col_id' => col_ids, 'color' => colors, 'value' => values }.to_dataset(%w(id _col_id color value)) +# assert_equal(ds_expected, ds.one_to_many(%w(id), 'car_%v%n')) +# end +# end diff --git a/test/test_gsl.rb b/test/test_gsl.rb index a344523..261b9cf 100644 --- a/test/test_gsl.rb +++ b/test/test_gsl.rb @@ -1,10 +1,10 @@ require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleGSLTestCase < Minitest::Test should_with_gsl 'matrix with gsl' do - a = [1, 2, 3, 4, 20].to_vector(:numeric) - b = [3, 2, 3, 4, 50].to_vector(:numeric) - c = [6, 2, 3, 4, 3].to_vector(:numeric) - ds = { 'a' => a, 'b' => b, 'c' => c }.to_dataset + a = Daru::Vector.new([1, 2, 3, 4, 20]) + b = Daru::Vector.new([3, 2, 3, 4, 50]) + c = Daru::Vector.new([6, 2, 3, 4, 3]) + ds = Daru::DataFrame.new({ :a => a, :b => b, :c => c }) gsl = ds.to_matrix.to_gsl assert_equal(5, gsl.size1) assert_equal(3, gsl.size2) diff --git a/test/test_reliability.rb b/test/test_reliability.rb index 2b6c57b..5a803e6 100644 --- a/test/test_reliability.rb +++ b/test/test_reliability.rb @@ -1,6 +1,14 @@ require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleReliabilityTestCase < Minitest::Test context Statsample::Reliability do + setup do + Daru.lazy_update = true + end + + teardown do + Daru.lazy_update = false + end + should 'return correct r according to Spearman-Brown prophecy' do r = 0.6849 n = 62.quo(15) @@ -15,26 +23,27 @@ class StatsampleReliabilityTestCase < Minitest::Test setup do @samples = 40 @n_variables = rand(10) + 2 - @ds = Statsample::Dataset.new - base = @samples.times.collect { |_a| rand }.to_numeric + @ds = Daru::DataFrame.new({}, index: @samples) + base = Daru::Vector.new(@samples.times.collect { |_a| rand }) @n_variables.times do |i| @ds[i] = base.collect { |v| v + rand }.to_numeric end - @ds.update_valid_data - @k = @ds.fields.size + @ds.update + @k = @ds.ncols @cm = Statsample::Bivariate.covariance_matrix(@ds) @dse = @ds.dup - @dse.fields.each do |f| - @dse[f] = @dse[f].standarized + @dse.vectors.each do |f| + @dse[f] = @dse[f].standardize end + @dse.update @cme = Statsample::Bivariate.covariance_matrix(@dse) @a = Statsample::Reliability.cronbach_alpha(@ds) @as = Statsample::Reliability.cronbach_alpha_standarized(@ds) end should 'alpha will be equal to sum of matrix covariance less the individual variances' do total_sum = @cm.total_sum - ind_var = @ds.fields.inject(0) { |ac, v| ac + @ds[v].variance } + ind_var = @ds.vectors.to_a.inject(0) { |ac, v| ac + @ds[v].variance } expected = @k.quo(@k - 1) * (1 - (ind_var.quo(total_sum))) assert_in_delta(expected, @a, 1e-10) end @@ -57,7 +66,7 @@ class StatsampleReliabilityTestCase < Minitest::Test should 'standarized alpha will be equal to sum of matrix covariance less the individual variances on standarized values' do total_sum = @cme.total_sum - ind_var = @dse.fields.inject(0) { |ac, v| ac + @dse[v].variance } + ind_var = @dse.vectors.to_a.inject(0) { |ac, v| ac + @dse[v].variance } expected = @k.quo(@k - 1) * (1 - (ind_var.quo(total_sum))) assert_in_delta(expected, @as, 1e-10) end @@ -67,31 +76,31 @@ class StatsampleReliabilityTestCase < Minitest::Test @samples = 100 @points = rand(10) + 3 @max_point = (@points - 1) * 3 - @x1 = @samples.times.map { rand(@points) }.to_numeric - @x2 = @samples.times.map { rand(@points) }.to_numeric - @x3 = @samples.times.map { rand(@points) }.to_numeric - @ds = { 'a' => @x1, 'b' => @x2, 'c' => @x3 }.to_dataset + @x1 = Daru::Vector.new(@samples.times.map { rand(@points) }) + @x2 = Daru::Vector.new(@samples.times.map { rand(@points) }) + @x3 = Daru::Vector.new(@samples.times.map { rand(@points) }) + @ds = Daru::DataFrame.new({ :a => @x1, :b => @x2, :c => @x3 }) @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds) end should 'have a correct automatic vector_total' do assert_equal(@ds.vector_sum, @icc.vector_total) end should 'have a correct different vector_total' do - x2 = @samples.times.map { rand(10) }.to_numeric + x2 = Daru::Vector.new(@samples.times.map { rand(10) }) @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds, x2) assert_equal(x2, @icc.vector_total) assert_raises(ArgumentError) do - inc = (@samples + 10).times.map { rand(10) }.to_numeric + inc = Daru::Vector.new((@samples + 10).times.map { rand(10) }) @icc = Statsample::Reliability::ItemCharacteristicCurve.new(@ds, inc) end end should 'have 0% for 0 points on maximum value values' do - max = @icc.curve_field('a', 0)[@max_point.to_f] + max = @icc.curve_field(:a, 0)[@max_point.to_f] max ||= 0 assert_in_delta(0, max) end should 'have 0 for max value on minimum value' do - max = @icc.curve_field('a', @max_point)[0.0] + max = @icc.curve_field(:a, @max_point)[0.0] max ||= 0 assert_in_delta(0, max) end @@ -107,7 +116,7 @@ class StatsampleReliabilityTestCase < Minitest::Test expected = total.each {|k, v| total[k] = v.quo(total_g[k]) } - assert_equal(expected, @icc.curve_field('a', index)) + assert_equal(expected, @icc.curve_field(:a, index)) end end @@ -119,33 +128,34 @@ class StatsampleReliabilityTestCase < Minitest::Test h = {} @scales.times {|s| @items_per_scale.times {|i| - h["#{s}_#{i}"] = (size.times.map { (s * 2) + rand }).to_numeric + h["#{s}_#{i}".to_sym] = (size.times.map { (s * 2) + rand }).to_numeric } } - @ds = h.to_dataset + @ds = Daru::DataFrame.new(h) @msa = Statsample::Reliability::MultiScaleAnalysis.new(name: 'Multiple Analysis') do |m| m.scale 'complete', @ds @scales.times {|s| - m.scale "scale_#{s}", @ds.clone(@items_per_scale.times.map { |i| "#{s}_#{i}" }), name: "Scale #{s}" + m.scale "scale_#{s}", @ds.clone(*@items_per_scale.times.map { |i| "#{s}_#{i}".to_sym }), name: "Scale #{s}" } end end + should 'Retrieve correct ScaleAnalysis for whole scale' do sa = Statsample::Reliability::ScaleAnalysis.new(@ds, name: 'Scale complete') assert_equal(sa.variances_mean, @msa.scale('complete').variances_mean) end should 'Retrieve correct ScaleAnalysis for each scale' do @scales.times {|s| - sa = Statsample::Reliability::ScaleAnalysis.new(@ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}" }), name: "Scale #{s}") + sa = Statsample::Reliability::ScaleAnalysis.new(@ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}".to_sym }), name: "Scale #{s}") assert_equal(sa.variances_mean, @msa.scale("scale_#{s}").variances_mean) } end should 'retrieve correct correlation matrix for each scale' do - vectors = { 'complete' => @ds.vector_sum } + vectors = { :complete => @ds.vector_sum } @scales.times {|s| - vectors["scale_#{s}"] = @ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}" }).vector_sum + vectors["scale_#{s}".to_sym] = @ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}".to_sym }).vector_sum } - ds2 = vectors.to_dataset + ds2 = Daru::DataFrame.new(vectors) assert_equal(Statsample::Bivariate.correlation_matrix(ds2), @msa.correlation_matrix) end should 'delete scale using delete_scale' do @@ -156,9 +166,9 @@ class StatsampleReliabilityTestCase < Minitest::Test @msa.delete_scale('complete') vectors = {} @scales.times {|s| - vectors["scale_#{s}"] = @ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}" }).vector_sum + vectors["scale_#{s}".to_sym] = @ds.dup(@items_per_scale.times.map { |i| "#{s}_#{i}".to_sym }).vector_sum } - ds2 = vectors.to_dataset + ds2 = Daru::DataFrame.new(vectors) cor_matrix = Statsample::Bivariate.correlation_matrix(ds2) m = 3 pca = Statsample::Factor::PCA.new(cor_matrix, m: m) @@ -177,11 +187,11 @@ class StatsampleReliabilityTestCase < Minitest::Test end context Statsample::Reliability::ScaleAnalysis do setup do - @x1 = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 30].to_numeric - @x2 = [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 50].to_numeric - @x3 = [2, 2, 1, 1, 1, 2, 2, 2, 3, 4, 5, 40].to_numeric - @x4 = [1, 2, 3, 4, 4, 4, 4, 3, 4, 4, 5, 30].to_numeric - @ds = { 'x1' => @x1, 'x2' => @x2, 'x3' => @x3, 'x4' => @x4 }.to_dataset + @x1 = Daru::Vector.new([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 30]) + @x2 = Daru::Vector.new([1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 50]) + @x3 = Daru::Vector.new([2, 2, 1, 1, 1, 2, 2, 2, 3, 4, 5, 40]) + @x4 = Daru::Vector.new([1, 2, 3, 4, 4, 4, 4, 3, 4, 4, 5, 30]) + @ds = Daru::DataFrame.new({ :x1 => @x1, :x2 => @x2, :x3 => @x3, :x4 => @x4 }) @ia = Statsample::Reliability::ScaleAnalysis.new(@ds) @cov_matrix = @ia.cov_m end @@ -190,18 +200,18 @@ class StatsampleReliabilityTestCase < Minitest::Test assert_in_delta(0.999, @ia.alpha_standarized, 0.001) var_mean = 4.times.map { |m| @cov_matrix[m, m] }.to_numeric.mean assert_in_delta(var_mean, @ia.variances_mean) - assert_equal(@x1.mean, @ia.item_statistics['x1'][:mean]) - assert_equal(@x4.mean, @ia.item_statistics['x4'][:mean]) - assert_in_delta(@x1.sds, @ia.item_statistics['x1'][:sds], 1e-14) - assert_in_delta(@x4.sds, @ia.item_statistics['x4'][:sds], 1e-14) + assert_equal(@x1.mean, @ia.item_statistics[:x1][:mean]) + assert_equal(@x4.mean, @ia.item_statistics[:x4][:mean]) + assert_in_delta(@x1.sds, @ia.item_statistics[:x1][:sds], 1e-14) + assert_in_delta(@x4.sds, @ia.item_statistics[:x4][:sds], 1e-14) ds2 = @ds.clone - ds2.delete_vector('x1') + ds2.delete_vector(:x1) vector_sum = ds2.vector_sum - assert_equal(vector_sum.mean, @ia.stats_if_deleted['x1'][:mean]) - assert_equal(vector_sum.sds, @ia.stats_if_deleted['x1'][:sds]) - assert_in_delta(vector_sum.variance, @ia.stats_if_deleted['x1'][:variance_sample], 1e-10) + assert_equal(vector_sum.mean, @ia.stats_if_deleted[:x1][:mean]) + assert_equal(vector_sum.sds, @ia.stats_if_deleted[:x1][:sds]) + assert_in_delta(vector_sum.variance, @ia.stats_if_deleted[:x1][:variance_sample], 1e-10) - assert_equal(Statsample::Reliability.cronbach_alpha(ds2), @ia.stats_if_deleted['x1'][:alpha]) + assert_equal(Statsample::Reliability.cronbach_alpha(ds2), @ia.stats_if_deleted[:x1][:alpha]) covariances = [] 4.times.each {|i| @@ -212,8 +222,8 @@ class StatsampleReliabilityTestCase < Minitest::Test } } assert_in_delta(covariances.to_numeric.mean, @ia.covariances_mean) - assert_in_delta(0.999, @ia.item_total_correlation['x1'], 0.001) - assert_in_delta(1050.455, @ia.stats_if_deleted['x1'][:variance_sample], 0.001) + assert_in_delta(0.999, @ia.item_total_correlation[:x1], 0.001) + assert_in_delta(1050.455, @ia.stats_if_deleted[:x1][:variance_sample], 0.001) end should 'return a summary' do assert(@ia.summary.size > 0) diff --git a/test/test_reliability_skillscale.rb b/test/test_reliability_skillscale.rb index d7dd9a0..831740b 100644 --- a/test/test_reliability_skillscale.rb +++ b/test/test_reliability_skillscale.rb @@ -5,30 +5,32 @@ class StatsampleReliabilitySkillScaleTestCase < Minitest::Test setup do options = %w(a b c d e) cases = 20 - @id = cases.times.map { |v| v }.to_numeric - @a = cases.times.map { options[rand(5)] }.to_vector - @b = cases.times.map { options[rand(5)] }.to_vector - @c = cases.times.map { options[rand(5)] }.to_vector - @d = cases.times.map { options[rand(5)] }.to_vector - @e = cases.times.map {|i| - i == 0 ? options[rand(0)] : + @id = Daru::Vector.new(cases.times.map { |v| v }) + @a = Daru::Vector.new(cases.times.map { options[rand(5)] }) + @b = Daru::Vector.new(cases.times.map { options[rand(5)] }) + @c = Daru::Vector.new(cases.times.map { options[rand(5)] }) + @d = Daru::Vector.new(cases.times.map { options[rand(5)] }) + @e = Daru::Vector.new( + cases.times.map do |i| + i == 0 ? options[rand(0)] : rand > 0.8 ? nil : options[rand(5)] - }.to_vector - @ds = { 'id' => @id, 'a' => @a, 'b' => @b, 'c' => @c, 'd' => @d, 'e' => @e }.to_dataset - @key = { 'a' => 'a', 'b' => options[rand(5)], 'c' => options[rand(5)], 'd' => options[rand(5)], 'e' => options[rand(5)] } + end + ) + @ds = Daru::DataFrame.new({ :id => @id, :a => @a, :b => @b, :c => @c, :d => @d, :e => @e }) + @key = { :a => 'a', :b => options[rand(5)], :c => options[rand(5)], :d => options[rand(5)], :e => options[rand(5)] } @ssa = Statsample::Reliability::SkillScaleAnalysis.new(@ds, @key) - @ac = @a.map { |v| v == @key['a'] ? 1 : 0 }.to_numeric - @bc = @b.map { |v| v == @key['b'] ? 1 : 0 }.to_numeric - @cc = @c.map { |v| v == @key['c'] ? 1 : 0 }.to_numeric - @dc = @d.map { |v| v == @key['d'] ? 1 : 0 }.to_numeric - @ec = @e.map { |v| v.nil? ? nil : (v == @key['e'] ? 1 : 0) }.to_numeric + @ac = Daru::Vector.new(@a.map { |v| v == @key[:a] ? 1 : 0 }) + @bc = Daru::Vector.new(@b.map { |v| v == @key[:b] ? 1 : 0 }) + @cc = Daru::Vector.new(@c.map { |v| v == @key[:c] ? 1 : 0 }) + @dc = Daru::Vector.new(@d.map { |v| v == @key[:d] ? 1 : 0 }) + @ec = Daru::Vector.new(@e.map { |v| v.nil? ? nil : (v == @key[:e] ? 1 : 0) }) end should 'return proper corrected dataset' do - cds = { 'id' => @id, 'a' => @ac, 'b' => @bc, 'c' => @cc, 'd' => @dc, 'e' => @ec }.to_dataset + cds = Daru::DataFrame.new({ :id => @id, :a => @ac, :b => @bc, :c => @cc, :d => @dc, :e => @ec }) assert_equal(cds, @ssa.corrected_dataset) end should 'return proper corrected minimal dataset' do - cdsm = { 'a' => @ac, 'b' => @bc, 'c' => @cc, 'd' => @dc, 'e' => @ec }.to_dataset + cdsm = Daru::DataFrame.new({ :a => @ac, :b => @bc, :c => @cc, :d => @dc, :e => @ec }) assert_equal(cdsm, @ssa.corrected_dataset_minimal) end should 'return correct vector_sum and vector_sum' do @@ -37,13 +39,13 @@ class StatsampleReliabilitySkillScaleTestCase < Minitest::Test assert_equal(cdsm.vector_mean, @ssa.vector_mean) end should 'not crash on rare case' do - a = Statsample::Vector['c', 'c', 'a', 'a', 'c', 'a', 'b', 'c', 'c', 'b', 'a', 'd', 'a', 'd', 'a', 'a', 'd', 'e', 'c', 'd'] - b = Statsample::Vector['e', 'b', 'e', 'b', 'c', 'd', 'a', 'e', 'e', 'c', 'b', 'e', 'e', 'b', 'd', 'c', 'e', 'b', 'b', 'd'] - c = Statsample::Vector['e', 'b', 'e', 'c', 'e', 'c', 'b', 'd', 'e', 'c', 'a', 'a', 'b', 'd', 'e', 'c', 'b', 'a', 'a', 'e'] - d = Statsample::Vector['a', 'b', 'd', 'd', 'e', 'b', 'e', 'b', 'd', 'c', 'e', 'a', 'c', 'd', 'c', 'c', 'e', 'd', 'd', 'b'] - e = Statsample::Vector['a', 'b', nil, 'd', 'c', 'c', 'd', nil, 'd', 'd', 'e', 'e', nil, nil, nil, 'd', 'c', nil, 'e', 'd'] - key = { 'a' => 'a', 'b' => 'e', 'c' => 'd', 'd' => 'c', 'e' => 'd' } - ds = Statsample::Dataset.new('a' => a, 'b' => b, 'c' => c, 'd' => d, 'e' => e) + a = Daru::Vector.new(['c', 'c', 'a', 'a', 'c', 'a', 'b', 'c', 'c', 'b', 'a', 'd', 'a', 'd', 'a', 'a', 'd', 'e', 'c', 'd']) + b = Daru::Vector.new(['e', 'b', 'e', 'b', 'c', 'd', 'a', 'e', 'e', 'c', 'b', 'e', 'e', 'b', 'd', 'c', 'e', 'b', 'b', 'd']) + c = Daru::Vector.new(['e', 'b', 'e', 'c', 'e', 'c', 'b', 'd', 'e', 'c', 'a', 'a', 'b', 'd', 'e', 'c', 'b', 'a', 'a', 'e']) + d = Daru::Vector.new(['a', 'b', 'd', 'd', 'e', 'b', 'e', 'b', 'd', 'c', 'e', 'a', 'c', 'd', 'c', 'c', 'e', 'd', 'd', 'b']) + e = Daru::Vector.new(['a', 'b', nil, 'd', 'c', 'c', 'd', nil, 'd', 'd', 'e', 'e', nil, nil, nil, 'd', 'c', nil, 'e', 'd']) + key = { :a => 'a', :b => 'e', :c => 'd', :d => 'c', :e => 'd' } + ds = Daru::DataFrame.new({:a => a, :b => b, :c => c, :d => d, :e => e}) ssa = Statsample::Reliability::SkillScaleAnalysis.new(ds, key) assert(ssa.summary) end diff --git a/test/test_vector.rb b/test/test_vector.rb index fcf6e98..0f6977b 100644 --- a/test/test_vector.rb +++ b/test/test_vector.rb @@ -1,687 +1,687 @@ -require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) - -class StatsampleTestVector < Minitest::Test - include Statsample::Shorthand - - def setup - @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :object) - @c.name = 'Test Vector' - @c.missing_values = [-99] - end - - def assert_counting_tokens(b) - assert_equal([1, 1, 0, 1, 0, nil], b['a'].to_a) - assert_equal([0, 1, 0, 0, 0, nil], b['b'].to_a) - assert_equal([0, 0, 1, 0, 0, nil], b['c'].to_a) - assert_equal([0, 0, 1, 1, 0, nil], b['d'].to_a) - assert_equal([0, 0, 0, 0, 1, nil], b[10].to_a) - end - context Statsample do - setup do - @sample = 100 - @a = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_numeric - @b = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_numeric - @correct_a = [] - @correct_b = [] - @a.each_with_index do |_v, i| - if !@a[i].nil? and !@b[i].nil? - @correct_a.push(@a[i]) - @correct_b.push(@b[i]) - end - end - @correct_a = @correct_a.to_numeric - @correct_b = @correct_b.to_numeric - - @common = lambda do |av, bv| - assert_equal(@correct_a, av, 'A no es esperado') - assert_equal(@correct_b, bv, 'B no es esperado') - assert(!av.has_missing_data?, 'A tiene datos faltantes') - assert(!bv.has_missing_data?, 'b tiene datos faltantes') - end - end - should 'return correct only_valid' do - av, bv = Statsample.only_valid @a, @b - av2, bv2 = Statsample.only_valid av, bv - @common.call(av, bv) - assert_equal(av, av2) - assert_not_same(av, av2) - assert_not_same(bv, bv2) - end - should 'return correct only_valid_clone' do - av, bv = Statsample.only_valid_clone @a, @b - @common.call(av, bv) - av2, bv2 = Statsample.only_valid_clone av, bv - assert_equal(av, av2) - assert_same(av, av2) - assert_same(bv, bv2) - end - end - context Statsample::Vector do - setup do - @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :object) - @c.name = 'Test Vector' - @c.missing_values = [-99] - end - should_with_gsl 'be created with GSL::Vector' do - gsl = GSL::Vector[1, 2, 3, 4, 5] - v = Statsample::Vector.new(gsl) - assert_equal([1, 2, 3, 4, 5], v.to_a) - refute(v.flawed?) - end - - context 'using matrix operations' do - setup do - @a = [1, 2, 3, 4, 5].to_numeric - end - should 'to_matrix returns a matrix with 1 row' do - mh = Matrix[[1, 2, 3, 4, 5]] - assert_equal(mh, @a.to_matrix) - end - should 'to_matrix(:vertical) returns a matrix with 1 column' do - mv = Matrix.columns([[1, 2, 3, 4, 5]]) - assert_equal(mv, @a.to_matrix(:vertical)) - end - should 'returns valid submatrixes' do - # 3*4 + 2*5 = 22 - a = [3, 2].to_vector(:numeric) - b = [4, 5].to_vector(:numeric) - assert_equal(22, (a.to_matrix * b.to_matrix(:vertical))[0, 0]) - end - end - context 'when initializing' do - setup do - @data = (10.times.map { rand(100) }) + [nil] - @original = Statsample::Vector.new(@data, :numeric) - end - should 'be the sample using []' do - second = Statsample::Vector[*@data] - assert_equal(@original, second) - end - should '[] returns same results as R-c()' do - reference = [0, 4, 5, 6, 10].to_numeric - assert_equal(reference, Statsample::Vector[0, 4, 5, 6, 10]) - assert_equal(reference, Statsample::Vector[0, 4..6, 10]) - assert_equal(reference, Statsample::Vector[[0], [4, 5, 6], [10]]) - assert_equal(reference, Statsample::Vector[[0], [4, [5, [6]]], [10]]) - - assert_equal(reference, Statsample::Vector[[0], [4, 5, 6].to_vector, [10]]) - end - should 'be the same usign #to_vector' do - lazy1 = @data.to_vector(:numeric) - assert_equal(@original, lazy1) - end - should 'be the same using #to_numeric' do - lazy2 = @data.to_numeric - assert_equal(@original, lazy2) - assert_equal(:numeric, lazy2.type) - assert_equal(@data.find_all { |v| !v.nil? }, lazy2.valid_data) - end - should 'could use new_numeric with size only' do - v1 = 10.times.map { nil }.to_numeric - v2 = Statsample::Vector.new_numeric(10) - assert_equal(v1, v2) - end - should 'could use new_numeric with size and value' do - a = rand - v1 = 10.times.map { a }.to_numeric - v2 = Statsample::Vector.new_numeric(10, a) - assert_equal(v1, v2) - end - should 'could use new_numeric with func' do - v1 = 10.times.map { |i| i * 2 }.to_numeric - v2 = Statsample::Vector.new_numeric(10) { |i| i * 2 } - assert_equal(v1, v2) - end - end - - context "new types :numeric and :object" do - should "set default type of vector to :object" do - v = Statsample::Vector.new [1,2,3,4,5] - assert_equal(:object, v.type) - end - - should "initialize Vector with :numeric type" do - v = Statsample::Vector.new [1,2,3,4,5,nil], :numeric - assert_equal(:numeric, v.type) - assert_equal([1,2,3,4,5], v.valid_data) - end - - should "show a warning when initializing with :nominal, :numeric or :ordinal" do - assert_output(nil,"WARNING: nominal has been deprecated. Use :object instead.\n") do - Statsample::Vector.new [1,2,3,4,5,nil,'hello'], :nominal - end - - assert_output(nil,"WARNING: scale has been deprecated. Use :numeric instead.\n") do - Statsample::Vector.new [1,2,3,4,nil,5], :scale - end - - assert_output(nil,"WARNING: ordinal has been deprecated. Use :numeric instead.\n") do - Statsample::Vector.new [1,2,3,4,5], :ordinal - end - - assert_output(nil,"WARNING: .new_scale has been deprecated. Use .new_numeric instead.\n") do - Statsample::Vector.new_scale 10, 1 - end - end - - should "test that new shorthands work" do - numeric = Statsample::Vector.new([1,2,3,4,nil,5], :numeric) - assert_equal(numeric, [1,2,3,4,nil,5].to_numeric) - assert_equal(numeric, [1,2,3,4,nil,5].to_vector(:numeric)) - - obj = Statsample::Vector.new([1,2,3,4,'one','two'], :object) - assert_equal(obj, [1,2,3,4,'one','two'].to_vector(:object)) - end - - should "test that old shorthands raise warnings" do - assert_output(nil,"WARNING: to_scale has been deprecated. Use to_numeric instead.\n") do - [1,2,3,4,nil,5].to_scale - end - end - end - - context '#split_by_separator' do - setup do - @a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 10, nil], :object) - @b = @a.split_by_separator(',') - end - should 'returns a Hash' do - assert_kind_of(Hash, @b) - end - should 'return a Hash with keys with different values of @a' do - expected = ['a', 'b', 'c', 'd', 10] - assert_equal(expected, @b.keys) - end - - should 'returns a Hash, which values are Statsample::Vector' do - @b.each_key { |k| assert_instance_of(Statsample::Vector, @b[k]) } - end - should 'hash values are n times the tokens appears' do - assert_counting_tokens(@b) - end - should '#split_by_separator_freq returns the number of ocurrences of tokens' do - assert_equal({ 'a' => 3, 'b' => 1, 'c' => 1, 'd' => 2, 10 => 1 }, @a.split_by_separator_freq) - end - should 'using a different separator give the same values' do - a = Statsample::Vector.new(['a', 'a*b', 'c*d', 'a*d', 10, nil], :object) - b = a.split_by_separator('*') - assert_counting_tokens(b) - end - end - should 'return correct median_absolute_deviation' do - a = [1, 1, 2, 2, 4, 6, 9].to_numeric - assert_equal(1, a.median_absolute_deviation) - end - should 'return correct histogram' do - a = 10.times.map { |v| v }.to_numeric - hist = a.histogram(2) - assert_equal([5, 5], hist.bin) - 3.times do |i| - assert_in_delta(i * 4.5, hist.get_range(i)[0], 1e-9) - end - end - should 'have a name' do - @c.name == 'Test Vector' - end - should 'without explicit name, returns vector with succesive numbers' do - a = 10.times.map { rand(100) }.to_numeric - b = 10.times.map { rand(100) }.to_numeric - assert_match(/Vector \d+/, a.name) - a.name =~ /Vector (\d+)/ - next_number = Regexp.last_match(1).to_i + 1 - assert_equal("Vector #{next_number}", b.name) - end - should 'save to a file and load the same Vector' do - outfile = Tempfile.new('vector.vec') - @c.save(outfile.path) - a = Statsample.load(outfile.path) - assert_equal(@c, a) - end - should '#collect returns an array' do - val = @c.collect { |v| v } - assert_equal(val, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]) - end - - should '#recode returns a recoded array' do - a = @c.recode { |v| @c.is_valid?(v) ? 0 : 1 } - exp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1].to_vector - assert_equal(exp, a) - exp.recode! { |v| v == 0 ? 1 : 0 } - exp2 = (([1] * 15) + ([0] * 3)).to_vector - assert_equal(exp2, exp) - end - should '#product returns the * of all values' do - a = [1, 2, 3, 4, 5].to_vector(:numeric) - assert_equal(120, a.product) - end - - should 'missing values' do - @c.missing_values = [10] - assert_equal([-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9], @c.valid_data.sort) - assert_equal([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, nil, 1, 2, 3, 4, nil, -99, -99], @c.data_with_nils) - @c.missing_values = [-99] - assert_equal(@c.valid_data.sort, [1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]) - assert_equal(@c.data_with_nils, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, nil, nil]) - @c.missing_values = [] - assert_equal(@c.valid_data.sort, [-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]) - assert_equal(@c.data_with_nils, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]) - end - should 'correct has_missing_data? with missing data' do - a = [1, 2, 3, nil].to_vector - assert(a.has_missing_data?) - end - should 'correct has_missing_data? without missing data' do - a = [1, 2, 3, 4, 10].to_vector - assert(!a.has_missing_data?) - end - should 'with explicit missing_values, should respond has_missing_data?' do - a = [1, 2, 3, 4, 10].to_vector - a.missing_values = [10] - assert(a.has_missing_data?) - end - should 'label correctly fields' do - @c.labels = { 5 => 'FIVE' } - assert_equal(['FIVE', 'FIVE', 'FIVE', 'FIVE', 'FIVE', 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], @c.vector_labeled.to_a) - end - should 'verify' do - h = @c.verify { |d| !d.nil? and d > 0 } - e = { 15 => nil, 16 => -99, 17 => -99 } - assert_equal(e, h) - end - should 'have a summary with name on it' do - assert_match(/#{@c.name}/, @c.summary) - end - - should 'GSL::Vector based should push correcty' do - if Statsample.has_gsl? - v = GSL::Vector[1, 2, 3, 4, 5].to_numeric - v.push(nil) - assert_equal([1, 2, 3, 4, 5, nil], v.to_a) - assert(v.flawed?) - else - skip('Requires GSL') - end - end - - should 'split correctly' do - a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 'd', 10, nil], :object) - assert_equal([%w(a), %w(a b), %w(c d), %w(a d), %w(d), [10], nil], a.splitted) - end - should 'multiply correct for scalar' do - a = [1, 2, 3].to_numeric - assert_equal([5, 10, 15].to_numeric, a * 5) - end - should 'multiply correct with other vector' do - a = [1, 2, 3].to_numeric - b = [2, 4, 6].to_numeric - - assert_equal([2, 8, 18].to_numeric, a * b) - end - should 'sum correct for scalar' do - a = [1, 2, 3].to_numeric - assert_equal([11, 12, 13].to_numeric, a + 10) - end - - should 'raise NoMethodError when method requires numeric and vector is object' do - @c.type = :object - assert_raise(::NoMethodError) { @c.median } - end - - should 'jacknife correctly with named method' do - # First example - a = [1, 2, 3, 4].to_numeric - ds = a.jacknife(:mean) - assert_equal(a.mean, ds[:mean].mean) - ds = a.jacknife([:mean, :sd]) - assert_equal(a.mean, ds[:mean].mean) - assert_equal(a.sd, ds[:mean].sd) - end - should 'jacknife correctly with custom method' do - # Second example - a = [17.23, 18.71, 13.93, 18.81, 15.78, 11.29, 14.91, 13.39, 18.21, 11.57, 14.28, 10.94, 18.83, 15.52, 13.45, 15.25].to_numeric - ds = a.jacknife(log_s2: ->(v) { Math.log(v.variance) }) - exp = [1.605, 2.972, 1.151, 3.097, 0.998, 3.308, 0.942, 1.393, 2.416, 2.951, 1.043, 3.806, 3.122, 0.958, 1.362, 0.937].to_numeric - - assert_similar_vector(exp, ds[:log_s2], 0.001) - assert_in_delta(2.00389, ds[:log_s2].mean, 0.00001) - assert_in_delta(1.091, ds[:log_s2].variance, 0.001) - end - should 'jacknife correctly with k>1' do - a = rnorm(6) - ds = a.jacknife(:mean, 2) - mean = a.mean - exp = [3 * mean - 2 * (a[2] + a[3] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[2] + a[3]) / 4].to_numeric - assert_similar_vector(exp, ds[:mean], 1e-13) - end - should 'bootstrap should return a vector with mean=mu and sd=se' do - a = rnorm(100) - ds = a.bootstrap([:mean, :sd], 200) - se = 1 / Math.sqrt(a.size) - assert_in_delta(0, ds[:mean].mean, 0.3) - assert_in_delta(se, ds[:mean].sd, 0.02) - end - end - - def test_object - assert_equal(@c[1], 5) - assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c.frequencies) - assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c._frequencies) - assert_equal({ 1 => 1.quo(15), 2 => 1.quo(15), 3 => 1.quo(15), 4 => 1.quo(15), 5 => 5.quo(15), 6 => 2.quo(15), 7 => 1.quo(15), 8 => 1.quo(15), 9 => 1.quo(15), 10 => 1.quo(15) }, @c.proportions) - assert_equal(@c.proportion, 1.quo(15)) - assert_equal(@c.proportion(2), 1.quo(15)) - assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], @c.factors.sort) - assert_equal(@c.mode, 5) - assert_equal(@c.n_valid, 15) - end - - def test_equality - v1 = [1, 2, 3].to_vector - v2 = [1, 2, 3].to_vector - assert_equal(v1, v2) - v1 = [1, 2, 3].to_vector(:object) - v2 = [1, 2, 3].to_vector(:numeric) - assert_not_equal(v1, v2) - v2 = [1, 2, 3] - assert_not_equal(v1, v2) - v1 = [1, 2, 3].to_vector - v2 = [1, 2, 3].to_vector - assert_equal(v1, v2) - assert_equal(false, v1 == Object.new) - end - - def test_vector_percentil - a = [1, 2, 2, 3, 4, 5, 5, 5, 6, 10].to_numeric - expected = [10, 25, 25, 40, 50, 70, 70, 70, 90, 100].to_numeric - assert_equal(expected, a.vector_percentil) - a = [1, nil, nil, 2, 2, 3, 4, nil, nil, 5, 5, 5, 6, 10].to_numeric - expected = [10, nil, nil, 25, 25, 40, 50, nil, nil, 70, 70, 70, 90, 100].to_numeric - assert_equal(expected, a.vector_percentil) - end - - def test_numeric - @c.type = :numeric - assert_equal(5, @c.median) - assert_equal(4, @c.percentil(25)) - assert_equal(7, @c.percentil(75)) - - v = [200_000, 200_000, 210_000, 220_000, 230_000, 250_000, 250_000, 250_000, 270_000, 300_000, 450_000, 130_000, 140_000, 140_000, 140_000, 145_000, 148_000, 165_000, 170_000, 180_000, 180_000, 180_000, 180_000, 180_000, 180_000].to_numeric - assert_equal(180_000, v.median) - a = [7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 9.0, 9.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 12.0, 13.0, 14.0, 14.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0].to_numeric - assert_equal(4.5, a.percentil(25)) - assert_equal(6.5, a.percentil(50)) - assert_equal(9.5, a.percentil(75)) - assert_equal(3.0, a.percentil(10)) - end - - def test_linear_percentil_strategy - values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116].shuffle.to_numeric - assert_equal 102, values.percentil(0, :linear) - assert_equal 104.75, values.percentil(25, :linear) - assert_equal 108.5, values.percentil(50, :linear) - assert_equal 112.75, values.percentil(75, :linear) - assert_equal 116, values.percentil(100, :linear) - - values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116, 118].shuffle.to_numeric - assert_equal 102, values.percentil(0, :linear) - assert_equal 105, values.percentil(25, :linear) - assert_equal 109, values.percentil(50, :linear) - assert_equal 115, values.percentil(75, :linear) - assert_equal 118, values.percentil(100, :linear) - end - - def test_ranked - v1 = [0.8, 1.2, 1.2, 2.3, 18].to_vector(:numeric) - expected = [1, 2.5, 2.5, 4, 5].to_vector(:numeric) - assert_equal(expected, v1.ranked) - v1 = [nil, 0.8, 1.2, 1.2, 2.3, 18, nil].to_vector(:numeric) - expected = [nil, 1, 2.5, 2.5, 4, 5, nil].to_vector(:numeric) - assert_equal(expected, v1.ranked) - end - - def test_numeric - a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :numeric) - assert_equal(10, a.sum) - i = 0 - factors = a.factors.sort - [0, 1, 2, 3, 4].each{|v| - assert(v == factors[i]) - assert(v.class == factors[i].class, "#{v} - #{v.class} != #{factors[i]} - #{factors[i].class}") - i += 1 - } - end - - def test_vector_centered - mean = rand - samples = 11 - centered = samples.times.map { |i| i - ((samples / 2).floor).to_i }.to_numeric - not_centered = centered.recode { |v| v + mean } - obs = not_centered.centered - centered.each_with_index do |v, i| - assert_in_delta(v, obs[i], 0.0001) - end - end - - def test_vector_standarized - v1 = [1, 2, 3, 4, nil].to_vector(:numeric) - sds = v1.sds - expected = [((1 - 2.5).quo(sds)), ((2 - 2.5).quo(sds)), ((3 - 2.5).quo(sds)), ((4 - 2.5).quo(sds)), nil].to_vector(:numeric) - vs = v1.vector_standarized - assert_equal(expected, vs) - assert_equal(0, vs.mean) - assert_equal(1, vs.sds) - end - - def test_vector_standarized_with_zero_variance - v1 = 100.times.map { |_i| 1 }.to_numeric - exp = 100.times.map { nil }.to_numeric - assert_equal(exp, v1.standarized) - end - - def test_check_type - v = Statsample::Vector.new - v.type = :object - assert_raise(NoMethodError) { v.check_type(:numeric) } - assert(v.check_type(:object).nil?) - - v.type = :numeric - - assert(v.check_type(:numeric).nil?) - assert(v.check_type(:object).nil?) - - v.type = :date - assert_raise(NoMethodError) { v.check_type(:numeric) } - assert_raise(NoMethodError) { v.check_type(:numeric) } - assert_raise(NoMethodError) { v.check_type(:object) } -end - - def test_add - a = Statsample::Vector.new([1, 2, 3, 4, 5], :numeric) - b = Statsample::Vector.new([11, 12, 13, 14, 15], :numeric) - assert_equal([3, 4, 5, 6, 7], (a + 2).to_a) - assert_equal([12, 14, 16, 18, 20], (a + b).to_a) - assert_raise ArgumentError do - a + @c - end - assert_raise TypeError do - a + 'string' - end - a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :numeric) - b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :numeric) - assert_equal([nil, 13, nil, 16, 18, 20], (a + b).to_a) - assert_equal([nil, 13, nil, 16, 18, 20], (a + b.to_a).to_a) - end - - def test_minus - a = Statsample::Vector.new([1, 2, 3, 4, 5], :numeric) - b = Statsample::Vector.new([11, 12, 13, 14, 15], :numeric) - assert_equal([-1, 0, 1, 2, 3], (a - 2).to_a) - assert_equal([10, 10, 10, 10, 10], (b - a).to_a) - assert_raise ArgumentError do - a - @c - end - assert_raise TypeError do - a - 'string' - end - a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :numeric) - b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :numeric) - assert_equal([nil, 11, nil, 10, 10, 10], (b - a).to_a) - assert_equal([nil, 11, nil, 10, 10, 10], (b - a.to_a).to_a) - end - - def test_sum_of_squares - a = [1, 2, 3, 4, 5, 6].to_vector(:numeric) - assert_equal(17.5, a.sum_of_squared_deviation) - end - - def test_average_deviation - a = [1, 2, 3, 4, 5, 6, 7, 8, 9].to_numeric - assert_equal(20.quo(9), a.average_deviation_population) - end - - def test_samples - srand(1) - assert_equal(100, @c.sample_with_replacement(100).size) - assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) - assert_raise ArgumentError do - @c.sample_without_replacement(20) - end - @c.type = :numeric - srand(1) - assert_equal(100, @c.sample_with_replacement(100).size) - assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) - end - - def test_valid_data - a = Statsample::Vector.new([1, 2, 3, 4, 'STRING']) - a.missing_values = [-99] - a.add(1, false) - a.add(2, false) - a.add(-99, false) - a.set_valid_data - exp_valid_data = [1, 2, 3, 4, 'STRING', 1, 2] - assert_equal(exp_valid_data, a.valid_data) - a.add(20, false) - a.add(30, false) - assert_equal(exp_valid_data, a.valid_data) - a.set_valid_data - exp_valid_data_2 = [1, 2, 3, 4, 'STRING', 1, 2, 20, 30] - assert_equal(exp_valid_data_2, a.valid_data) - end - - def test_set_value - @c[2] = 10 - expected = [5, 5, 10, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99].to_vector - assert_equal(expected.data, @c.data) - end - - def test_gsl - if Statsample.has_gsl? - a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :numeric) - - assert_equal(2, a.mean) - assert_equal(a.variance_sample_ruby, a.variance_sample) - assert_equal(a.standard_deviation_sample_ruby, a.sds) - assert_equal(a.variance_population_ruby, a.variance_population) - assert_equal(a.standard_deviation_population_ruby, a.standard_deviation_population) - assert_nothing_raised do - a = [].to_vector(:numeric) - end - a.add(1, false) - a.add(2, false) - a.set_valid_data - assert_equal(3, a.sum) - b = [1, 2, nil, 3, 4, 5, nil, 6].to_vector(:numeric) - assert_equal(21, b.sum) - assert_equal(3.5, b.mean) - assert_equal(6, b.gsl.size) - c = [10, 20, 30, 40, 50, 100, 1000, 2000, 5000].to_numeric - assert_in_delta(c.skew, c.skew_ruby, 0.0001) - assert_in_delta(c.kurtosis, c.kurtosis_ruby, 0.0001) - end - end - - def test_vector_matrix - v1 = %w(a a a b b b c c).to_vector - v2 = %w(1 3 4 5 6 4 3 2).to_vector - v3 = %w(1 0 0 0 1 1 1 0).to_vector - ex = Matrix.rows([%w(a 1 1), %w(a 3 0), %w(a 4 0), %w(b 5 0), %w(b 6 1), %w(b 4 1), %w(c 3 1), %w(c 2 0)]) - assert_equal(ex, Statsample.vector_cols_matrix(v1, v2, v3)) - end - - def test_marshalling - v1 = (0..100).to_a.collect { |_n| rand(100) }.to_vector(:numeric) - v2 = Marshal.load(Marshal.dump(v1)) - assert_equal(v1, v2) - end - - def test_dup - v1 = %w(a a a b b b c c).to_vector - v2 = v1.dup - assert_equal(v1.data, v2.data) - assert_not_same(v1.data, v2.data) - assert_equal(v1.type, v2.type) - - v1.type = :numeric - assert_not_equal(v1.type, v2.type) - assert_equal(v1.missing_values, v2.missing_values) - assert_not_same(v1.missing_values, v2.missing_values) - assert_equal(v1.labels, v2.labels) - assert_not_same(v1.labels, v2.labels) - - v3 = v1.dup_empty - assert_equal([], v3.data) - assert_not_equal(v1.data, v3.data) - assert_not_same(v1.data, v3.data) - assert_equal(v1.type, v3.type) - v1.type = :numeric - v3.type = :object - assert_not_equal(v1.type, v3.type) - assert_equal(v1.missing_values, v3.missing_values) - assert_not_same(v1.missing_values, v3.missing_values) - assert_equal(v1.labels, v3.labels) - assert_not_same(v1.labels, v3.labels) - end - - def test_paired_ties - a = [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 4].to_vector(:numeric) - expected = [2, 2, 2, 4.5, 4.5, 6, 7.5, 7.5, 10, 10, 10].to_vector(:numeric) - assert_equal(expected, a.ranked) - end - - def test_dichotomize - a = [0, 0, 0, 1, 2, 3, nil].to_vector - exp = [0, 0, 0, 1, 1, 1, nil].to_numeric - assert_equal(exp, a.dichotomize) - a = [1, 1, 1, 2, 2, 2, 3].to_vector - exp = [0, 0, 0, 1, 1, 1, 1].to_numeric - assert_equal(exp, a.dichotomize) - a = [0, 0, 0, 1, 2, 3, nil].to_vector - exp = [0, 0, 0, 0, 1, 1, nil].to_numeric - assert_equal(exp, a.dichotomize(1)) - a = %w(a a a b c d).to_vector - exp = [0, 0, 0, 1, 1, 1].to_numeric - assert_equal(exp, a.dichotomize) - end - - def test_can_be_methods - a = [0, 0, 0, 1, 2, 3, nil].to_vector - assert(a.can_be_numeric?) - a = [0, 's', 0, 1, 2, 3, nil].to_vector - assert(!a.can_be_numeric?) - a.missing_values = ['s'] - assert(a.can_be_numeric?) - - a = [Date.new(2009, 10, 10), Date.today, '2009-10-10', '2009-1-1', nil, 'NOW'].to_vector - assert(a.can_be_date?) - a = [Date.new(2009, 10, 10), Date.today, nil, 'sss'].to_vector - assert(!a.can_be_date?) - end - - def test_date_vector - a = [Date.new(2009, 10, 10), :NOW, '2009-10-10', '2009-1-1', nil, 'NOW', 'MISSING'].to_vector(:date, missing_values: ['MISSING']) - - assert(a.type == :date) - expected = [Date.new(2009, 10, 10), Date.today, Date.new(2009, 10, 10), Date.new(2009, 1, 1), nil, Date.today, nil] - assert_equal(expected, a.date_data_with_nils) - end -end +# require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) + +# class StatsampleTestVector < Minitest::Test +# include Statsample::Shorthand + +# def setup +# @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :object) +# @c.name = 'Test Vector' +# @c.missing_values = [-99] +# end + +# def assert_counting_tokens(b) +# assert_equal([1, 1, 0, 1, 0, nil], b['a'].to_a) +# assert_equal([0, 1, 0, 0, 0, nil], b['b'].to_a) +# assert_equal([0, 0, 1, 0, 0, nil], b['c'].to_a) +# assert_equal([0, 0, 1, 1, 0, nil], b['d'].to_a) +# assert_equal([0, 0, 0, 0, 1, nil], b[10].to_a) +# end +# context Statsample do +# setup do +# @sample = 100 +# @a = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_numeric +# @b = @sample.times.map { |i| (i + rand(10)) % 10 == 0 ? nil : rand(100) }.to_numeric +# @correct_a = [] +# @correct_b = [] +# @a.each_with_index do |_v, i| +# if !@a[i].nil? and !@b[i].nil? +# @correct_a.push(@a[i]) +# @correct_b.push(@b[i]) +# end +# end +# @correct_a = @correct_a.to_numeric +# @correct_b = @correct_b.to_numeric + +# @common = lambda do |av, bv| +# assert_equal(@correct_a, av, 'A no es esperado') +# assert_equal(@correct_b, bv, 'B no es esperado') +# assert(!av.has_missing_data?, 'A tiene datos faltantes') +# assert(!bv.has_missing_data?, 'b tiene datos faltantes') +# end +# end +# should 'return correct only_valid' do +# av, bv = Statsample.only_valid @a, @b +# av2, bv2 = Statsample.only_valid av, bv +# @common.call(av, bv) +# assert_equal(av, av2) +# assert_not_same(av, av2) +# assert_not_same(bv, bv2) +# end +# should 'return correct only_valid_clone' do +# av, bv = Statsample.only_valid_clone @a, @b +# @common.call(av, bv) +# av2, bv2 = Statsample.only_valid_clone av, bv +# assert_equal(av, av2) +# assert_same(av, av2) +# assert_same(bv, bv2) +# end +# end +# context Statsample::Vector do +# setup do +# @c = Statsample::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], :object) +# @c.name = 'Test Vector' +# @c.missing_values = [-99] +# end +# should_with_gsl 'be created with GSL::Vector' do +# gsl = GSL::Vector[1, 2, 3, 4, 5] +# v = Statsample::Vector.new(gsl) +# assert_equal([1, 2, 3, 4, 5], v.to_a) +# refute(v.flawed?) +# end + +# context 'using matrix operations' do +# setup do +# @a = [1, 2, 3, 4, 5].to_numeric +# end +# should 'to_matrix returns a matrix with 1 row' do +# mh = Matrix[[1, 2, 3, 4, 5]] +# assert_equal(mh, @a.to_matrix) +# end +# should 'to_matrix(:vertical) returns a matrix with 1 column' do +# mv = Matrix.columns([[1, 2, 3, 4, 5]]) +# assert_equal(mv, @a.to_matrix(:vertical)) +# end +# should 'returns valid submatrixes' do +# # 3*4 + 2*5 = 22 +# a = [3, 2].to_vector(:numeric) +# b = [4, 5].to_vector(:numeric) +# assert_equal(22, (a.to_matrix * b.to_matrix(:vertical))[0, 0]) +# end +# end +# context 'when initializing' do +# setup do +# @data = (10.times.map { rand(100) }) + [nil] +# @original = Statsample::Vector.new(@data, :numeric) +# end +# should 'be the sample using []' do +# second = Statsample::Vector[*@data] +# assert_equal(@original, second) +# end +# should '[] returns same results as R-c()' do +# reference = [0, 4, 5, 6, 10].to_numeric +# assert_equal(reference, Statsample::Vector[0, 4, 5, 6, 10]) +# assert_equal(reference, Statsample::Vector[0, 4..6, 10]) +# assert_equal(reference, Statsample::Vector[[0], [4, 5, 6], [10]]) +# assert_equal(reference, Statsample::Vector[[0], [4, [5, [6]]], [10]]) + +# assert_equal(reference, Statsample::Vector[[0], [4, 5, 6].to_vector, [10]]) +# end +# should 'be the same usign #to_vector' do +# lazy1 = @data.to_vector(:numeric) +# assert_equal(@original, lazy1) +# end +# should 'be the same using #to_numeric' do +# lazy2 = @data.to_numeric +# assert_equal(@original, lazy2) +# assert_equal(:numeric, lazy2.type) +# assert_equal(@data.find_all { |v| !v.nil? }, lazy2.valid_data) +# end +# should 'could use new_numeric with size only' do +# v1 = 10.times.map { nil }.to_numeric +# v2 = Statsample::Vector.new_numeric(10) +# assert_equal(v1, v2) +# end +# should 'could use new_numeric with size and value' do +# a = rand +# v1 = 10.times.map { a }.to_numeric +# v2 = Statsample::Vector.new_numeric(10, a) +# assert_equal(v1, v2) +# end +# should 'could use new_numeric with func' do +# v1 = 10.times.map { |i| i * 2 }.to_numeric +# v2 = Statsample::Vector.new_numeric(10) { |i| i * 2 } +# assert_equal(v1, v2) +# end +# end + +# context "new types :numeric and :object" do +# should "set default type of vector to :object" do +# v = Statsample::Vector.new [1,2,3,4,5] +# assert_equal(:object, v.type) +# end + +# should "initialize Vector with :numeric type" do +# v = Statsample::Vector.new [1,2,3,4,5,nil], :numeric +# assert_equal(:numeric, v.type) +# assert_equal([1,2,3,4,5], v.valid_data) +# end + +# should "show a warning when initializing with :nominal, :numeric or :ordinal" do +# assert_output(nil,"WARNING: nominal has been deprecated. Use :object instead.\n") do +# Statsample::Vector.new [1,2,3,4,5,nil,'hello'], :nominal +# end + +# assert_output(nil,"WARNING: scale has been deprecated. Use :numeric instead.\n") do +# Statsample::Vector.new [1,2,3,4,nil,5], :scale +# end + +# assert_output(nil,"WARNING: ordinal has been deprecated. Use :numeric instead.\n") do +# Statsample::Vector.new [1,2,3,4,5], :ordinal +# end + +# assert_output(nil,"WARNING: .new_scale has been deprecated. Use .new_numeric instead.\n") do +# Statsample::Vector.new_scale 10, 1 +# end +# end + +# should "test that new shorthands work" do +# numeric = Statsample::Vector.new([1,2,3,4,nil,5], :numeric) +# assert_equal(numeric, [1,2,3,4,nil,5].to_numeric) +# assert_equal(numeric, [1,2,3,4,nil,5].to_vector(:numeric)) + +# obj = Statsample::Vector.new([1,2,3,4,'one','two'], :object) +# assert_equal(obj, [1,2,3,4,'one','two'].to_vector(:object)) +# end + +# should "test that old shorthands raise warnings" do +# assert_output(nil,"WARNING: to_scale has been deprecated. Use to_numeric instead.\n") do +# [1,2,3,4,nil,5].to_scale +# end +# end +# end + +# context '#split_by_separator' do +# setup do +# @a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 10, nil], :object) +# @b = @a.split_by_separator(',') +# end +# should 'returns a Hash' do +# assert_kind_of(Hash, @b) +# end +# should 'return a Hash with keys with different values of @a' do +# expected = ['a', 'b', 'c', 'd', 10] +# assert_equal(expected, @b.keys) +# end + +# should 'returns a Hash, which values are Statsample::Vector' do +# @b.each_key { |k| assert_instance_of(Statsample::Vector, @b[k]) } +# end +# should 'hash values are n times the tokens appears' do +# assert_counting_tokens(@b) +# end +# should '#split_by_separator_freq returns the number of ocurrences of tokens' do +# assert_equal({ 'a' => 3, 'b' => 1, 'c' => 1, 'd' => 2, 10 => 1 }, @a.split_by_separator_freq) +# end +# should 'using a different separator give the same values' do +# a = Statsample::Vector.new(['a', 'a*b', 'c*d', 'a*d', 10, nil], :object) +# b = a.split_by_separator('*') +# assert_counting_tokens(b) +# end +# end +# should 'return correct median_absolute_deviation' do +# a = [1, 1, 2, 2, 4, 6, 9].to_numeric +# assert_equal(1, a.median_absolute_deviation) +# end +# should 'return correct histogram' do +# a = 10.times.map { |v| v }.to_numeric +# hist = a.histogram(2) +# assert_equal([5, 5], hist.bin) +# 3.times do |i| +# assert_in_delta(i * 4.5, hist.get_range(i)[0], 1e-9) +# end +# end +# should 'have a name' do +# @c.name == 'Test Vector' +# end +# should 'without explicit name, returns vector with succesive numbers' do +# a = 10.times.map { rand(100) }.to_numeric +# b = 10.times.map { rand(100) }.to_numeric +# assert_match(/Vector \d+/, a.name) +# a.name =~ /Vector (\d+)/ +# next_number = Regexp.last_match(1).to_i + 1 +# assert_equal("Vector #{next_number}", b.name) +# end +# should 'save to a file and load the same Vector' do +# outfile = Tempfile.new('vector.vec') +# @c.save(outfile.path) +# a = Statsample.load(outfile.path) +# assert_equal(@c, a) +# end +# should '#collect returns an array' do +# val = @c.collect { |v| v } +# assert_equal(val, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]) +# end + +# should '#recode returns a recoded array' do +# a = @c.recode { |v| @c.is_valid?(v) ? 0 : 1 } +# exp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1].to_vector +# assert_equal(exp, a) +# exp.recode! { |v| v == 0 ? 1 : 0 } +# exp2 = (([1] * 15) + ([0] * 3)).to_vector +# assert_equal(exp2, exp) +# end +# should '#product returns the * of all values' do +# a = [1, 2, 3, 4, 5].to_vector(:numeric) +# assert_equal(120, a.product) +# end + +# should 'missing values' do +# @c.missing_values = [10] +# assert_equal([-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9], @c.valid_data.sort) +# assert_equal([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, nil, 1, 2, 3, 4, nil, -99, -99], @c.data_with_nils) +# @c.missing_values = [-99] +# assert_equal(@c.valid_data.sort, [1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]) +# assert_equal(@c.data_with_nils, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, nil, nil]) +# @c.missing_values = [] +# assert_equal(@c.valid_data.sort, [-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]) +# assert_equal(@c.data_with_nils, [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]) +# end +# should 'correct has_missing_data? with missing data' do +# a = [1, 2, 3, nil].to_vector +# assert(a.has_missing_data?) +# end +# should 'correct has_missing_data? without missing data' do +# a = [1, 2, 3, 4, 10].to_vector +# assert(!a.has_missing_data?) +# end +# should 'with explicit missing_values, should respond has_missing_data?' do +# a = [1, 2, 3, 4, 10].to_vector +# a.missing_values = [10] +# assert(a.has_missing_data?) +# end +# should 'label correctly fields' do +# @c.labels = { 5 => 'FIVE' } +# assert_equal(['FIVE', 'FIVE', 'FIVE', 'FIVE', 'FIVE', 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99], @c.vector_labeled.to_a) +# end +# should 'verify' do +# h = @c.verify { |d| !d.nil? and d > 0 } +# e = { 15 => nil, 16 => -99, 17 => -99 } +# assert_equal(e, h) +# end +# should 'have a summary with name on it' do +# assert_match(/#{@c.name}/, @c.summary) +# end + +# should 'GSL::Vector based should push correcty' do +# if Statsample.has_gsl? +# v = GSL::Vector[1, 2, 3, 4, 5].to_numeric +# v.push(nil) +# assert_equal([1, 2, 3, 4, 5, nil], v.to_a) +# assert(v.flawed?) +# else +# skip('Requires GSL') +# end +# end + +# should 'split correctly' do +# a = Statsample::Vector.new(['a', 'a,b', 'c,d', 'a,d', 'd', 10, nil], :object) +# assert_equal([%w(a), %w(a b), %w(c d), %w(a d), %w(d), [10], nil], a.splitted) +# end +# should 'multiply correct for scalar' do +# a = [1, 2, 3].to_numeric +# assert_equal([5, 10, 15].to_numeric, a * 5) +# end +# should 'multiply correct with other vector' do +# a = [1, 2, 3].to_numeric +# b = [2, 4, 6].to_numeric + +# assert_equal([2, 8, 18].to_numeric, a * b) +# end +# should 'sum correct for scalar' do +# a = [1, 2, 3].to_numeric +# assert_equal([11, 12, 13].to_numeric, a + 10) +# end + +# should 'raise NoMethodError when method requires numeric and vector is object' do +# @c.type = :object +# assert_raise(::NoMethodError) { @c.median } +# end + +# should 'jacknife correctly with named method' do +# # First example +# a = [1, 2, 3, 4].to_numeric +# ds = a.jacknife(:mean) +# assert_equal(a.mean, ds[:mean].mean) +# ds = a.jacknife([:mean, :sd]) +# assert_equal(a.mean, ds[:mean].mean) +# assert_equal(a.sd, ds[:mean].sd) +# end +# should 'jacknife correctly with custom method' do +# # Second example +# a = [17.23, 18.71, 13.93, 18.81, 15.78, 11.29, 14.91, 13.39, 18.21, 11.57, 14.28, 10.94, 18.83, 15.52, 13.45, 15.25].to_numeric +# ds = a.jacknife(log_s2: ->(v) { Math.log(v.variance) }) +# exp = [1.605, 2.972, 1.151, 3.097, 0.998, 3.308, 0.942, 1.393, 2.416, 2.951, 1.043, 3.806, 3.122, 0.958, 1.362, 0.937].to_numeric + +# assert_similar_vector(exp, ds[:log_s2], 0.001) +# assert_in_delta(2.00389, ds[:log_s2].mean, 0.00001) +# assert_in_delta(1.091, ds[:log_s2].variance, 0.001) +# end +# should 'jacknife correctly with k>1' do +# a = rnorm(6) +# ds = a.jacknife(:mean, 2) +# mean = a.mean +# exp = [3 * mean - 2 * (a[2] + a[3] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[4] + a[5]) / 4, 3 * mean - 2 * (a[0] + a[1] + a[2] + a[3]) / 4].to_numeric +# assert_similar_vector(exp, ds[:mean], 1e-13) +# end +# should 'bootstrap should return a vector with mean=mu and sd=se' do +# a = rnorm(100) +# ds = a.bootstrap([:mean, :sd], 200) +# se = 1 / Math.sqrt(a.size) +# assert_in_delta(0, ds[:mean].mean, 0.3) +# assert_in_delta(se, ds[:mean].sd, 0.02) +# end +# end + +# def test_object +# assert_equal(@c[1], 5) +# assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c.frequencies) +# assert_equal({ 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 5, 6 => 2, 7 => 1, 8 => 1, 9 => 1, 10 => 1 }, @c._frequencies) +# assert_equal({ 1 => 1.quo(15), 2 => 1.quo(15), 3 => 1.quo(15), 4 => 1.quo(15), 5 => 5.quo(15), 6 => 2.quo(15), 7 => 1.quo(15), 8 => 1.quo(15), 9 => 1.quo(15), 10 => 1.quo(15) }, @c.proportions) +# assert_equal(@c.proportion, 1.quo(15)) +# assert_equal(@c.proportion(2), 1.quo(15)) +# assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], @c.factors.sort) +# assert_equal(@c.mode, 5) +# assert_equal(@c.n_valid, 15) +# end + +# def test_equality +# v1 = [1, 2, 3].to_vector +# v2 = [1, 2, 3].to_vector +# assert_equal(v1, v2) +# v1 = [1, 2, 3].to_vector(:object) +# v2 = [1, 2, 3].to_vector(:numeric) +# assert_not_equal(v1, v2) +# v2 = [1, 2, 3] +# assert_not_equal(v1, v2) +# v1 = [1, 2, 3].to_vector +# v2 = [1, 2, 3].to_vector +# assert_equal(v1, v2) +# assert_equal(false, v1 == Object.new) +# end + +# def test_vector_percentil +# a = [1, 2, 2, 3, 4, 5, 5, 5, 6, 10].to_numeric +# expected = [10, 25, 25, 40, 50, 70, 70, 70, 90, 100].to_numeric +# assert_equal(expected, a.vector_percentil) +# a = [1, nil, nil, 2, 2, 3, 4, nil, nil, 5, 5, 5, 6, 10].to_numeric +# expected = [10, nil, nil, 25, 25, 40, 50, nil, nil, 70, 70, 70, 90, 100].to_numeric +# assert_equal(expected, a.vector_percentil) +# end + +# def test_numeric +# @c.type = :numeric +# assert_equal(5, @c.median) +# assert_equal(4, @c.percentil(25)) +# assert_equal(7, @c.percentil(75)) + +# v = [200_000, 200_000, 210_000, 220_000, 230_000, 250_000, 250_000, 250_000, 270_000, 300_000, 450_000, 130_000, 140_000, 140_000, 140_000, 145_000, 148_000, 165_000, 170_000, 180_000, 180_000, 180_000, 180_000, 180_000, 180_000].to_numeric +# assert_equal(180_000, v.median) +# a = [7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 9.0, 9.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 12.0, 13.0, 14.0, 14.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0].to_numeric +# assert_equal(4.5, a.percentil(25)) +# assert_equal(6.5, a.percentil(50)) +# assert_equal(9.5, a.percentil(75)) +# assert_equal(3.0, a.percentil(10)) +# end + +# def test_linear_percentil_strategy +# values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116].shuffle.to_numeric +# assert_equal 102, values.percentil(0, :linear) +# assert_equal 104.75, values.percentil(25, :linear) +# assert_equal 108.5, values.percentil(50, :linear) +# assert_equal 112.75, values.percentil(75, :linear) +# assert_equal 116, values.percentil(100, :linear) + +# values = [102, 104, 105, 107, 108, 109, 110, 112, 115, 116, 118].shuffle.to_numeric +# assert_equal 102, values.percentil(0, :linear) +# assert_equal 105, values.percentil(25, :linear) +# assert_equal 109, values.percentil(50, :linear) +# assert_equal 115, values.percentil(75, :linear) +# assert_equal 118, values.percentil(100, :linear) +# end + +# def test_ranked +# v1 = [0.8, 1.2, 1.2, 2.3, 18].to_vector(:numeric) +# expected = [1, 2.5, 2.5, 4, 5].to_vector(:numeric) +# assert_equal(expected, v1.ranked) +# v1 = [nil, 0.8, 1.2, 1.2, 2.3, 18, nil].to_vector(:numeric) +# expected = [nil, 1, 2.5, 2.5, 4, 5, nil].to_vector(:numeric) +# assert_equal(expected, v1.ranked) +# end + +# def test_numeric +# a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :numeric) +# assert_equal(10, a.sum) +# i = 0 +# factors = a.factors.sort +# [0, 1, 2, 3, 4].each{|v| +# assert(v == factors[i]) +# assert(v.class == factors[i].class, "#{v} - #{v.class} != #{factors[i]} - #{factors[i].class}") +# i += 1 +# } +# end + +# def test_vector_centered +# mean = rand +# samples = 11 +# centered = samples.times.map { |i| i - ((samples / 2).floor).to_i }.to_numeric +# not_centered = centered.recode { |v| v + mean } +# obs = not_centered.centered +# centered.each_with_index do |v, i| +# assert_in_delta(v, obs[i], 0.0001) +# end +# end + +# def test_vector_standarized +# v1 = [1, 2, 3, 4, nil].to_vector(:numeric) +# sds = v1.sds +# expected = [((1 - 2.5).quo(sds)), ((2 - 2.5).quo(sds)), ((3 - 2.5).quo(sds)), ((4 - 2.5).quo(sds)), nil].to_vector(:numeric) +# vs = v1.vector_standarized +# assert_equal(expected, vs) +# assert_equal(0, vs.mean) +# assert_equal(1, vs.sds) +# end + +# def test_vector_standarized_with_zero_variance +# v1 = 100.times.map { |_i| 1 }.to_numeric +# exp = 100.times.map { nil }.to_numeric +# assert_equal(exp, v1.standarized) +# end + +# def test_check_type +# v = Statsample::Vector.new +# v.type = :object +# assert_raise(NoMethodError) { v.check_type(:numeric) } +# assert(v.check_type(:object).nil?) + +# v.type = :numeric + +# assert(v.check_type(:numeric).nil?) +# assert(v.check_type(:object).nil?) + +# v.type = :date +# assert_raise(NoMethodError) { v.check_type(:numeric) } +# assert_raise(NoMethodError) { v.check_type(:numeric) } +# assert_raise(NoMethodError) { v.check_type(:object) } +# end + +# def test_add +# a = Statsample::Vector.new([1, 2, 3, 4, 5], :numeric) +# b = Statsample::Vector.new([11, 12, 13, 14, 15], :numeric) +# assert_equal([3, 4, 5, 6, 7], (a + 2).to_a) +# assert_equal([12, 14, 16, 18, 20], (a + b).to_a) +# assert_raise ArgumentError do +# a + @c +# end +# assert_raise TypeError do +# a + 'string' +# end +# a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :numeric) +# b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :numeric) +# assert_equal([nil, 13, nil, 16, 18, 20], (a + b).to_a) +# assert_equal([nil, 13, nil, 16, 18, 20], (a + b.to_a).to_a) +# end + +# def test_minus +# a = Statsample::Vector.new([1, 2, 3, 4, 5], :numeric) +# b = Statsample::Vector.new([11, 12, 13, 14, 15], :numeric) +# assert_equal([-1, 0, 1, 2, 3], (a - 2).to_a) +# assert_equal([10, 10, 10, 10, 10], (b - a).to_a) +# assert_raise ArgumentError do +# a - @c +# end +# assert_raise TypeError do +# a - 'string' +# end +# a = Statsample::Vector.new([nil, 1, 2, 3, 4, 5], :numeric) +# b = Statsample::Vector.new([11, 12, nil, 13, 14, 15], :numeric) +# assert_equal([nil, 11, nil, 10, 10, 10], (b - a).to_a) +# assert_equal([nil, 11, nil, 10, 10, 10], (b - a.to_a).to_a) +# end + +# def test_sum_of_squares +# a = [1, 2, 3, 4, 5, 6].to_vector(:numeric) +# assert_equal(17.5, a.sum_of_squared_deviation) +# end + +# def test_average_deviation +# a = [1, 2, 3, 4, 5, 6, 7, 8, 9].to_numeric +# assert_equal(20.quo(9), a.average_deviation_population) +# end + +# def test_samples +# srand(1) +# assert_equal(100, @c.sample_with_replacement(100).size) +# assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) +# assert_raise ArgumentError do +# @c.sample_without_replacement(20) +# end +# @c.type = :numeric +# srand(1) +# assert_equal(100, @c.sample_with_replacement(100).size) +# assert_equal(@c.valid_data.to_a.sort, @c.sample_without_replacement(15).sort) +# end + +# def test_valid_data +# a = Statsample::Vector.new([1, 2, 3, 4, 'STRING']) +# a.missing_values = [-99] +# a.add(1, false) +# a.add(2, false) +# a.add(-99, false) +# a.set_valid_data +# exp_valid_data = [1, 2, 3, 4, 'STRING', 1, 2] +# assert_equal(exp_valid_data, a.valid_data) +# a.add(20, false) +# a.add(30, false) +# assert_equal(exp_valid_data, a.valid_data) +# a.set_valid_data +# exp_valid_data_2 = [1, 2, 3, 4, 'STRING', 1, 2, 20, 30] +# assert_equal(exp_valid_data_2, a.valid_data) +# end + +# def test_set_value +# @c[2] = 10 +# expected = [5, 5, 10, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99].to_vector +# assert_equal(expected.data, @c.data) +# end + +# def test_gsl +# if Statsample.has_gsl? +# a = Statsample::Vector.new([1, 2, 3, 4, 'STRING'], :numeric) + +# assert_equal(2, a.mean) +# assert_equal(a.variance_sample_ruby, a.variance_sample) +# assert_equal(a.standard_deviation_sample_ruby, a.sds) +# assert_equal(a.variance_population_ruby, a.variance_population) +# assert_equal(a.standard_deviation_population_ruby, a.standard_deviation_population) +# assert_nothing_raised do +# a = [].to_vector(:numeric) +# end +# a.add(1, false) +# a.add(2, false) +# a.set_valid_data +# assert_equal(3, a.sum) +# b = [1, 2, nil, 3, 4, 5, nil, 6].to_vector(:numeric) +# assert_equal(21, b.sum) +# assert_equal(3.5, b.mean) +# assert_equal(6, b.gsl.size) +# c = [10, 20, 30, 40, 50, 100, 1000, 2000, 5000].to_numeric +# assert_in_delta(c.skew, c.skew_ruby, 0.0001) +# assert_in_delta(c.kurtosis, c.kurtosis_ruby, 0.0001) +# end +# end + +# def test_vector_matrix +# v1 = %w(a a a b b b c c).to_vector +# v2 = %w(1 3 4 5 6 4 3 2).to_vector +# v3 = %w(1 0 0 0 1 1 1 0).to_vector +# ex = Matrix.rows([%w(a 1 1), %w(a 3 0), %w(a 4 0), %w(b 5 0), %w(b 6 1), %w(b 4 1), %w(c 3 1), %w(c 2 0)]) +# assert_equal(ex, Statsample.vector_cols_matrix(v1, v2, v3)) +# end + +# def test_marshalling +# v1 = (0..100).to_a.collect { |_n| rand(100) }.to_vector(:numeric) +# v2 = Marshal.load(Marshal.dump(v1)) +# assert_equal(v1, v2) +# end + +# def test_dup +# v1 = %w(a a a b b b c c).to_vector +# v2 = v1.dup +# assert_equal(v1.data, v2.data) +# assert_not_same(v1.data, v2.data) +# assert_equal(v1.type, v2.type) + +# v1.type = :numeric +# assert_not_equal(v1.type, v2.type) +# assert_equal(v1.missing_values, v2.missing_values) +# assert_not_same(v1.missing_values, v2.missing_values) +# assert_equal(v1.labels, v2.labels) +# assert_not_same(v1.labels, v2.labels) + +# v3 = v1.dup_empty +# assert_equal([], v3.data) +# assert_not_equal(v1.data, v3.data) +# assert_not_same(v1.data, v3.data) +# assert_equal(v1.type, v3.type) +# v1.type = :numeric +# v3.type = :object +# assert_not_equal(v1.type, v3.type) +# assert_equal(v1.missing_values, v3.missing_values) +# assert_not_same(v1.missing_values, v3.missing_values) +# assert_equal(v1.labels, v3.labels) +# assert_not_same(v1.labels, v3.labels) +# end + +# def test_paired_ties +# a = [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 4].to_vector(:numeric) +# expected = [2, 2, 2, 4.5, 4.5, 6, 7.5, 7.5, 10, 10, 10].to_vector(:numeric) +# assert_equal(expected, a.ranked) +# end + +# def test_dichotomize +# a = [0, 0, 0, 1, 2, 3, nil].to_vector +# exp = [0, 0, 0, 1, 1, 1, nil].to_numeric +# assert_equal(exp, a.dichotomize) +# a = [1, 1, 1, 2, 2, 2, 3].to_vector +# exp = [0, 0, 0, 1, 1, 1, 1].to_numeric +# assert_equal(exp, a.dichotomize) +# a = [0, 0, 0, 1, 2, 3, nil].to_vector +# exp = [0, 0, 0, 0, 1, 1, nil].to_numeric +# assert_equal(exp, a.dichotomize(1)) +# a = %w(a a a b c d).to_vector +# exp = [0, 0, 0, 1, 1, 1].to_numeric +# assert_equal(exp, a.dichotomize) +# end + +# def test_can_be_methods +# a = [0, 0, 0, 1, 2, 3, nil].to_vector +# assert(a.can_be_numeric?) +# a = [0, 's', 0, 1, 2, 3, nil].to_vector +# assert(!a.can_be_numeric?) +# a.missing_values = ['s'] +# assert(a.can_be_numeric?) + +# a = [Date.new(2009, 10, 10), Date.today, '2009-10-10', '2009-1-1', nil, 'NOW'].to_vector +# assert(a.can_be_date?) +# a = [Date.new(2009, 10, 10), Date.today, nil, 'sss'].to_vector +# assert(!a.can_be_date?) +# end + +# def test_date_vector +# a = [Date.new(2009, 10, 10), :NOW, '2009-10-10', '2009-1-1', nil, 'NOW', 'MISSING'].to_vector(:date, missing_values: ['MISSING']) + +# assert(a.type == :date) +# expected = [Date.new(2009, 10, 10), Date.today, Date.new(2009, 10, 10), Date.new(2009, 1, 1), nil, Date.today, nil] +# assert_equal(expected, a.date_data_with_nils) +# end +# end From 675632b229edd65d5331c996aecb0431b29e97cf Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Mon, 25 May 2015 11:30:26 +0530 Subject: [PATCH 051/119] Statsample::Test::WilcoxonSignedRank now works with daru --- lib/statsample/test/wilcoxonsignedrank.rb | 88 ++++++++++++----------- test/test_wilcoxonsignedrank.rb | 8 +-- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/lib/statsample/test/wilcoxonsignedrank.rb b/lib/statsample/test/wilcoxonsignedrank.rb index be8b223..5661904 100644 --- a/lib/statsample/test/wilcoxonsignedrank.rb +++ b/lib/statsample/test/wilcoxonsignedrank.rb @@ -8,13 +8,13 @@ class WilcoxonSignedRank # Name of F analysis attr_accessor :name - attr_reader :w - attr_reader :nr - attr_writer :tails + attr_reader :w + attr_reader :nr + attr_writer :tails # Parameters: def initialize(v1,v2, opts=Hash.new) - @v1=v1 - @v2=v2 + @v1 = v1 + @v2 = v2 opts_default={:name=>_("Wilcoxon Signed Rank Test"),:tails=>:both} @opts=opts_default.merge(opts) opts_default.keys.each {|k| @@ -22,66 +22,68 @@ def initialize(v1,v2, opts=Hash.new) } calculate end + def calculate - df=Statsample::Dataset.new({'v1'=>@v1,'v2'=>@v2}) - df["abs"]=df.collect {|row| - r=(row["v2"]-row["v1"]).abs - } - df["sgn"]=df.collect {|row| - r=row["v2"]-row["v1"] - r==0 ? 0 : r/r.abs - } - df=df.filter {|row| row["sgn"]!=0} - df["rank"]=df["abs"].ranked - @nr=df.cases - @w=df.collect {|row| - row["sgn"]*row["rank"] - #p row["sgn"]*row["rank"] - }.sum + df = Daru::DataFrame.new({:v1 => @v1,:v2 => @v2}) + # df[:abs]=df.collect(:row) { |row| (row[:v2] - row[:v1]).abs } + df[:abs] = (df[:v2] - df[:v1]).abs + df[:sgn] = df.collect(:row) { |row| + r = row[:v2] - row[:v1] + r == 0 ? 0 : r/r.abs + } + df = df.filter_rows { |row| row[:sgn] != 0} + df[:rank] = df[:abs].ranked + @nr = df.nrows + + @w = df.collect(:row) { |row| + row[:sgn] * row[:rank] + }.sum end + def report_building(generator) # :nodoc: generator.section(:name=>@name) do |s| s.table(:name=>_("%s results") % @name) do |t| t.row([_("W Value"), "%0.3f" % @w]) t.row([_("Z"), "%0.3f (p: %0.3f)" % [z, probability_z]]) if(nr<=10) - t.row([_("Exact probability"), "p-exact: %0.3f" % [probability_exact]]) + t.row([_("Exact probability"), "p-exact: %0.3f" % [probability_exact]]) end end end end def z - sigma=Math.sqrt((nr*(nr+1)*(2*nr+1))/6) - (w-0.5)/sigma + sigma=Math.sqrt((nr*(nr+1)*(2*nr+1))/6) + (w-0.5)/sigma end # Assuming normal distribution of W, this calculate # the probability of samples with Z equal or higher than # obtained on sample def probability_z - (1-Distribution::Normal.cdf(z))*(@tails==:both ? 2:1) + (1-Distribution::Normal.cdf(z))*(@tails==:both ? 2:1) end # Calculate exact probability. # Don't calculate for large Nr, please! def probability_exact - str_format="%0#{nr}b" - combinations=2**nr - #p str_format - total_w=combinations.times.map {|i| - comb=sprintf(str_format,i) - w_local=comb.length.times.inject(0) {|ac,j| - sgn=comb[j]=="0" ? -1 : 1 - ac+(j+1)*sgn - } - }.sort - total_w.find_all {|v| - if @tails==:both - v<=-w.abs or v>=w.abs - elsif @tails==:left - v<=w - elsif @tails==:right - v>=w - end - }.count/(combinations.to_f) + str_format="%0#{nr}b" + combinations=2**nr + #p str_format + total_w=combinations.times.map do |i| + comb=sprintf(str_format,i) + w_local=comb.length.times.inject(0) do |ac,j| + sgn=comb[j]=="0" ? -1 : 1 + ac+(j+1)*sgn + end + end.sort + + total_w.find_all do |v| + if @tails==:both + v<=-w.abs or v>=w.abs + elsif @tails==:left + v<=w + elsif @tails==:right + v>=w + end + end.count/(combinations.to_f) end end end diff --git a/test/test_wilcoxonsignedrank.rb b/test/test_wilcoxonsignedrank.rb index 04cedd3..c32e341 100644 --- a/test/test_wilcoxonsignedrank.rb +++ b/test/test_wilcoxonsignedrank.rb @@ -5,8 +5,8 @@ class StatsampleUMannWhitneyTestCase < Minitest::Test context Statsample::Test::WilcoxonSignedRank do context 'Example 1' do setup do - @v1 = [110, 122, 125, 120, 140, 124, 123, 137, 135, 145].to_numeric - @v2 = [125, 115, 130, 140, 140, 115, 140, 125, 140, 135].to_numeric + @v1 = Daru::Vector.new([110, 122, 125, 120, 140, 124, 123, 137, 135, 145]) + @v2 = Daru::Vector.new([125, 115, 130, 140, 140, 115, 140, 125, 140, 135]) @u = Statsample::Test::WilcoxonSignedRank.new(@v1, @v2) end should 'have same result using class or Test#u_mannwhitney' do @@ -34,8 +34,8 @@ class StatsampleUMannWhitneyTestCase < Minitest::Test context 'Example 2' do setup do - @v2 = [78, 24, 64, 45, 64, 52, 30, 50, 64, 50, 78, 22, 84, 40, 90, 72].to_numeric - @v1 = [78, 24, 62, 48, 68, 56, 25, 44, 56, 40, 68, 36, 68, 20, 58, 32].to_numeric + @v2 = Daru::Vector.new([78, 24, 64, 45, 64, 52, 30, 50, 64, 50, 78, 22, 84, 40, 90, 72]) + @v1 = Daru::Vector.new([78, 24, 62, 48, 68, 56, 25, 44, 56, 40, 68, 36, 68, 20, 58, 32]) @u = Statsample::Test::WilcoxonSignedRank.new(@v1, @v2) end should 'have same result using class or Test#u_mannwhitney' do From 45382ca0dc05939fee86120fa9528a4dfad984fe Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Mon, 25 May 2015 11:32:29 +0530 Subject: [PATCH 052/119] Statsample::Test::BartlettSpericity now works with daru --- test/test_bartlettsphericity.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_bartlettsphericity.rb b/test/test_bartlettsphericity.rb index 4c724fb..3865259 100644 --- a/test/test_bartlettsphericity.rb +++ b/test/test_bartlettsphericity.rb @@ -4,11 +4,11 @@ class StatsampleBartlettSphericityTestCase < Minitest::Test include Statsample::Test context Statsample::Test::BartlettSphericity do setup do - @v1 = [1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70].to_numeric - @v2 = [5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0].to_numeric - @v3 = [10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4].to_numeric + @v1 = Daru::Vector.new([1, 2, 3, 4, 7, 8, 9, 10, 14, 15, 20, 50, 60, 70]) + @v2 = Daru::Vector.new([5, 6, 11, 12, 13, 16, 17, 18, 19, 20, 30, 0, 0, 0]) + @v3 = Daru::Vector.new([10, 3, 20, 30, 40, 50, 80, 10, 20, 30, 40, 2, 3, 4]) # KMO: 0.490 - ds = { 'v1' => @v1, 'v2' => @v2, 'v3' => @v3 }.to_dataset + ds = Daru::DataFrame.new({ :v1 => @v1, :v2 => @v2, :v3 => @v3 }) cor = Statsample::Bivariate.correlation_matrix(ds) @bs = Statsample::Test::BartlettSphericity.new(cor, 14) end From 9be7f8a5f15808779fcd2a61e863fb338fff2e82 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Mon, 25 May 2015 11:39:12 +0530 Subject: [PATCH 053/119] Statsample::Crosstab now works with dary --- lib/statsample/crosstab.rb | 16 ++++++++-------- test/test_crosstab.rb | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/statsample/crosstab.rb b/lib/statsample/crosstab.rb index 981f550..6efe9b6 100644 --- a/lib/statsample/crosstab.rb +++ b/lib/statsample/crosstab.rb @@ -24,10 +24,10 @@ def initialize(v1, v2, opts=Hash.new) @name ||= _("Crosstab %s - %s") % [@row_label, @column_label] end def rows_names - @v_rows.factors.sort + @v_rows.factors.sort.reset_index! end def cols_names - @v_cols.factors.sort + @v_cols.factors.sort.reset_index! end def rows_total @v_rows.frequencies @@ -69,8 +69,8 @@ def frequencies_by_col end # Chi square, based on expected and real matrix def chi_square - require 'statsample/test' - Statsample::Test.chi_square(self.to_matrix, matrix_expected) + require 'statsample/test' + Statsample::Test.chi_square(self.to_matrix, matrix_expected) end # Useful to obtain chi square def matrix_expected @@ -100,10 +100,10 @@ def report_building(builder) generator.text(_("Rows: %s") % @row_label) unless @row_label.nil? generator.text(_("Columns: %s") % @column_label) unless @column_label.nil? - t=ReportBuilder::Table.new(:name=>@name+" - "+_("Raw"), :header=>[""]+cols_names.collect {|c| @v_cols.labeling(c)}+[_("Total")]) + t=ReportBuilder::Table.new(:name=>@name+" - "+_("Raw"), :header=>[""]+cols_names.collect {|c| @v_cols.index_of(c)}+[_("Total")]) rn.each do |row| total_row=0 - t_row=[@v_rows.labeling(row)] + t_row=[@v_rows.index_of(row)] cn.each do |col| data=fq[[row,col]] total_row+=fq[[row,col]] @@ -150,9 +150,9 @@ def table_percentage(generator,type) when :total then _("% Total") end - t=ReportBuilder::Table.new(:name=>@name+" - "+_(type_name), :header=>[""]+cols_names.collect {|c| @v_cols.labeling(c) } + [_("Total")]) + t=ReportBuilder::Table.new(:name=>@name+" - "+_(type_name), :header=>[""]+cols_names.collect {|c| @v_cols.index_of(c) } + [_("Total")]) rn.each do |row| - t_row=[@v_rows.labeling(row)] + t_row=[@v_rows.index_of(row)] cn.each do |col| total=case type when :row then rt[row] diff --git a/test/test_crosstab.rb b/test/test_crosstab.rb index 28ab370..8f39460 100644 --- a/test/test_crosstab.rb +++ b/test/test_crosstab.rb @@ -1,8 +1,8 @@ require(File.expand_path(File.dirname(__FILE__) + '/helpers_tests.rb')) class StatsampleCrosstabTestCase < Minitest::Test def initialize(*args) - @v1 = %w(black blonde black black red black brown black blonde black red black blonde).to_vector - @v2 = %w(woman man man woman man man man woman man woman woman man man).to_vector + @v1 =Daru::Vector.new( %w(black blonde black black red black brown black blonde black red black blonde)) + @v2 =Daru::Vector.new( %w(woman man man woman man man man woman man woman woman man man)) @ct = Statsample::Crosstab.new(@v1, @v2) super end @@ -12,7 +12,7 @@ def test_crosstab_errors assert_raise ArgumentError do Statsample::Crosstab.new(e1, @v2) end - e2 = %w(black blonde black black red black brown black blonde black black).to_vector + e2 = Daru::Vector.new(%w(black blonde black black red black brown black blonde black black)) assert_raise ArgumentError do Statsample::Crosstab.new(e2, @v2) @@ -23,8 +23,8 @@ def test_crosstab_errors end def test_crosstab_basic - assert_equal(%w(black blonde brown red), @ct.rows_names) - assert_equal(%w(man woman), @ct.cols_names) + assert_equal(Daru::Vector.new(%w(black blonde brown red)), @ct.rows_names) + assert_equal(Daru::Vector.new(%w(man woman)), @ct.cols_names) assert_equal({ 'black' => 7, 'blonde' => 3, 'red' => 2, 'brown' => 1 }, @ct.rows_total) assert_equal({ 'man' => 8, 'woman' => 5 }, @ct.cols_total) end @@ -51,15 +51,15 @@ def test_summary end def test_expected - v1 = %w(1 1 1 1 1 0 0 0 0 0).to_vector - v2 = %w(0 0 0 0 0 1 1 1 1 1).to_vector + v1 = Daru::Vector.new(%w(1 1 1 1 1 0 0 0 0 0)) + v2 = Daru::Vector.new(%w(0 0 0 0 0 1 1 1 1 1)) ct = Statsample::Crosstab.new(v1, v2) assert_equal(Matrix[[2.5, 2.5], [2.5, 2.5]], ct.matrix_expected) end def test_crosstab_with_scale - v1 = %w(1 1 1 1 1 0 0 0 0 0).to_numeric - v2 = %w(0 0 0 0 0 1 1 1 1 1).to_numeric + v1 = Daru::Vector.new(%w(1 1 1 1 1 0 0 0 0 0)) + v2 = Daru::Vector.new(%w(0 0 0 0 0 1 1 1 1 1)) ct = Statsample::Crosstab.new(v1, v2) assert_equal(Matrix[[0, 5], [5, 0]], ct.to_matrix) assert_nothing_raised { ct.summary } From bfcf3e83a38017404865e383331943173fc2d73f Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Mon, 25 May 2015 13:11:35 +0530 Subject: [PATCH 054/119] Excel read/write works with dar --- Gemfile | 4 +- lib/statsample/converters.rb | 12 +++--- lib/statsample/factor/parallelanalysis.rb | 10 +++-- .../reliability/multiscaleanalysis.rb | 3 -- test/test_factor_pa.rb | 8 ++-- test/test_xls.rb | 38 ++++++++++--------- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/Gemfile b/Gemfile index 53e79c8..caf14d0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source "https://www.rubygems.org" gemspec -gem 'daru', :path => "/home/sameer/gitrepos/daru/" -gem 'gsl-nmatrix', :path => "/home/sameer/gitrepos/gsl-nmatrix/" \ No newline at end of file +gem 'daru', :git => "https://github.com/v0dro/daru.git" +gem 'gsl-nmatrix', :git => "https://github.com/v0dro/gsl-nmatrix.git" \ No newline at end of file diff --git a/lib/statsample/converters.rb b/lib/statsample/converters.rb index 02b4c8f..42bf917 100644 --- a/lib/statsample/converters.rb +++ b/lib/statsample/converters.rb @@ -144,12 +144,12 @@ def write(dataset,filename) sheet = book.create_worksheet format = Spreadsheet::Format.new :color => :blue, :weight => :bold - sheet.row(0).concat(dataset.fields.map {|i| i.dup}) # Unfreeze strings + sheet.row(0).concat(dataset.vectors.to_a.map(&:to_s)) # Unfreeze strings sheet.row(0).default_format = format - i=1 - dataset.each_array{|row| - sheet.row(i).concat(row) - i+=1 + i = 1 + dataset.each_row { |row| + sheet.row(i).concat(row.to_a) + i += 1 } book.write(filename) end @@ -215,7 +215,7 @@ def read(filename, opts=Hash.new) if first_row fields = extract_fields(row) - ds = Daru::DataFrame.new({}, order: fields) + ds = Daru::DataFrame.new({}, order: fields.map(&:to_sym)) first_row=false else rowa = process_row(row,empty) diff --git a/lib/statsample/factor/parallelanalysis.rb b/lib/statsample/factor/parallelanalysis.rb index 5cfafa9..8186208 100644 --- a/lib/statsample/factor/parallelanalysis.rb +++ b/lib/statsample/factor/parallelanalysis.rb @@ -22,10 +22,12 @@ module Factor class ParallelAnalysis def self.with_random_data(cases,vars,opts=Hash.new) - require 'ostruct' - ds=OpenStruct.new - ds.vectors=vars.times.map {|i| "v#{i+1}".to_sym} - ds.nrows=cases + # require 'ostruct' + ds= Daru::DataFrame.new({}, + order: vars.times.map {|i| "v#{i+1}".to_sym}, + index: cases ) + # ds.vectors=vars.times.map {|i| "v#{i+1}".to_sym} + # ds.nrows=cases opts=opts.merge({:bootstrap_method=> :random, :no_data=>true}) new(ds, opts) end diff --git a/lib/statsample/reliability/multiscaleanalysis.rb b/lib/statsample/reliability/multiscaleanalysis.rb index 83ebb85..a695277 100644 --- a/lib/statsample/reliability/multiscaleanalysis.rb +++ b/lib/statsample/reliability/multiscaleanalysis.rb @@ -91,8 +91,6 @@ def initialize(opts=Hash.new, &block) # If second parameters is empty, returns the ScaleAnalysis # code. def scale(code, ds=nil, opts=nil) - require 'awesome_print' - # ap @scales if ds.nil? @scales[code] else @@ -100,7 +98,6 @@ def scale(code, ds=nil, opts=nil) @scales_keys.push(code) @scales[code]=ScaleAnalysis.new(ds, opts) end - # ap @scales end # Delete ScaleAnalysis named code def delete_scale(code) diff --git a/test/test_factor_pa.rb b/test/test_factor_pa.rb index 2fe9499..94760e5 100644 --- a/test/test_factor_pa.rb +++ b/test/test_factor_pa.rb @@ -25,14 +25,14 @@ def test_parallelanalysis_with_data vectors = {} variables.times do |i| if i < 5 - Daru::Vector.new( - vectors["v#{i}".to_sym] = samples.times.collect { |nv| + vectors["v#{i}".to_sym] = Daru::Vector.new( + samples.times.collect { |nv| f1[nv] * 5 + f2[nv] * 2 + rng.call } ) else - Daru::Vector.new( - vectors["v#{i}".to_sym] = samples.times.collect { |nv| + vectors["v#{i}".to_sym] = Daru::Vector.new( + samples.times.collect { |nv| f2[nv] * 5 + f1[nv] * 2 + rng.call } ) diff --git a/test/test_xls.rb b/test/test_xls.rb index 0eb5e2d..cb0b7c3 100644 --- a/test/test_xls.rb +++ b/test/test_xls.rb @@ -5,46 +5,48 @@ class StatsampleExcelTestCase < Minitest::Test @ds = Statsample::Excel.read(File.dirname(__FILE__) + '/fixtures/test_xls.xls') end should 'set the number of cases' do - assert_equal(6, @ds.cases) + assert_equal(6, @ds.nrows) end should 'set correct field names' do - assert_equal(%w(id name age city a1), @ds.fields) + assert_equal(Daru::Index.new([:id, :name, :age, :city, :a1]), @ds.vectors) end should 'set a dataset equal to expected' do - id = [1, 2, 3, 4, 5, 6].to_vector(:numeric) - name = %w(Alex Claude Peter Franz George Fernand).to_vector(:object) - age = [20, 23, 25, nil, 5.5, nil].to_vector(:numeric) - city = ['New York', 'London', 'London', 'Paris', 'Tome', nil].to_vector(:object) - a1 = ['a,b', 'b,c', 'a', nil, 'a,b,c', nil].to_vector(:object) - ds_exp = Statsample::Dataset.new({ 'id' => id, 'name' => name, 'age' => age, 'city' => city, 'a1' => a1 }, %w(id name age city a1)) - ds_exp.fields.each{|f| + id = Daru::Vector.new([1, 2, 3, 4, 5, 6]) + name = Daru::Vector.new(%w(Alex Claude Peter Franz George Fernand)) + age = Daru::Vector.new( [20, 23, 25, nil, 5.5, nil]) + city = Daru::Vector.new(['New York', 'London', 'London', 'Paris', 'Tome', nil]) + a1 = Daru::Vector.new(['a,b', 'b,c', 'a', nil, 'a,b,c', nil]) + ds_exp = Daru::DataFrame.new( + { :id => id, :name => name, :age => age, :city => city, :a1 => a1 }, + order: [:id, :name, :age, :city, :a1]) + ds_exp.vectors.each{|f| assert_equal(ds_exp[f], @ds[f]) } assert_equal(ds_exp, @ds) end should 'set to nil empty cells' do - assert_equal(nil, @ds['age'][5]) + assert_equal(nil, @ds[:age][5]) end end context 'Excel writer' do setup do - a = 100.times.map { rand(100) }.to_numeric - b = (['b'] * 100).to_vector - @ds = { 'b' => b, 'a' => a }.to_dataset(%w(b a)) + a = Daru::Vector.new(100.times.map { rand(100) }) + b = Daru::Vector.new((['b'] * 100)) + @ds = Daru::DataFrame.new({ :b => b, :a => a }) tempfile = Tempfile.new('test_write.xls') Statsample::Excel.write(@ds, tempfile.path) @ds2 = Statsample::Excel.read(tempfile.path) end should 'return same fields as original' do - assert_equal(@ds.fields, @ds2.fields) + assert_equal(@ds.vectors, @ds2.vectors) end - should 'return same number of cases as original' do - assert_equal(@ds.cases, @ds2.cases) + should 'return same index as original' do + assert_equal(@ds.index, @ds2.index) end should 'return same cases as original' do i = 0 - @ds2.each_array do |row| - assert_equal(@ds.case_as_array(i), row) + @ds2.each_row do |row| + assert_equal(@ds.row[i], row) i += 1 end end From b409856082b29ef83bdb062193991e7be9fef01b Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Mon, 25 May 2015 15:35:39 +0530 Subject: [PATCH 055/119] minor fix --- test/test_factor.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_factor.rb b/test/test_factor.rb index 4f9a691..969b28e 100644 --- a/test/test_factor.rb +++ b/test/test_factor.rb @@ -151,9 +151,9 @@ def test_kmo_univariate end # Tested with SPSS and R def test_pca - - a = Daru::Vector.new([2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1], dtype: :gsl) - b = Daru::Vector.new([2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9], dtype: :gsl) + dtype = Statsample.has_gsl? ? :gsl : :array + a = Daru::Vector.new([2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1], dtype: dtype) + b = Daru::Vector.new([2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9], dtype: dtype) a = a - a.mean b = b - b.mean ds = Daru::DataFrame.new({ :a => a, :b => b }) From e6b1376e1ba673c12d6b5a5a48125c55c8910308 Mon Sep 17 00:00:00 2001 From: Sameer Deshmukh Date: Wed, 27 May 2015 01:59:26 +0530 Subject: [PATCH 056/119] Mods for making this work with travis travis build should pass now build nm from source minor amend travis wip travis attempt one more travis attempt... mods for travis --- .travis.yml | 31 ++++++++++++++++++++++++------- Gemfile | 3 ++- gsl-nmatrix-1.17.gem | Bin 0 -> 589312 bytes nmatrix-0.1.0.gem | Bin 0 -> 1267200 bytes statsample.gemspec | 2 +- 5 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 gsl-nmatrix-1.17.gem create mode 100644 nmatrix-0.1.0.gem diff --git a/.travis.yml b/.travis.yml index 5f70a8b..d4c70cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,33 @@ language: ruby +env: + - CPLUS_INCLUDE_PATH=/usr/include/atlas C_INCLUDE_PATH=/usr/include/atlas + rvm: - - 1.9.3 - - 2.0 - - 2.1 - - 2.2 + - '1.9.3' + - '2.0' + - '2.1' + - '2.2' + - ruby-head + +matrix: + allow_failures: + - rvm: ruby-head + + fast_finish: + true + +script: "bundle exec rake test" + +install: + - gem install bundler -v '~> 1.6' + - gem install nmatrix-0.1.0.gem + - gem install gsl-nmatrix-1.17.gem + - bundle install -script: - bundle exec rake test - before_install: - sudo apt-get update -qq + - sudo apt-get install -qq libatlas-base-dev - sudo apt-get install -y libgsl0-dev r-base r-base-dev - sudo Rscript -e "install.packages(c('Rserve','irr'),,'http://cran.us.r-project.org')" diff --git a/Gemfile b/Gemfile index caf14d0..eea84bb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,6 @@ source "https://www.rubygems.org" gemspec +gem 'nmatrix' +gem 'gsl-nmatrix' gem 'daru', :git => "https://github.com/v0dro/daru.git" -gem 'gsl-nmatrix', :git => "https://github.com/v0dro/gsl-nmatrix.git" \ No newline at end of file diff --git a/gsl-nmatrix-1.17.gem b/gsl-nmatrix-1.17.gem new file mode 100644 index 0000000000000000000000000000000000000000..91a0d956157a401f6102d25b81c0d2cd1376d230 GIT binary patch literal 589312 zcmeFYLy#^^5U$y_?f%-fZGCN?wr%sYZQHi3)3$lqwsqQ=e`Xf<#@)|5x$iz=5k6bQx|b0MryUTl{6$Gc(nBST)9w zxEW^}gjVPB8gzedv{|Ly+j*5L1GWHKeeIU1W~buBx(;+zx-S7~PTD{IOUK(Gz$qyEV&8o%Gy7XZ4HO zx(eN38Cx|9T{qn>u9Bu{dZnavJAh++3O;OD-StGDHT4_Pu7^>4lTG3HHuh`R?kcd#NWk6pwYSjUtW>rNZsL6ahRJPptUk|7 z&TN>#tOmcLMp~p<#8gbId2Ft4N+`{jomwjmyh0Wy_^1y8FvZLTB(*{uxTLdPX^3 z!@st*Deyn_QqfL;1kKF-$Q{H4gt{RFKF5|&5qEfh_yu+R&cCqa6J|6~J;qyc3h$QH zAVa(7364J_xvk&7L|3Z(imcn>J%-Emg6C-@aQ{(7cY>e?GlSy^_-(@xr{hwlJ9F)BgPe8Qv5 zvj|9}q!50FVHw})02|X@Eq;N+)`6t}((QZoaAlCE8sgNR^M!ygfWbE+U0GFk8UwJO-qM~JLw?TWhLe`_yJL0p&x3t+ ziNW<4PoH(msU&`wG-^_pZaD_K4Hfc*{^Ayw;uf+<`2w2wA5_&2C%n8&Gt7xTMM#`ngDD(H74)utGPTR@ye9U2Dp$nh9)%ZPa&f+BOQf zWVzs#S@a?bGre%i(@rtss7-vJuw&x&@DMAy9eO@4=D4kfAUDedgcdXv$ei7<0IUC) z(Hy<^0`JlWIGj^^aLx{3N!37u@mY}BvV_m2m;r`b?Y=H5v!kSH9;$eG%^|4oAU|!S zpe1cZI4r?Y$XR#8%pT+t1-QvVxPBq*=4T@PrZ_AR*q+v#6z;pTAf{=Rr21L)((WH_nnLM%B+=~IhbW`a<06KpE z*9D0{B&(KbjYmp3Ww8o;L?YwvzmzRnatZKuq`9fkwmpV@>g2uoKSOL;g|JfTpp23` zD46?sVm@zQbK`q%6s!?s@+qa7+i)6XhT*|v`ff7n*toG1Pcx%_Ibvkx2~J3kC0O&7 zba*uVU+~Gx5GUXdE!*+Ic|~`sLD)ybR%meRv9M6_KBl{r?sQRN~7qvWcdOXE=iP8_-yLxvlfVCJQ+? zLsbJTIA9CsZ>CR{`imLRLh_gyZRgaPlb;)@Q=j_+x&80T63I|eIlZAQnl)>;sFM{Q|U8A;iU5Vyelp{0_k zN}0{1e|&Hzvb>30zS3phvKMAuI-|(xEZxw%s=WB=UVSkuq=Hye9onPDxJ-H7LwI`6 zt8B3!qvG@YOUw8scg$|Vq9D8uUSe>K78Tf6^RB4uvBg!~=06bAvzqOSc_mK-Zo!0p z&vtbXLh}$`jHS0KhWEBi`a&OARJ(uV&YASf$5m#ix-@ft?Uq0JG%@6Aje#wiHX3fd zxhJ?mfAJ#D)E7JH*b-TQ98>Pel4mh+&~c#r7UDJuCvhCpb`D3i^<)v2+BDH)&4>y& znJke~b%lP8MA+lp$A$A9a|Mwqi$3r*@@*al;QfjuK_K?J*$L%)<}q?eipaJ{MHtrPqghb=1udI-2js10jWdy zJB7+k1>6oi9b6_LHGrFrEn%{8s5wS`gtUH~H6yk8l6<`|WGTzNw1nD~Nmd1di3Jy@ zS#v>U&x{D)b8w0*5!3sajo=7N7dg6-Wgeui4uEr3sFM~^_uk-SMAQyRwiB7?64qqt zZDhI{nKr`4Ju_d~0D3pvBk}Zf^;a1u4pnLW7f0hPEM@jNTGddGx4@Z46>P-0S__7p zM_^;)wrWko$+Bu^N^!jBx21Awyq9~sB@mWs$#UpHRd0Jr7q}rO!{zb!Q1PoI9Mj#XvG>iy6mN6H?&a_`GhMZzf%Ccqd{>*;o|>Qe^=qX7%L5o4ty)DYO(%WtfuO^I!wSC zr>I#f0+_*noL}C*i(;5R_IdtG=IJ;d2Tyvoi@uE83kh6 zKuh1{Ga2Jwp?@yLo;Jf=&IIre6uw5ep1|Q5vBt|a&P~Q!l#NQ%nEUY)j&SK|;Jrh_ zj|6>!>m*p=nyq^$oELMdJEB7mxU)U?a6_|FC6u!pDjcZIxaSy0l1zGnqp_HMaGa6I zVOJ0Nrg{>cnar`1J%qz8_~8KmBg%r-%S>0#524Vsry0M3=WLD7_2 zrE8BruRh19x;LIZ27f)AhiDN7t@1@CC1WXH)lKh#6{sy-kB9a*u!hZXn0`Fc_dC)V z6)_QsLEmjV_E`cC?)rcoq13z|DOf<&_o_{Soizv>g$EV8MTr!TJ8LbY!`+qk1_zDT znH&jOPq-^reUlh1m&l9Vg1jDc*F|~jK&DLHkvdvTg)OP>5>>ZfoX=!|Hhia%ner%Y z&dy3XJYA6F<;B+Hx=Fovx;le6`vZf3(!`B=v&!Mhv}<*a9ga(rD&6t5MwEKzpx;t--q|@DSTVE}7}< zSixn4(d7YyrqBoLHR~u^6SLNUus3I%&wQX!r*hwF=^>``jB?xWr7>(q6$3fGyd5(r zoy3vl$}22dL|i?Crr>?3B+jjsZI*78!ubR&hX&m3hR8NRbTozt%9j! zUPr{SIEd0!5{k~TpmqmLMxC+h;tz%qa>r8)>3pd}@HYNO=TVm!eW6L?IbI;jhCE$q zlZ{%G6LCyw+}Q*RDOs2*^g^%6i;*Y#8o|L6}xxwk}2+#``uA4rpe} z@wd84>W&$gN=g--C?W+4jX6qKWYEwEi9XGGWb=A^RBG~TR2cH}R45ixc%*|ves(Kd zfz74rS|b~J@zTT3IqWBGT0GW2pRyRA4bLLnQ}??nlibf!XnSvebm3-{xg|})XDfW*L&_QZJ^!Hn*h9*~3$q0azG7Rk zGQE*vRkrvjnspkX=yVgAk%0@V^MO@x>Z&<0C}OB)m1D9|hY8yTa^$T@^b6%q`wIFt z9JYgHOq;=<+c;5EV)8Cxq;(%0hfHyIGDh3 zH(z3$@}oYY{v(3FYz>ZleY_KE%U68)lqGsl|LxRPF6``krl0w4MkeZ&!0S*1BNp-s zdc+YQr*rQ}w1 zDgvK4hqA8>e)Ru=0WI!^(PtmAD22<;Qr~WZnTfj^vb+9X1|n`QHX*_%F_^c^R= zHA>-F0sU)(Hl^!7NM`U#pYo%Xa90f2p%S_I0gS)#|IMD`ME(L=GClblpnmD0_0_b# z>5qrk`4hyCA-;Culi1*JRPRfZbLia_rwU3y^0w6Iy;k1-b|vYap~_bLO1qHUJqa5l79-96V%RRbP%F0=u=~^s&3O^h5h(R% zm;3(^4ER6T&wm8u|1JK*%EHOa!NT%C@t^-V(SIO_gY*CAKmSiWlmEegu6lj_HzYTx zd;Bk7en)7XK0y#9B!ci!%6l{hn`q;A2PB`&zB|IB6yV9Ip|#1hpPC35?7bP!{bMcW z>X&)3U%FcTZWY0s`Ov*}nc;X}j?Ae%g?={hYwwFvuDtqd?O%Ck@ZD~VliG)4-fOK^ zt=_B>`lnJ1KRYqI+o{K55x|3QZM{rB{HJj*6JyArDt zUOs+_@ibE&w$%T3hx zc=m_+Q)rFr@ztPOgXsRp<#UF?p$kIJ!BP}rY9F{{dy8c-dGPvhRmElL5jR(--wKsJ z^L;+mYld^hvt#*(VADr~y%@VE!I@YwLr_pb4^j%oNpTp16M@Wk)g;*5>D1lT5FK+mzOwN#$Qk@k4cdM^#>((AL;n4E^9u=a^ zcI2avbE;|SDJ3H9!r1lZ%iW%fe0^7+KDrp+lpo?{msbakm&^FqAD-`>xBcl(CvhQN zZ+FXBhJZ449u)_F54~;osXW5-yxO0Rd414!oM;ytSNoaY$gJ@!17Caqy}e5@zK+Jz zXJGr7?6uA&{D_VP(qJ%(&|tJ*IFo;VSHKXj;db%--PjMIf$kq05B;CO|H_fo8qD!o zPt1StbNY&o|E=#)H+-p4zG9abIaOWU{M;Duqm!#Ii=O?nvnF!(n#U}Api@9F$j#3l zfKB`nt-dE`kw{H|O>+Nc6e$>)+}|cKmoM(2VHk~0oKb=)0!gv(EcxE1vFGqpfHX{n zAn~}$7-=#^2sP^o7k?_R%o0n>?zm!OUF2p1O-Jl~0HW9i{x_1;hn|!;g>C)!{T_c$C?xzBJok#Tqd4(c$_mRPi$%rbg)`T43pD|+e}C2Mu|=*$oy4tsqk zBOA<#xnXa+tZZ0JexFPJU#D&uJ>xSQ1ZXT?Y&f{Qp*|ip93@f&P&Oe^l+a=jw$=7; z$VFDG=CqMSgHslx4lMB`gfZ&tQor-a_Vid*`oc7_D8@Gk zH=w)?!ArhSHZ<>cqiKy(X$Ham2pnPHDe$v~@?#noN}~ZyM>32Q52nUjIl|*xC5Ap8 zsvB(!H_Lnd5JhAD1-5Nx)>d(AV3^VN63QajT>W23NPdGz%Ot(2WD&tJ#2?q{Ih`AluROtOf*Smo(%%_!mL* zgg71!0u_sc{OuYxyA??w6W(B+r)+l^Rc6r?B9OFn`)*;&BWkr4wyXL9nK|3#!+Kdnm}G3=rRncZ33fdg($U^jjxDDc2$}-yK0@Y2ytW z`JktOY)P@9ZVzNhK9kx;1Syl7k9@N6U4o=(56};Og67FG%QRMZXM!h3xnL}gbqYYT zQ*q*;WvBgaNYOW{aEV_-VKC_uxNvhJ8bsLFUc;)xNmMLocc#{)BZWtZ0L^Pq2JM01 zj-}kvsPT#nfL~1KLOLupbiQ~~5)@`^DWbG;#We?qU&yPu$GPYcbH!QI!`Ep%C1DGO zv_75}L;fh2u|_Q;f$5@4N8oiS4+sX4X7RRN2&Zu3x6u#c^pqYqH6#sa?H-l|_wONa z;);mey8v@Fh7`^^?Re^dtL$Q*h|d!Z8(}3y8rpLX%0I0}<4B@ZTr0JNB869<{%JPnAf@?ipKqGQliNTdHDaJbMlQTzZCI5?c4eYPbsP2KHU`#?KK`S(Tf-0pKnW@y>*PK1z~nNb(IJ(YT;eTh-auzkdgBT$ZsqYv zY!_KkrXXq25c6*WEqTk@lBdfcemI*1rNqXzm`>EW3bax#czj-_!C8tI2WSXe{IbT9 zLZe$%$OW<1`?FNw zc{(15qmJS4U}WLuJoP!8uv?i~BZJ5=)Y2`~R!!iIS%GB2*7;BcN~n7os_}_Qd#_d_ z>}C}b_fHsS_ejKCM~2XpP_() z_`$W(DK#dzE3_D%*UI;jF92^E$0%s}l~g)y!n;f#sSsfGI*T*UB8+(=EBXAuP$FE(_rJj4-92hc(tYUqYFFtDSb zd7kY~;KOD~V_V-bay4Y^o3hG{w5tY=<)b&K#rcSgBH;OrV2yDB#3;UeiD4W$S)pv& z?^BMmwcs-(lSmElH1OiChFr`r|4}olrOpT`h`uc#wF49ZP!YRzc5DfE)S`SNEBLl0jZu+6=B6}~OY}yFNWM++ zM=9oZ_C{t&aR*puHK_>KR7Y*A9QIu~mkub2Kuc9AFrw3>(V~5AAs9|4umfw=N62I{ z7FpV^IH{E_P?xfL@;FH4=~WsAQ;qP$$wcAK1~uqZGLo(Q0$@sFeSoB96DGWc$N*at z^`Imb&R8n|#4Z^E~q77)E zK};-1GfELGO2pQxDjJMHh4EJ7S0{;#iKF5R!74XI$l(;W7O^IMniU}>-YW_y2mzV7 zJM#KQtAO8tr9h`F8CT+OOgzw13zS={gT+)DgtRLwA#cDDB*$_TIsh9P{w>nRIry5A zFo#lao}Cu`BI^P}wq7BSPxl?V0;}Td>vXy1!ILH3N{*&q=qA(@smqZtV6N$ovGEw$ zBwWQ2@JAVQ{4%0+ETKPnjuV`T?U;hn7#FTYvYC$ss$OK99yS?5A0HD`IE5 zCb2*bfMrSKV|K9we0xwK?(akv2S)L>v4q-=nm@FC=qt+I!#>#9?c6#sMIJiNl zJ%&Nk5~^YT%brz`uBq!_5-cusS9hfEFea-XR6vsq2lH&Y3VnP2wW66%`K!C#LHwL;KkK%BQ5;IT>Tt*3zjKNMi$hKx1vf~_71;j0F z1}xz=G(bHo@gRX+;v&|3cN3IuXfkFrjg;MTnoGeQ82A8=a(ZI9sdS;%p^Op0(k`IJ zK(KS?l(?w1L^GRIrE$T}kCc-tk7?zl6%9j9Aqa@XxZg%=|Nb3G`3-B?Yjgx%ped7; z7ZY!`8v=(2F7cz%Ytkgc9IHJk$@t4xQ*92JWYEJZqSS)3G0c{6Whcc4&-yRI?&1`+f%O} z%iK7G(GW^n$;|X*O$Ms~jiQ9yN#DqBvf?g)e7uK}k3ie7)03=X#-p|$^woftP1%@L zyliN34L)N~g+8%1emd$tQv4>VqxA&o7qkl}^aVj8N8DIzQ*A(-9a?J;1UV^8brQyS zDa7&q308Koz|IcYfb@+A4I*e~J|VT1j)E?pzU*+Q*G*;EXH2=LtDh6=rEm~2+n%CM z@b%*y*&Ft%VGCjx0djc&jReqw;!Ta!TpMum)u~s>ASQ_$T4S+F@P5D|kZc^YwQly< z9}*Ku1T2!K5wX)@^Ppw}V-=t$eqmy;@g?xG>S+wH9#IOn=9!l5KOtPuLBjOFdAJHb z3rdnT@3u`s!EB3gGO$ceNFR#jp)wz=<*{m?u{LMUZxQkq1&Vbo*(OFS=j;5faI;sr zo~`)@zgU<^aRF2)Y7H>wG6*Zl)HFJaCon?+$7n#gvwONTnWDSUVij64a(#lwT@I+G z`5v+N)96uIl$iy>BTYmYrj$3_wR4|SZ(8jSmSMK1GV5sLE8^){4k34jwT$iW5ZPgl zmgQ+y$YawLY|NgBYOzf(B(S3({;kX~46^x>Mhk#PkzceD;bj$#cL0gG=82{xNxCm0 zD1_ptH7w=O4MmNxH#HR<%qA4F1~OO6#vfr_xNN0Rj=0MjQ{!eeH$%($HBmB@SWBVO zg@Sf?kX@TXoXra%T9=oV&OynHrEO(US_0TwQ>@i#0uOi-%&*Mqf;i4p4>)0q)o_1n zCgl)YXAe&zqz*H{r^Kf(X+^d1IsfFu=G=g#l%?#-R3+tfQgh(;ge%FKLWPiWLNsh+{EVUGN}cN75a@5!;o?5hoomB5%{eOn(-$Fe&W9KuqTH#!ga5Mor z3t&i3HWU6($9+Ms45pC$S5B(IN#T?V5wM3>W-O_r+m$v#eqKMQf@axvcMJ2EWJ zG=+4GJuXMmy&(EI1eY&U6-44N7t|E2p!KJNKye~%OcV&@J0`S5Bv(G-ZkuFJysUgi zp4TUX>v_52UHnqI(FgBhyl&83R8f*1WS$`d1shZl@gCYM==cSAOB4sYXtL%mT$UIq zU${q}Cr?W9+Y(2T0`_c=cEntP#pbWsPvf2*ZA9^DnDribV9zAp!av>CzWb&J(u36 zyI>uy#i~Jk!!3uXXA&T&jOQkytI-L6YF_=Dpc#wjT06p;!dV-HA>NXrQQ zR=L z1O9@WoKM4}AL%H`96ShUe2HlU+*Dr}h{l*wt$&d}rze;yIg3&bv+a^(HslXwv1cR& z6S!mOh+GPG@@>mdIKeC#?y`a*T)Wwi=yJrYUdL64^Z#8r(cV#Lr1CX%!I*+oy+QBr zEzp&aVS!7ql)1(*q6qxnr(ItMUY^SpqdS?-q{CE_R_Ys-5noz^D7)J5n|FhQfzrP$ zk6uONUKdz|WjK3-{u%Xg!b3msq^59RZYi=|n2rSHJoO}p=qP}*oDgR|*9>a{^@*Wh zIGm|W9NFTKh!z=t0S$JFCMOTdyCB`;5iVtAp1d9_6w{u4Kl#dM;=S73r>IrMd$7b9wvMzV$2E1Sxe2N z5g$&ac!hv`MmbV&)e8VK!56kwAkQO=tB737Pip4@L75;)3bE0`WyIY0wXhQ*3uDt0 zB0{F8#?w>E*AhIeqjo-OI+K~umN(U1Ce2(a$FnS>&>cLCPuL2kKBPdia1dceM;trs zna5gS{$B@8Ugi#?Uj8ZUg=m_k0-w`yPQq=8-*04hH4z4-IB30ual|>H(TE&ffAm`& z!fk1$cimMqSTCKn3ymmM${LPOT0`?uh{S-qtj{WcS?OlijG3ECjjd#Xh@~ha_ZT_r zsY2>rV4W3cL4uD?lMTHM# zNEGnNeGbo?py+F(uqNMuy+zw#(g-3wo0RKQ_}dtwfZT0|$|Wky=0Q(rb^V?I<%2UO zuwR-J4Lg%nAhTZ38L!HPWQVhR+r0cIE?{teMESDAYsDt=+e%i~ok`y#`?P3VwXUgt zSf+gi4V&uVwPoFRo7U)$J-kcFrRdnY8f3y{(2kDKlTPlR4qLam%`d@a_a_B*CZ@uN zlLal=9FE;VjCMb6d%fq8%_R+~?*5D2wnCv^KXmvOz5~1VZ3hp1)zGutO}jq9>*%HE z=3ljpop(1PmugD7oZVmU)#V~WJ1qug(5*{XBJCc-o9;hP2UWx#M9_b)deC=U`Raj1 zD`ipILb~oc0yuq8ZtZi0V+^n3(jCA#R2^o_ze%jgYbJ1^Zw zQ!#euvpl%9tpR-!Jzwed`}Ydu@*sgGnD%pdZ6H6~g8|h#GK-DGLE!x8r=3vyb|H^XgWO&W%J{V5 ztiWiB=uFhE%!k8}y0dqf&r3hATYw+$vMcPur`Z(66yD}W}6cSdikp%cc^XRNMdeO6&ya35&9#-=8t zDQ(>6d7!0GV&(W_sSJWd` zrD`efXt{RS(NfKtECjfg*~jjr3g(NvS5_758x#x!C~a-fb&f#}`caQ`4fAB``Zds2 zk>w=rheC)P6AH8bD9{UD1$^Ghu%&ChmarsVB6)o;2&ebRh**#*BL&DL2|shpQlRg%~EOQIFOV%R*jfuxWG9bo-S9A zu|L_8ksR9cX1z8R&1Q7YC%1~{OciVKubY1g>~?DTg@8w}PNXtc#OW@d=WL8GX!J3^ zs!Lo&A722?W%}S~=Kzat-fh$wqn5pV<#>LV=RkljoX3a%HQ4m_OHRpvrY+~TC6*fxd9=XoQlhd4>W2D>^# z0<%yklv5JkN2Z9mLgl@9`ima`uzK7UH8p{c*-{185W@(a|D)&*Pvd35ho*|)+6!W* zX>$DFymNZNi$n}9h?wEZdLSro&XzvPWLp>|e}M>rd-wphXEb$&H;zS7()~K2poH_7 z3{)fhAqu`Ln_abyh$^&;i)yrg{*AEw&n|Lt_2KOJ0^82_WmQz1C&|4!gf z6-cJPPF7$!ScAg>u##+fBa9zhWZC^GiE(bAgWqIFr}4V^!^ z9>+n}bEA(xG4GP)xmLlAY4>yAEMrzZHA>k=-#GFu$ka z!0eUt~2*cpZm-m#&W&wBsUL0Ubl6X`looSMG^* zQXK13`J^j7K&19>Tk9Vt9fu&l6uSCVXr0t+sxSDJ5S_DeOb6||#(HYa2^cjpR<=N0 zanQ8iztqWd^-Hzsq!A7v#rP1;z_{IH6ryFe7M9fz4ml{X+oq^)keHui(lc*$esrz1Dc$5-`PF<$GgTW4f&!HS<%9pE9 zX5{4WJPYtp2!i_6d1MpEn+?_z$@*Q6_ zc6rR3Nt%bBlt0^x3vzeO%=Cek7WNvN!$G+XGB%EVdS=hEBE@{2aQ7Xl<*(0H9}UZb zV*aP3baD|#QN+#y5nZuA21<5Lq+6yC=dWh%GVR}yT1@6Y{Oht0zp6P_z=thG0RM7W zhQYM6Jcp#NnyuN=fw%&+@B4*0YzWK4dG{<6N^n+Jx&4BaC6O+vW)*wFXr@Q)=2gh| zix(X&YC?rwgp)1|FU~&v7?7~pk}Qz*9i9zsg{q?xU=Kt1g&49{7Eo37x7Q7aAYpuP zqB><(Rxo?T%*LdNtf7NcNRBiiDs(srZJiKy#OY+4>02tgJ=TREI4xsO;DKe@GEg(? zOYoy(DoT@d{48{HFP9P$UK*L_8!abtP>E2-7?EbkGg&3b z)cLHw+y#?rEy_3?QMMt7DPwXEEqeSH3HbO~lCR7d==O9O_H!``RB6-5;tAYov<<#@ zF^mY4Wc;R4{ZqerGgMF(5|jbEMqwZ=ByWezONpWWzcevq-&*=aNgwf3tYd>qs&sF$ zK$gIF4(6rg78^sj*I0|EwIU`8>9m7FqsHcoP?B{n`yfVvaxH8a3SCejj)C8M{n@Ui z$;Gor$LvI|Oie`%3kBf6>D4gpW0N{KqHVj~uw}hYlp|#My=Bs7x!E$&Z+^!N7=co4 zEyv~D@02Vw++xRJBWmjp&_()i+-5R?XZKA|ruL`jSl>-Hd>&QD&RV-#!p+4smX~q- zZGRE2t49yog1%~PVfp7!+A=&AkWL#UZoZV>XXl`CLVmcQN0UDq(9`>b>x7?xYavH{ z4hwTSQ?-Dz$<2|8B$SdHp-xju?$r?Z5)fLhm66btv0M)pn2Zhx0kV=_4b`YMu zJFezG(;nr*-xk5ub1`O_;|iFVZcJyHoAiq%@z^)X2}bE>e*7BMkz_N>V1{Db&mcxG zU$l|iy5G7NP9apBi)JPxUdegWKv0(W?s@EW~DB|V4rd+NGQsxq$ejZROPF> zJGgbI`ilZ4BIQhJ@6nU+4~#+KDdai$br#Pif9Gg3vl52k&cZM@20Qf-8FM0i6$+hx zsdyE|nje*e6u`=h$Z$nZCq{}~Q6UNd$ZsBWRU$WrD9F=$TOH9S{_XFI-jjVkO6`JL ze+_7*K;(h+JDT6Yd-9qq$j#urQK)e!!&LAP>49LTH3YnQQKvpaNSswACH^;a7DDj7 z5@_1QeWi@)6XPLl2EjN0|VqK_?@Ni~AFeAcPBJ(&H6}hMEG63*%Sm4z&gMpz3My7AZ;P z=JK+JQZngcDI#AV_&J&H)doZlp9mKpUxO){4P;i7` z0Yvah@N_3W6M2X!F8bg4Gz(-~j>wD^xKAg*ydp!x3c&!4)o zW|bcZoG}1IS_3EF7Bh95=~fikeWh?$A-fD^khh-6WdRo?Wwv7>VFtm7h|K%0%(TXd zh)RN97mmwf!A-+-e>H~@8~J7qxk6lSKVIQkoGGS}f!auWuV2*(dv&U^=eKr}MkY5& zD(0;t{2drck+1?U1jxn;x#i@evXi*-R$40+maD1?6**ER=rh}m7Jc)`Fx)>YS7hT{ zUK_9&c|Ket#k%)8?(RjcDEf{OSJH{7&^NRO@@zk!xyqLjTS8Qd!yWsRp{+y3O!4rV zNHvfb$z+FJ53VODv`#J|BSQ+hO^Qz#J)mIe`-)FkcD2`7@LYf&h)I|-Y)>*9ty zUwuq3&Q#S!pDtCZOVG>>fodAv*9|ykkv8LwP8Dkp@r}UPvbUycY{Qco4Vk;Tq-2wx z7WJ`Ug=bqT?!>$}th^pfIU~N~`lp-}3*vC7qAVvWZw(c=jIbCXpQd9~IXs26Z-Kd9 zDhI?jd=<3IE1jTD?n@LONGd{kRvd2+h;?!mci+|el3!bp-UXPCt0Z^cLOT(XZ6*@g zNr#vj2YhQC*^3IM)ojhxG-PDau?hW(>`LCGPVAIEku>q5un7RrnfpE-;5f@7dL%zT zE}eUt#XUPYFV1BWbx38@)%`Y#D2$@jfng+?05NPi>>L26(v}nvWhHoBFrwhFH=|F+ zXOIy~Rf|6-MQcToD_J2qcp^$5tt0qce#n7@4P~#QFO6-lH|5UmS#`&K6^rJYW<^VW zk1NCBx8{f{zbdIG+SP7sN(USdIjaBU#_vK z;v?GRyP9!Mpq-aID-CyJD7{f91PX%|XkN5)p|G*uDKazC3v4eGC;7Xvrz(ugJF%h0 z?4L(+Ak3girZA33?stUKbS{v&0e@4 z2G$zO1ohmK6iSugT$B<$Gn>7~R@WCTW`Dzwwq{YMG(5`G@Vc~KrsZ+#9BneMor&e3 zyjWg(7H}kt^>_jvIJT1}3u{iEXyNJ(!P=8;TRODpqe<#`&25OaPf2Y4|fZmt4k<BMXp zd}j;Jicp;n&tev`e6#L7Oj%Yl_U7=XC~`&_yq3)b;QLG%54i_YoM}c4?860foCQ1@ z2c!mow=q=rc0n0M#ulGIPCDw8Rv{1pcHr0)L>FO(ftv_o{an*#F;hON&-hEkRa`U! ziC!9d-rZR25-C4NktzXs(2_s!G`vcffs&?vr-FGRjF?6t?mfCB=qR^puNPH)OcF_a zQ2iCXDg3USB9~RBB%%a^m@%UE>Kg~;x5Iw}HX@_76XZo)s#?h7Ua2+LLNlL^M}{y; zd&n8-*#5TQlJii4Ef*59Tlu3dQI>?JPbrcfNFkKPC;NWV&TfhXBA2i0Y`6!D(F-5T zU^)2F?SRbnf|~u-)I?umMBg9DD_0O`u!1<84-okzuIVfIUdRzbI2asY(Kg>jgmtHW zb&5@DaIp4Uco7CXtht~Fu!)sjeD-ZCKTLSiPIXS}(eX!@a0qB@ON*b|!7(g5Vx>+9 zSxjC$@_wnQp$BydpOB2)qp8;-W&}rsK$s{q-ozyp52c;4!{02`5!67hkoAs@Hz;fi%|^qW?RSd zU9(*T21Z7Jmya+?dj7Fuc_9l};_p5yFETD&^_5AV>WB*$(ZY)AKSE+N zC2%f5#OHEC_D@BqLWlzz3rc#Vp=RR3V5?`0wLBt6%!5C`42GlffKX$jA<#!)=Ppc~ z%OKd-P!$$&ho&m_b}s%&1buP60oA8SW)PK`(C8=$(;41F>9T|dO;Fh}fjbJr#p0KS zD@)&z_JRH}JQ$8Mf zO1UAha`<9nqb)Q4W(2QV)=h&6eH6-fd|PA1L1?{ba;7Yu97nDoETyL|`d=?SO=y*s zS!>)~K8fQgH2?}!wBQ|^Qf@JKXc%f$RXDgrsGa8|fXp0%&-?T0eH~neyS~#aqg(q= zOuky<$szp_ns4`CS=SaeQ3$7gcL&Rx8R!or>fF}<2Ae=+zt2{A{4pPm34gRI zdE#)u+wTfelF0n<4ZfUtwrbui5!woNDSiY%g3r3kg%~Jvp z59v*)0}QjeX%c1MQ6#t5dqc@k)W{Z1!w+xDo?-w78I`oJa5%Lod3KfEor}G` zalg*He@6TCb?XO=K)9Z@E7sBmu*IQAHO!M4HFURk26 zQNZok7>%~C6u>CF%b2f~S7q#aU~B;iGtt#kuBYfvFJXC{&p^?AWaVB+Vb06~A&R!gtXxO)f!s7f$Z0$7 z+GtED@)X`0V_W03CSvE_L9_tsRHY`kp?MORfW#o(&CJoB9f%2HCuObTU7 zF<->f4xtK>x7l1r@|UWll&ORdm?AQwLw74V^A(n|UO`aDes9&~KCxSXDmdAYSRJ?&fuJPH+*)a5W#OhcQ z|I~;{uWc9i>wec5GY`lAsZ@;GK`#D}xnDVWjQ{gF{-ho2DQ=gAjlDe-FQ*vt#Sf-W zHGBeUui=Au>apqZX-PDuP9$D_BX(3Vc%LnYWFDKa4R*e|79(or5{bf&L4Te=+K>wkhf_kM0QMrY?$&D6`=moG3^I zoRAKv8sC}$bAi{;;59sQk0e>P1qvy)#>F}2H7cY|bh;ci#0GkuS`4)UGUgetpl^YS zynv30xlgud3L_dubcd#G^Ep(!u-oT&)D!@hD~c}!#(RzM(Sl~!fFrpB#>;iQIr}(I zEK&wx3h&f?N))fnUxtL&w4HZ4q3lj7~d`t7doHi@rSc(TnMiz%VTdEY@gpSZI57L~Q;JLs6Z{Qi?OCo39R#GFxyzfG z_&THneJm3WtJni7LQTqI-}*C8y!COm9+~x@iT?w({@x0}NBF-{TZ{i`9@O_A`Tytm zQ?VRQ00wljQH%$;w_5n>r=N9w4-X0>N07SZFC{S;oV3qcgF#W#RN1FWX?VJxv?smt z29Xusq`nU+4o2`)(>NkV&}e`vdTP%XHu~z*)$CLTNb1m@Qv3j!QxENExhOQ`<}zp_ zDF^o=UOYPjN*z}#A6mFck2(wP;{C5M0vRCC7fawKw(erg>gn2to$k^T|IIyB*Tw%g z-&RG>@shT4G)>I-yL zeL;v*$*i0rJc4lI!PlMLUFg!!qta-xD9wJ^;f_fy6~1HSwnFD?PhD$Yyg3M6V*%RQ;!UX%L&CYkd-?xpA(f`UpB~SlN^D+MO=UIOYkUw)$ zXYaLhE3{^KQ|fol&5dL0tA*rXQBlhUmPxhF4yJpwRhpc7 zMuTWk`|hH5+U{PU5WYfrTogFU417Y>A$=qYM1EJ}XdFqV<5_?nVGF#<;bU*s$*{0)m>$fJoqwxS)e6h_0u@H!p_M9$<_wQVR-Es5&x z$j2bcyPJwa!A-?#Sd8=UZf$W>K}a|Bx++?ztG4;4Z2ZYc+Zz4{!`}t}59;;p`u~1y z4gU{}NBsXR_W!@`Eklr@9B@?3# zFx%Xv7d1N5`m0N6jyE=p%y2mq`Dx+_NFXTi zY;MnE#(B(#@|cvTy1kw%)uWLi#|Htt5YxSq*ty#&lAb;xYr)KpEU2fVSI{%)ZbrV9 zl=wA9S~}cCA2d*r+fasFPHrmoAGJFP< zbALSFM90If(WJ2Rl`T^L{R6L9r{-0$_cZ_4=~pxNYGz)m#&uEM9SP%o7S94&63Cjs z{z(A*-jdiyd4aaCFcK^s?fhpkh?xEl=NA9-+jD@A(0`Es>N)yfuRiMkUk?4ZvSjgn zMK=tT=M0nnt>V8XrI?grR8S&5NcgHn z?FiC`ne-|VMD19$k_6l<#P7fteDdTkiA`yS4m_k;5=YqON>N*gV-5ak?jHS_B)}~G zkG;S(e{%xxQT#X7@W1wm|6dIL%lNM(0BD-4P%eGng$&|Q)X?1I2vWdw_8`WTA2TUJ zxVVK=a7yAwRu`S3z$h7E;m2Yz&5GB{j{G^a?}8jMhs=;L&Qbt`-vWHZPfCW6oBQ%M zj<3cXMg+=zH6?)pIx#L%gMViG58*$ZKr()70`L+1$6%Lv{IBmn?*D#i_z!57erA-@ zwEH}#2i6Q+`&wJ&KGaI0UK0D)$Yt+!gtx(X>_>$a^4=5K)K2=)|dO16hYW$rQi{f7;o zKk#=|-U^p)HUp-_<<~!d;O|)eUJviA1y9jE{J+w)R7U#7Qx$)P8o9^^t{+oBq)UC^ zQgVc;EY#Qg#R`Qks&SR;wfuG;e~yd%^`SC<%@x`X7goG%+2z93m9zDMdSS)AUwz0l zn0&rZXIZbAAx5qMb@Jgl|GJ^le-+AT+VEsj!Q_RD6aE^fY{e=$4OHCvpk`&Ps#{-z zK%x^@fCbi)9=Aar_kR-pZ+>R}U#YI`|JUmW<|F_A^7#M%fafJ4#8ppGgpJRX$9 z^O88cMl0ic6CU?#6eR9COWHu+zbzai(3$EXk#|s@>s)nw-DoUV#zBBytJ4eM#T+9EV`{^ zA*|h@_K5jg@Lxt`{R8%Y5BBrtKkJ71c>e2)vHy1fLCs1<>l|OSPELC8_FU^6bz9Bv z;87)(F&Kyn{MBecWdrupE^2{7a105?*bTJ~<9%Lv{kHrB#DgnmyZ`!rtZcEEmqOI% zVqOYKKHT4Lq&1zAh%3F@R#tnW42nAO&AbsV?eN>FA6Bh2BqTFe>XQ-HZPKPZ|0U2N^+258WR2wqgt687oWb}n>t0d;#jG;m+t&C zJx&wGEr+_oS+s*7-{180dW9nx6b+6_nDIJ&d|i_Gx`p?s#Um@dNBavh)tz!*{2gjVt%~otivKYA5AR9%oe023@xPw8|J17c zl}G&lQt*GGaOFgX{9(Q*eir|{M;_=*1fn}S_#!v2=q5hc^U#*>2X)EQf6+8N#)aYj zH9x>(_%B7RqK(2B8xabb>(ck)nyo3zxl6(W5ez#xk$f2R$xz2z6$=yc5;Q>mG%An* zk}=1?qs?l_7t~V8STMy{@A2Y@k_a#>1m%uGXZ&0#86~q+{RQKgp=)ddC2sK|t0B}_)E}jG;bLeT#d@CuY!yUm zdE7Sj|XR$Cuq{J@m>Q5!`PuWeL#j=#NofAND%NNQi~BkP8KLOpgqNmozr)NT~)|; z?Wq-KiPP~EKPUk{;J+1#CqGXXzlhThJIcpQ_T!De1Yvf<9atQ?E-9BnF7QDs{SbOt2L@(ObHA8m*na>y{!IIS3jaAS^yifSKmg3^ zfBV(P{r`Up{0F=;31#uO%)h%anpf#}7k)7vgbxeZe+9I`GW`48dEAWu67gRy^6<0a zf33Er|J4ubkNE%Z%l}84{kYM+uc(bTe!P%<49P095TI(<`TTT!5OzQb%X|A+ z%$8h!7^+{B9^~~w3z;2W3-pz!mo4+dZ!nGXV3l<2{Xd>Ut>OP<;X1#u0`L+1uh!S% zKkQc??Z1Dd{U=d013O%Ru5Ggc=)@WZtMa7{`lUE`Ml*Yk7xr*mDM)l}v;#cJ8-p=eHYS`0>>Pj1&1X=v}Ib(IA)o#SqOP?TG1_DKbL=A@)oHQ`l_#EaL@3T@t zy(fY1MZ$G3_$TLUZOn%o1*u#S)JFCq*NGmg?_uC9L9gKd)^7@=P$)Wy-N?e9f(Wg6 zAW#P#b7 z1x>vH)ktDYb~I?q-7{>%&;r>k%Q~BKq|CGE27p7PLeeTr23a!A&e6ecg*~pqKcEAj ziKktA+z{t&QN3|X;&ou#UV}!62~WSU_%ZA9pSlC;w-W+CcK&Z@JL~_J1PR9C~h7;U6lrEB@~>{7*R@E zWE()nLZ@{ny3C2c*ljv_{Q;EPJD@-)e7raG!zvJziV4K;pl;&RHQnFlOdNa6_h(#y z{Bmo{z*f|)nR*Mw(4PBubaP0y5K`Yv!7$%Vl-WKM^Qq{pzMcw#(T3E}9&M^2+?eV| z8&a)cw7DDnftShL^mTJT`NIRnHV*__+c*~f@K86xEydu&gT1HO*svtC&1*8-yePBH zt1{cXESu|&W}CXg3ozR{#}9mYc>1>##4isN+%hPh=&gN;hi2>%>Chdz{?VrAk?;Mu zoLUjTsTj!cQvRTQFtBAoXfQ%_0o+S^4G?;&&-(CLYe(aflgt2Lj z|GTlg`MLPNQ9oGc|K?--&o9va^XWF94VIi=VFr3M`}}rhpZ|fOMp}LTbyLj~RCzSl zG1e5>MA5@D{s(o+X0ByBsHgJ%DlHJ3gog9#ofeq%!Cd>0Q~69%1knXtK5SL|%vkZ; z=r*?vXTA(gaGm}y=bzvI$5`|Kemwv6@9zJzJ49usTt}&=;?PbUz*fekXYAa@@0Qqo znfL|mrTv1a97%xmn34#oiuj$~v{MqCm6Gky_mN$<(^8%FXZJu@!+$>!4I1xflb`MArQBmXgRi@W?g<|wdC^470bt5)thUjyjY|5 zJK%q4M_)kvuLJXO|Nq~C|1-O$aK81-Ta{dWVh$cicI?VGYcAidDLr&s%`Fb=1j!i|8e)k%l|7N6%UzjI9Z0(z^BV@ z>!gh>P`x{vJn^)t1h$zefeCz#=+>ia$24p@Q$7esxSgxvNM&*ujUiEG3hYT>hcit( zZ^Ee0Tb(P&`F;nlU^&4X2Jy;0AYR9aVwM+$_@Nm_f$#b5iZ1D+H$+2c1YhH`Ui7!q zjC|J_QNGS{G`FolT&k=vdfN(@0bT2dhKIi6+G{nm-Gv=E(1j})4wdRyOOVmhDVG6R zDngZ#tKtm?Qn#r=G>lZ`>fr6mrHK<`|UTBtYrp!*6bXA*BcO6 zH9P&mDfMnG^{Ck;hRn3?%;K^N4-&f90L9*%i% zdT~X)#^ZM9t$wSUN|y8f-b|*o+3Q2a7f^rmY;e_SbF#$|S*TQD>l2Cv8~z zW*>XWRpkZ`Ndr}mW(yO}uTn3U-HU$ewbQ!dyxA3PcUl9OkE`SURhK%`efR39)qTxt zeA*qrinp(5L7JV{ICJN@Dgb0Uac&TvIxX33=j!;Z)l8>l%EhDR?>J6aJq7~q`}&tm z1^v^D^BzuQ_X5TTOM#0QPkAkw^7D4*s!xMckRpNjZL9Si6rlX|B!9beOz3fPaY4O# z2hhy*oVDbyDz%@K>NKzV1X3#7NfUcWfZ2hHWQl{M#N|bshud%cz|S(z^AxZi^RfV{ zx2Wq^JwU9pGoIq(W(SrBa4AW;`YwLS%+~i;%`VSvs-eSlXRbQ0B&dlUcsXvxZwiiD z{VpsdW#(o);YKCtGEDYGw@tZ@ z`d8G^Qy_u7(14ql7jIkLxL}H~N-EduBZ{40^e&-TiwD~6%8>#A0m^&c>Kb=hmjZJU`z@3pw~i|<;!I7^B|^!n|yBU*HzogIGofy$mTKq6qm(!V-u zM8B`wubVvM_?sOJLW9J&vv$l97}q)%49my#d(t}Z z(?hf0zsOhfU#(02`0nBu9)LjLPMd2v9tbK+jgQLXDRwDBkyrz-Z&7Jm_;lN!QKSTG zJO=5gup>}ND?j;}jYq(6fb#qzC84x*MWINbWX$Q`8@gULu9NflP#y2O#jQEAwN!gy z99TrW|H>R+i?2co1O{2*v-A{op1m~Uow2prARkJb0_EMOKRp$ZFF>e{u0iZgj|{8J z6^A5}PoBv+P!cBXui;FFGyiT7E{8aq1;-ng#2x0mQ*{`0c>*S^4JSJqESk&|yk67L z)bhls+b1z@(|KmZ)3~8s@yQdJs_~_fpG%%imu3@3FK6>EH=B1`XY)=U`uByW|9n~! zPs>j^W%K;9yA)5aBWGB~SpZGNK|iiaF6opY& zE@r~x+>#lDqF3$YGVkQ(9==1iUnD-jXhut0bU+KbKlO6DNh(K2Y1s3u6pQt}g%XBYC`ranpd7GNX zVPIP~+`XUT?qPmz3|=Upkpi`hE+Z)P($|Ex@hr&bp)r+(Au4s2Pr3~TE#UlvN88w+ z*!~v<=pOTV&;OZ~HUIB@qw;9~|6=z4wwm5JzEay0_y$zni8x#wP^ql(#VzCfp;u@myCt1$8ddyGNk$Ymzff&mDJ(TunCDq>ey%NLO7T0tl% zv2MylEgC@G7>8SaE(=+>DE7qY1%9Xds@3;rx$kknyEz%87&AoVxO5846)oxrh#u!q zy1^0WR1x$q|H?+wW_V2!4qi*3;^$qdkNnA6Cf}0TzznjiOw8nR+$G&$s2daYoi9hG zLGQ(7k}knuHx^RGU@p?dHm^FFPvcc51McIhudVv%!DSzP8SB1r)gSrcpO*jA_3QAj z>wo_HfA`nof7KuF|NV0K|AXJzZ|9)owAER1ZyZ`IpQU3oo!GBHgH$1<1kH@v^MGW(< z#1Dy2_y4C*-7||UFU1#1#5m8!6u`D&2#AbkIxAX+TM=4SF6+rkVUwraB#1keiFmeG z<|mSPvhAb_0UC@VRY(fOFcgp~Q*jocsiNwB1-ier*EAtS==6yn+*!dGJ^t4FUim%x zzJn*{E7;G8`M|6muc80WWb(P?Kf_#)|6eyA@BjGEr~f$unx^^yhY8XD z7bt%Hvl4vSVMUhi{{=E%{{pCddeR>t@(*WA(fE8uMW>O-Y$5OXX$y77Pmjd?cccHa z=nuO8&DgK4-T!3P9_jy=LjUveUlf@^E9lThRT-3Id#t^F&`iJ@HPfFbm~~?=Jc_Jx zK~p_CK;uot!UrlCYCoK}JCYXvaNa}*-YPuy+W*yJN966s11wn*Sg0g0e^Cnw@G*aE zNq`n#Ik+bp+2YjlZqV{e$8Y?=O(Liq*2YAxPbfO*K;7J?#_C&toBlfRr(?(se@?i*`eEa*k1rAkFVo&*R$|Za=s#G(r656sDp1S1?&U&x!{$AD}6Pv#%C`DGRi$1 zCH+&}wLqHm-n-YkQ}{=F@<2`)^Ly_aa&0Yv1LrLKO>M)kAQ8F>xp-guzVazcPnSWg zDydbk-7ssOfbwAM+D=#OX6t0v>W2p zX&-&IY4`rg&$uAJ{6;6dK9&AEfi?PlIKao~f32E7|6Qxs9^-#}Y4l%8VE`tY*3O^% zMkhTw_kC$C=Z>KsO!Yt^;0ye5mp_TJ%){?Saj%Ura2A5v&@C@=M!xGVBU_d$$;yi# z&tuY6z@oLXP5E2Hnvd*=4x}~biOmh{)2SU`@iWUEj%mI4$&8<>@l!2+s>e_J@zX*4^j!P%TnQ4>f7_Yb9{>Bf z?f;c}b?yG2$Mb(*j{V1tFEY2Hz`1|VSCkohCjV6Vr>2wCv>_RP>i9M782)}k4*c0y z)vP7s&jEY|HUnVF!mO(k#16uhi${w&U41XnyFJR~yVc^H@g;99f_wa0aElt1<$}4d zxA=L1-!zS{c!Ng{fCato6~>@+YAGczW~m`+uz6|5C449_9ZphW=+1 z0HTF!JYhpjOpqpB3I)OUbW==3+x zzM1WSxg-xw!f1dka82>$sjz@o1|L;n*x_19trS^FL z?-wQib$(0P?|&xzEjEd4i;a@nqs;Pa@!$7w@_t(a@X`2h>-cZhAN9X4SpNg=j(AGJ z9swioGKx~8(P1cRG~W8b4H@B;SO5%oOKjXEhxX)7mwU;4^cO97DeRUoiuk?d18{gc zjE8cCq-j|~HwPu41U7~}@DOp~D78+mBYO$aUYqjgQxSJffKKxsFUxud2 z*cMWn;xfvi zX!IDjr2S>ISVnv*=ym5xTpahCZ(1cpXh!vK#QWRQ_?qo31s)cT2nIw{0$qz#PBhdM z#%Y?cuh)#~p?HS-weaUKaj4ho`;Z30)AM>QomNh#nR*pyNUChy&MneksQ(yaI6+$m z3@q@M)0wC?Bq^#K9zIXXRvb>ORqGYHwUu3k)F=aMA!dC`v21`75{g-`0eOFQ1tEoKh?bd&jGwW z(*Mu#7uu0vJbb<)HxMly5^x!x4KqP%Ge?+n~C zcXz$LDQ;L6yk|ynk3K9MtY#59vinr7QruPUPJ3o?mu3L-7^yr)E{`Gen5jHwE|00| zNjGNXTcfWUEz*h|P@X~=NPEhO>`R5KY;b}fI8*cl;uS|g5Nbe?6U}Y$w1v*64S~K% zm8T`KlOYphE7}oDVKhv!h{S;^(^qW?NB^rP?tzw_|K?r}|0lKr3+@=H@oc z?QPVkjZYa$GLS7ZW;`%u^=(7>66pVDivM1#*Vf{{oBNOV|NUX|{}UljEtJ5Wkx0M+ zodthd0&QHn!nH>;3qFV16U5>?(u*cSXEZ#cy*mfpOzp{7o;NV%m4Km5*8sZ9(M(LNFtUS&CiCyt zG@0Aj$K02GWP5nulJ+L&REh<1PNh@_d5lsp1rMZXOhW!MzK7H|?41b|68XE@&_1;X34|*&&bFQ#w|l_OOWukMH3>VB~L#TIAxbx2OhiDtFMp9vYb* zp1${BkxCv#AcRsfsf4L22|JjS#}QIb%0@A%o+* z_dK1-9oaM2Gb#8o^8gl?fA%sye>5-)IEMG0i3_!RpN+<)LK3G}r6fYzM&ffIcKRqP z0sX4$#*VyC6A!R3@d?=qkjg`NMI?@Ti_1H{2bv|?6LDuFp3;YxfR^&(OT^0(e)NZt zC2^Q8Lss{87KY1+aW$M{&}5*2nMoI-mFj#WS%D<7-dAQQNcIq4)kE=>Q3=IAHzfXJ zEuga4@hDCt{$mHhKMMc&>K{dX*_2#d1K#q#@<+ktGVrm_%T9-#@bV6iHTk+qGgaC_ zoW5ZS#^S3Q20Qucxn75VpB>=OKev3?FckvYD9KK+9ScFNBnq-$Z4 zo*l?~%($Ls2XReiIZ2Z>nY-kojv$-Gt|w~knR*~mAaR^_woUYG*F`S|=H_sSrsN&E z={9>+JdnyFdM#BbcAz~1{)VTs^bjbpT^P|fBmK?DelyeG%v`xlrAE5e6K8Jg{=)VO z>!L8g*4<8#yr(9Me@b%TY^4UyIx(0mVgUHc7ODT}q}+W;lySuG9lA$%WY7a9nemf> zU(*zWi7)VOZ%GnVB0Us|U}`3frH}L*sCt4as%Wtk-GKToVm7d z?mvG{$>#IJdi8*9*a!Pnlgk|(){OcAXMSEggcd3DVQs%&sj{WqG!D%=J=P9thp1kR z)$G*u+VjH$9I3K#Z+_IW$>jt6pt5fsep2T2qoJFJHPigG%&GR^@!$aF$NfLn;{Q}0@BjRA=zoU}*VdkEz2^DlS-bO^jGBa1Pk<`koPN;yY*Rq- zQf1ohXeZs)_Xb}IgpVjW8fHa{9*FSOXZ^pJoaDuGsCX>cOm04=dUvDZ|Igl=wzq90 zjiUGCefa78gpPD_P0A#Mm5VYtc|Eb6_`Htd*v?GmXf%2t5|l6@K?WoxQkmS}e1F2N zr5k80B&9^5=>(n$1@uzAplj*s>MGa+Y${?y5xOC}G+<@dYAfu(3Y|=|5RFEtr!5d+ z=aUN={?4v(EU}es_?NR9McR|E@zsWag-xM`eGJ?XV91y<*_eNju7~(r3RB=AM(YRh zB#pRz_VwNQ?tF~p@!$R(2*6hSw+sGXEVGjTD*S&8{3qQ1L?ZvPLjH~T&yH7ieg6-p zp>Y4NQ=R|p4gRB^sqL7xfj{@xSZ|)3u=+lN;&cCUl*9#o`SN{I=hZ7x3o#0R3~GmfE&*PlLtu%+A{ty%P`?|S%B;)WSTmoVxF*T8+tlXrCD&YE zv83ojOCD$Ri&2K^MIr3l2~Z6EqVn{lsNg~6z-hnsfBb3f9qa!ryKw)z)2{r#eoXY= z*emk?+ll`qzEU)>Kbl0r7H*)^=}+=(pd_=9T3%KqBxLxLdyx8kjgm5(x=SB$PvfB#X15ja;Kb+S#m)B?} z+BZf#fmDZQ&{Q*c-7ikz*MQaQ<#7Ids3B#tsL_CnFalY37Stuwbe^_XDHIBEnD?ny zO1JsfE~VHx%2g!Yzf_XxgX#YxiGo}A|89Z)du}EF{Wap(PeFoY5k`$?V=loQ;v z$G@50meIaX%IR!*QI#3e5!VA?rnL%LWm|nkek;2B$EW|lgAQQp`F}fa|I>EN%K!gI zu>ZdY{eOto)*{#R`p!Su6&#jr{!5OhUVi)b@6X;oFO|O6*V3wqVBQC6U%O{}Y0U>5 zhe_?jI)WK`7Tm}c;Qwuola$sDcLA6x&o;r@TyES&$htt$T0ZvI&vHdO8aqQNuT z2Dcr4MlZEUye z@iVsDwg?Uxs=J#da-*-=lVWl-{#=j5wB_ zovw!-&2yW}V7o4_rJlz)<=}HWU3)zoui$Ud!+93mw_cCaq5oa7+p}C8SJ$)>yy%)X ze^YvzkUGaaBZ9i9_H0?`-x*AdIfWWZ4te4xDQ0HY_3c6%JOh-yF{-)gR-YPZMfw4Pn+GtsLyt-~(Ss}6nlu45%Q+a-Es^L#SB z;7oHcpAfWoTV>sSl2%lT^OPcnWl&OY{o*gMA}<1%0M;o7XCqXL@CF zy36x}eRd^%#yKQ<)#i4IUiG-2NUtn_Lv^v;9?uEUD_+B0qE~HRV??h!T8CYxSHwrU zM6Ya<%X|D?5GK(p_$~yq!+1yOU50~mNc5`b&@<7i9d+IXItVCwoCL1=m|Y{nRa16>CR1u;Sjx&V?la_yaU@sdWF0L%M-nVIiYly z`+#F1dS!7RI+9LeTSTucqR-t9;gQ475`VEg(JP=WSf1#W$8*({<3;#HuYix!v&+vo zMxs|9;}_8@n{le!;kAcz)$OLVqAS})_(ZQ@POwZ*@?RVy(JSVUU7}ZAkIT3TfA12# zYV*7iy>c1Xh+f$|KSZx=UIRq0U>*QYk1z7{u>U=xSAy0<&rIn?kLZ<6w7tjl%I0)0 z8GDcDl}Br)NA#*K%Mc&za+w~}J;1vj(JP?UZG5)4Eo_(RmBZ;yLdSYUubAidh+e@O zqjYMQ=B>PSudokFzM?XcPSUf-bcV{1eAVN0 z9IwUSh3OT~57VnI^?~UX_s6voUehCb#r(aeK3J&T`- z|H`_^e;vZXo|3O{>`bqE)K8*U4$-|H(JM=qahWbr8INTHoKK`z7OZE2iS71CHs~R} zvRJO@A-%FZo`0lQmPPp6BYNdAe4hNhrKq< zv&VIN{ERSJzH<3FkvB^Bf4jB+HbGP@+W%Gl-#(cB=TJP0{LyL_l9vJUQZX6%vGDuz z@FtwEFx9eJtH6(EqQB~o0;oc+FtBX27+(f41+(MjG&9JZ##$FaSm~Q*?_WRrD|*d{ zpUlMAj~4z&e0l9p5&LQdjTAQBRPb{usbL7qx$j5y15KR)mGBk|)+0qqqTW$u=z`ec z6uoXRJcATCe7tI*JD8T;IM*+W;2jaWabzE#*2=4`DLjP1th-8rcT(^U2?69=BnUo; zLyFge*K4r4u2kYoStWnL#O3=XZy3NC4nKFjB*p)5ejF^Pq-xr&{>OBkc6I;%1N}2m zsq`{0)A(qzwZ97`9KQngqywYSlgJQr%@<1wF+ zKcDm3EW8@~d2eAdnVh~s{W^f>>)R`I-HMGLCAU4v;pwG`fuhNO^igbF`HR^s1io+4 z{ZaDZ+7v+yOtF6*0#hAKk}I6wpvT*n;r!&}r{BMM z{q1|@ZVVkNuFR~*s?WJC+DFQNPmjq%w&Rde0shsYDXaG>o|Qcl`Rdd`+5%>)drnU# zq7SMzSHCP&M?Wa8Nou7`b<<}3o4^Rt9jpCmWmNDx(0@Xq$L9cB<3D$b@?WR&|FJjp z|4@7x2k6ieHwR^O{@}-GF~5#|bmyl?J1S6?ia9X3M131kQB)S5N~5yny1h`#ZHhLL za-9b->3r))KvyNfznBh&^`&SD4Bbx6GgC0_lEQUh2XxhI1B=+oBak^LfNIEg*3llZ z1Lqt#plNH|0Y+e_jN?eFkoqoY91%d6DqG_??7Re$Xd3Lz^I*$~u(S4Dk_Xw@ZDPw6 zZT!s2f!>Sd6h6fXIcHR9>Z>09Oi#Zw1%2DYpO+>)^XEPM8UFr)JdR#g<}Vo#Z`tow zDcDb8qY)6TrzdjS#{;0xLlc=gc;vRr>d?rsf}N?r{VS`0Q^_@~Y!j<>O{`QC7)|hx zNdIvq+0(%ParRhjU@QH%3iiK_UETlvIO#tuCrfS;mZl}?KjE?^xq>2sHWjgD;e?3d zX&kc&Te1mf8=L4A*o3v2K3bBzSd9mgQ)VpQE*J|VAyAw%sPE$4Zu+JP&xRMkkH$PIt(b6&H?^!<^>6cpj>W}-*cOV@eRiVOH<-@ksZE|2zVgAlh&PVI+WED?Euw22YT zmxpqKFsKh$JrA#R`Uz#hVQHZFX9(bq_cPOH`5=04DYlZCB zZ&0c_xIk%R#Q`Wd+PSv#k%ic_(Cj^LyS|7X}8HG z*<*%%q_r?i;~!lK`ahp(4wRDfnH8-h-l7!g3@z;zL@0+r*Vt% z5dFCRLsQ%WVC~H4P+SJHxqt~j5&yuGlPl;r{QMyQ{L7~9^v^I-eE-a%U;U8g5Zm`- zn74i)#{KO6prl6rE*2R9zQ^=@t;_ zmTp8khmw*8rMvqD=^8@1ySqe??i%Ut?rxB7X70!L`~JP>?6cQe``LbWK7P)*;x!-8 z+!4AR4?@H1zt;E8UlP#XO}tOD97c70XT`)=p@td8W3&UFo@dtfQhhA{{#>4aEjx;s ztKGokFc&n<#>BkF!imKikXd^%9bIX+SzqAme7)Ngj@SGj=Qar~C$~&gUOza%AW_*Q z6ir@jDk>VHB91wU$IRhTdE@HDaL5feJ0op_~3Q}ma;c_w0DFB;uy zX=+`w={o7toc}^-oYS|>LSv3gRb7>16hdie@fMlrbnMQ-Mewwbj!zIPf*nYMQdu3a zLuzjSOMsyYeANtKH+&Y(HwUoWEvN@?{ zujKtN=j1xrcVB$|neoRVh;BeJmA*7m!me76qYQZjF<@uxS^1=oW5$J1Wvs7i!Pn`} zO}f}hwk8SwT%qAas_87G)Q3GrLZwDj9w&SAG_4t$)p={ReTL8uWyrmaVQTq;M>-oz zTT=Mix5vyd0FC;spmgwiRDL*MNXC3lEC6e)W#*N`D)iY>6X)x|V@7eLysWWrtE2eH zz4Df9ngJXkp+ClJz;V+;dBfEu8ZF*v^*ajVgKtccwbCcs8u1OJtr%0g#hZ@!z6w%*hUXHn23r$utl}o(?C2|%CwH5p~OQ*V0y#oz%f{7^& zQXE?C((0LMibq~fg}qars%(${Sfd*!)YV+kQtzFaz7N^k>+9RK?_o&ZrS~WDn8moE zx0lon*u=*?Vn%eFn_H-())-L`kWfnalz)WqFe4RD?0A`|wwx>Rz2JcK)NfXm>j=!= z?s*ja7H^1_tfeSbe%J0SZ6O?Ew8yTQ7C`B+C_`wHvpS`tFR4OvwHby5tH z4!O7x!QUs<_6PyLL#8=2R;3~eWVkAe>@#CTm2!$>9joy}5kV598oTflv_H)Lau8*y z)qaPH)P?dZqH#wa4C5IJ6yXuIQxyK}`n-h{h^O#N`2<0+YEBeJw%i7FD`daSgeAB) zj9Nk@9ib}5YNmO0+`NGp_1XgWCip?eAwM<5DqZ|&evxpn=R{r2kigC*ZNDN}%UP+( zbYA-`TH?&yU4}=QcZ^1eXuAPY{7&9>DCg6@f6}Na=}@igQ(cY7rvu^3=AvY{&jn(n zeX=53wB%`QT_Rr>s34Z9RE)eRFwBWVBTOiq2nmMxF<=h$!#$-ldEE)4p#J+G2kJEuP@-fINP`7`@a3K2O*C-frXi zL{repCd<7zPE`NzWHu0r<496CHzMp$@b;^^}! za%-?}1-idL#TQYOfqMTLFZ7?!gz4wAB7Qo%%8vTjb@#Y;&dkvkgbMa1yV*x->z(9#u*WX7uS=TzDxPRtP6S9^YWC6V^N-^$FwrouZ&7WQd*m=5+o zxI~ccTNsF8i^ziYVARK>Hkcq#{F&r(?h&Mj?#Ibzk;(@M;`Y zBo7rqZv1yC{hkYyYskWyH?sV#%v2#bTjmNJCfAHG-f~(fjEY{FXLv2;MU2Xez!8!t z4SdW<*H|#;%HufLp;lv-y1;2ENdS{X_eqRZvpzlFT(!?Xx_rh18derKQL#c~yaaV3 zPvyxslnMpM(1gnIF1^3Zu&(r{^>?FdI)NXwNwH*sF?w&*IgXus)I{_FWkcsu=74QeMPOW1J*TOz{$8n zuslworb?q9=Y#5mFCn91uJDOfVq#5JoIKZM1YOHrpw6z0hl9qaAmx=SN&U3ezRHov?1~?zwvmdp50KxX0ukj=Zc>v&i!ITG-v&5eHbLjRwlGOk+7Lfpo>b zuvTqKk^e;D!wlx>lO`@U5!-7!%q%!dAWrWFRyX&b2Nz!Qnszl|;Co_#_Or#LUErre zwBvXmgXi<(iiq9*8JPhQX%b1SWmE9E8ZY6;!e1#Z*gueElI#Pg z>9oY(742_{vkhuW>0p<9!EXa&#BmAbFXPG>Cahep+e#qNl5t`?5@fz1Ly(_@ga1 z=nwrQTx?!*?(wW34Ah5V(4ju}k?iQx=H~__k-z+E5Syrl3h{RBaM|aRpPTAoFTQJ& zRH=FU{9}e3B2(C}i`frWiTet}B@Fo$y%{;z;3epn?xX1tz$XXTgF?K&?*ne&N1(vd zo#bVASV;i#0l=|;46gWsRLIqejhwg^45nDZ9@9jyKejK+`E1jhkeEgkhfj-OE zzpH&IoYpwZ;N#v-c>5` zM_S7ql?q0Z9W(6u7{6%k%w`ay-kc5xxQc2sUJpv>o@h8ROo*Ms!#xTPQeBl>Ok4)q zl5f4?0qOVIL2RWO{&0JWZqcX`iV>G(lw$`mh@BDeJn&hh)`>;(dGN#a*Peh!ID2_+?h6V>_7Gu(l@>V1(^wJEDEy z>}~0foJ;3-(b?og6*L%5|DxCiSWb)vle}K7 zS^hfQ=auSB0gb}*&^Vk>AZ*71jV7uIw-2i%*OQBoILY2GKp^DS)aA+P7j@{Bd9+wm zpXX=ofS<*LI_LW`ZjuUgGaUNbm3jjBlfeQVSBKk50rwGTqVO-T`-IoM46)Sn1`|WZ zOz*!Lks>1bOyd-KMb{CmPdo7^3N;$db^HxAp2<`eFV;NAnV}3xQ0=LT-)2`GM6C~< zn8=0jgu31tjw*1hntgPQ6wGGJCjfJZxncQOUplxCtx86ubO_9@e>z~S;hT+cIQT^z z?w}K+pmu$j4`>|)^BPhZaU9h6h84~Kn*e7wX$S!2JHT1Y>!4-a={Y|ydQB7((*ioK zZaX9AQBcYm)%59+8pE<^B42JqqybsvbaOXCs{K=*>(UGY-qmQp z56p}4T{s5V73hEbznHe31*OVqB_ww`mi{|!4Tt_QAcbUdaqsDAX&Wo7@|V?3(O#H! zsL$SPlt$>zjXF|Z8nWBBFi^_Yb;TtZPc|xN0H_}`eWhCEW_{iK@H)8yhFcluCkBlH zMNg_)(%Va7nME3dMrhCr4KlSZZDB4lFtQ$-xECMj>4grKz?2};9XOAIAAXb)QDqN- z!bG#ScI10K+tS0PQ%s0=@(WzTiu%vm?oag0MhHjt4;oauki2h@9BlJCcFPugUEFvv zbZZQP)}8F;O|?F^q&(wIqcx3xv}edWN?qMXdMH?GP0bQ=mLW27y`D;InCA1pu0A0) z{m*{`zxf?s;rpQMk!?opBr0VE{hxOxMWY8pH~Ol0AL~Bwq)WqRjMI~d@FS&Df##GN zPd1g0z_^A6lOANi{6A0RXD6)(uo%tLWLMhjBK1Lnno2EJbMcT>y1XPMsLohB!b5lf zpkjocuIB6$Or?EICrx0n{-HD?{EA2AIhpX>MN|Untq|=9^mjJ}9=h+3w?ad>xJ$xde0SN|&%-tZix~g)hbk zt~DK2gRy6NqY6G{$hvaHs)zhNwrWFbMY6#U#C~S)KVct43E-W5h~iA`^tl5Y=0`N1 z&UN{{y!Pw0!H>k+y-Nz`fz9tGRHo#rT>ZDLSs5w$UwP&4u8oy$K#317*PG51yPh+wFwK*$O$wka zY1+&uV96Vp`VRzt^*{@_5SLyX9!QF_(t# zjb7qFOr0F2L>~Fb6r~#<`aQKxu1<0g3U+*rtDgFC!!>qLL=xn<3bb0nU|MAw$6&E7 zSeyeC$Jt6eSzM(1w!&D%r?A9OJV+qka2Kb+j^tuDMSDsKP3Mn$x@VId^@z8MiWUx?;V5$pOgRXD7nBbdaO9K@Syp+~4R**~UC z!1$;zV|osvZ8RxdGRY1IwiTRB^s|aHN%*R~2*mQP14O)K4M*HGohLpjuFz-SMT{Nu z=f0LGLi6W{&-i^Kr%{3n`*8F>R~O9QxFZ_m#{GvwOxK>ci8;s)iLhX|1EE7v6%&tI zA`GRMU%K1DKwk;jso~Qu=xp(4kg#0yza<~v!6Mv`@_!|C6;L!plRQ2qqU1_Fw~IFg zuJ*$O8*b_!Cj+WpM^qEJmq!kF0%YCu57*!PmoZ5Q8+Xdh*B^Eo_3TPl*&9X7&puT? z?d5wA8t5TNhFnp$CJ_~q0doomBqm9oo%Afr+5o@=0??(K^q9%Ptv-SW>wau*HgulA z1}TSt#AbdBy&wqzhLmbuh9|UZCFi#QT2^=IQGD9+-*~Su+@WtyzqTTCYsM%<)%Xw& znflmoo$3E+l7_HKQ{lm;6-v~ydq#`slPeyk9kh3ku&Z;2P7A8^+Rat!`;rW>REfNpAZ};}j!4Ox`CE5ICfx%%r3|`SsMBCbN@>-YQyALozpEA;S zLnK{VY|?r6jL>S_lOD2JM~G%k`J#s_iG z@H!%WZs#{*Zx67|(I4Hunjyc~A2-H2L+R`sPSz(UCWw5Atq<*EFNA6bTIgJjgM~nx z+~b8GOlt~o?N9*P`JeMZytRuR2)+U?)oiQjNBj$Te&@Kc$H~@8!JdQ@2d65VC#W_Z z)r`pMkJ7rq7GFnkZB_i4CR0Do=e`z+yeL}&dA|3!*MfhwPb0L^Uit)``~MIRn<6XU ziW_Q*IM2Nn?XZ-F=knu^+=QBXn&vp1$>kK<)&#rimyhe8@5TdGe%im&{A<$B|GiFf zJ05#|JeMVzMpHQRQVE~ufk>c+dhAwtDET(EWI@zEv?Z657rSla)n%;+W z@WXfQfe3m!|8EaeKg+xXc&sS^!1gV$^O6B3xI90HnTyDv^0fd066Zh}!jL3uD33N* zPq9)dv*$G0AU^OOg`L3;?yFn_HCfcwv~hQ8C5ryt@@h5AQdN3JCEk=NPkb);gDs0r zhN&Pn?c-Hi&=V^WLD2YfFbP_YjfopS*}Y9hk7t&j7@WAT^m}yTm*_!-%fk=0JNe!E zWWyv6S3=}Hsm?B3bUUdkQ5ZHRF?)hEnno3l^70(&xkY>Tq*g>Kh#MfVo2@?bO-6DA z*ZPrB?_&$W$Eui5CxhJr;X`?JcUQW(mXt^Eo&5gvF24e4hP_|&3_uJprx`%k@;#7a z3pBI7%C4c3xRHIQwZTPq8lrLUmj%RMLE3MoX?)rg)%o7*=I!+r@F`mE?e!`H)B0^* zPbjMu@~F&P{xL*EN>9iw_Ou}tF)>)H17hh_Gu_1(x^1l~tLlk6!J(DUQaZ`WQ`2Zq zc-A;8a>VNPQz5_S>9b$`1b^PF-iRLt8MWmr5nCJw_e;!asp?fQ!64K+=O|?h)W7Jh zi23)=qfrO2S#=41o3_ug0{#8boQ$3CIMQOCyGMoleTwqBxw zQ~$8Q_>tkCUl$+r+XVoRKknPFo8_-y-@j1MN>2gnXF4Q)*qeMq0389Y=C?K?D`iNn_LQf4LdeXKAvyv&*8t5^Mu+C zkihZ^x;4MWWm-+UEoQS2egzKtz zzE0)}xDE?Za;EGlbW%ErqGW$*UT;qVz$Tw~DCS`}M1KA(SVV|qbo4f(O5TD+-%1`-p% z@9XzsLapJO0?P1*DX()9eZ$C84v@ZE;#}q>)u5T84}3O3UnG3Lk}|++pOKiVv*G-w zovy#&vxiN_yW-ASdQCYdDnIv##NwAelyhDM(&Wjxq0WNAv@!P) zA}`Vzeh2=LCa#z57(qxI*P%p=MS5(AJrV532r-VR0603U@bm~I)@YpQo)2&dQLop%p0Y@ObJw2dz-ivfImz>+B>ozI zl0LU!A)Bfk1_verg`)0NY*OUpln%Oz z?_Dw}&-PplGVtKjn7vH4rk^@|v%7Q}mdnU;1C-Fu*T1YYE*LiW63Gus=+%U!=&S`$)te(jMjgEfWn9VRif|id7^AY#mX>VwU zdTw#?zTNK>O!28b65SWXbDpo4AoIhvqMT*$u0>u4)T6x(Xbn4r2qNcy^GEAQH8gh* zh#6lMU`-&zc;Oso#R+jiq{t43nRXC6qfs^(NA->(e?g*I$n*a&I6OXM5PFI%0?PGL{{?FSl)J$Zc zowh6YU)k{#JuQOJVO)T@KMe+eT`*oZ%l~VDEqetW-FS4wNk3pdOlgULpwaAM063=0 z9%q>T_vR6s7;#-YQNVzAgO8%&; z#ATSD$<8&i!4Bk^+OJcrfOS%}8h|<$fa)a((20W_?efbYlU5~0C4lwvV#FsLG4RH- z!cE`Vzy~Zbgm^1T(1uPXJu16J?fL$8`zi7cDne5%v&+pEL$=x$vglD@xQll2xHD^2 z4p~9M^L8w{+*z1K>apxasR-lupnu0wVk9I%v))XuZn?)kSQCMtKHM^@NanIosR@xY zX3&y0K5L_AR#XY?P#ZLrJr2k!*E#iO;g%e1r|sbEMA8}H`(lR-Uam1p!dyz;3&F@y z?hFpZ3EGeN3va8|Z!~tWy~LUPJjcCO;OV`#!zct{%RLo5obT_5GX;oH)8fbwyepti zA7OT@c3RJy&_zH_!=RaY?MB8y_@l*&RF8p-;%w~tTn6be?>pVdMmtVF3FgbTqE3(a zpUkw8fvP|nim|<4AcPX?9<9VjBxCDbklV3WZkC)0o8d|bdlHO7qHAZ@YI$)$(GLRD zfjh7$3k-A#%d`7-4+uYnDa#?Dp+4HpKUPOUQgw09qWO5B_@jr+bGL_(#}pYhvLe{xXOBS#W_ zim^;zk~Y>j9d89|Dp#Oy-TVe`oO(z1wx76$SmUOe0xXMGOrGfLdq%<-p>7?*cR zzv1S29mT$FiHiiF!}cO1N>$&5TY(GD^B`!NjSm6rVrSFuzoezLw9VWuAEQJhaF_Rk z5_a5RQnx97IOn{OB>1>j zFD+7i<#DX$(|g2JlVNm0`ya;3H{$9>;tJ1HA4b3U?oJA#1>t(kPMd}@u6vjVj=jJ9 z$42Lr?bZioqUa}}@di4zWC$IvKoM=dgZRHZLxHD*hD^((8==iBsnGx)v9cih5 zQTAe^!}IEOXElwe+Q{$j&gXv0lc`VBoICckbhlQA3R*v^5<8inHg9y9242X!I_y>0K6X ztD+arMV)5Lu2yH_{w8vq*7V;;!DDLBkon_LeDQdFwibE7$rbo`lrlH2P4;hpmymmG zf!(cUBVarwee7AS4TSymxE>+jn2O4Uyz9>lvNwuDhO9e{2pTa?No)Q7;!(UKqWTM>}_cJp8n?ya95;&6=v%QXi2_j+Tni| z{z)OXCJ+kxEJ!>)a}6F&gK+%8rTa2xs&1&sJu9FKcbv0T$d)0eA1B~35IUS&VzHp~ zS7#0XdhDXgnMZiAl}*uH<5a=jJqgzH9Krc15wo&4>C|*)(UMC^s!XvhR3+o~EOElT zgjFc%)r{+yas1l@Y!ocj?-EmSd{GTFjuk?k_5mNQ7my1~affh#By;Sb-k)6gkS2Y? zxZjX=;vF2nY*LGi1!I)pnS0*{YIc-lFZEfd1SDjfstttikzGYD0a1Sc)GBT6Y)Juj z$Q^aLsfi2B7%(B1l1g_a2t%Bx=Srd)))mjrU`+kN!JR}m$=lZX;~yPmX_CRFOUhU6 zr!W5-t)YuHhi4Qfh5xQ8s!A*t;y+}Mz-%I}j=2J!CdVl^=UP&N#b4ZEO&8MQkQ9)sP0-8WUM>sHo}r_8eVNL&3b z6|aZdqK{^};O`C8HL6QhxL^8@aq$YBQfIym3icO4Ui--A761N9cD6N8`5&f!g90-1 z(pMXE@sz1v1o5&Y)SWNu5vHv(*gU75dQ&g;cEBisa#&k+B+&I<`3lO!qxyx{t+Shvo1pAQmMV+s4=nmTT%A#9YJ$;HXA zSuVpHcxhm7r8+*ggBm=G?FoY8XRJD zzwfpv=V}d0;HXa+**2oFKU#QxUj5YZuRySDGdwF6$iSTkrC#8_Y0Xv$pJBNcgg-lWf-FBDcK z%#I%#@$Q(vz8_{`mwZ5E{+aB(Vu!K1+=CosB_COqZ+*-8QO&cIy>7wYYza$bo7pqS ziCwQ@1D$AwO?fnlL$K&cQ_rBPlJETs3t%_dehJvPSBgMAe&0YkivY!&va8~18ooCL<$a_o;Te~iWp6F&Cxr8Q-{OGVh$0fMj27KUe>ba%Ajda=}M3g zkXJ>nlX#NC2>42{u+K=%vIb*+yg0)J&J$mN6kstf7od87azgdstqF1%_6S^im+11LqGkG@yAXFI-PHcc^h{yyxvi5MpJDge}-3-WaX__Or_sY@N!J4=eM#-8_%<-Eu*{C z&HPwaYK6F>Y>RAhWK33H+YFhwJ7!njpH<;cF{VnH<;^P;@Bc7RVbx&aH>~~e5~y(y zt6^7fo}YfsQc+4|=6Yv!lf3ktj08L{8^LJcp>`P=xW%VpBKCRjt?SK|iQx<~TPHj0 z(@BJLG9vu}$J@<~8~}(lr~$afM}Xb1QUG7_c{tedw0zY2I+E=arc=v)z7GFl7r|mc z5n3wTt2J!Vh9sTjo;{`}T``Y0_Kk<8@7*5$LHyhh>rN708wzFr*qYd9>E;^9fvYgZ z-~(#R*ry+|n8Q!i#WoIwHv3ZiRD369KG2p%HI;t$T)Y>HUZX9U%vZ-_Deq1TxuUj= zB{Q92uhC~`o4mQ#nB5Wo$%0d_Q_jSl_XN&g+x*2j*kWf&ggWJno5_nD8rwxzPnslT zcpa9?+>II(qawTg8N$ul&_b?%&;Nses%tkGJn<$>wzczz)JVCQ+PF{5kqho&XN;C4 zxfXCFP{OjipoM0k_68|i%|_V%Aq!^gP@}WYu(WS)r|A13gevV7@UuiThUqSY%vjAG z5`BhusWx5oOyS`DZ*}esIPdkCuG;CY+DYISWnku1FM!LhUR$Xv)n#Bw40f=y?%}kF z*h;EvC+%c#1~p9cA^1e-^QW*JH<90GwgHa0{jgyPCo|Pt{fs@y)G0gJEIp(Ex?0-L zk(H{D4Ay)Cw@E>r+NptN;S}hhEH%)c+wP7jj;$X^yiJQ@s=IRigoa$>?Pt?mIq)7< zOIAsNf{>B`5$XNcV!3FqS3_wZU!ygqIB4kA!5!Q&m0!eF`N8AM%1qY7>>yC3g+gb`qQajNT^oW^atj!hw|6 z=D2I4W1UN(YEeDqz|E4uzx8K=mx>^?iGm455E92|1$F=xJmB;q+y|ay-3C%%7iL>P z=bbmsAt!$~&@v@`N!%$^Ac7Ru765vrTFQy8+^= znsv8W`mJ~MgzGIgOK^`K-9^6D{qG-A@N$rKYS0I#~bG)zv&VyTEF-tb3)$md$-7}HT7hn*fdWyRm`pE z2WGP=4l8vl4q&~A6(;hq9|TPrF(MlK?N(smen<2`j$#e5G~{RFnJ*o8tyiwXkMjCc zOo!o#zbzra!(!Tcq5;OlEPv^y>y-x>S0e4k|d2z4l0y;5vHU8=J4I(J`n zZ|Bra{gdDyE$n)GFt1C~9DCjTzKEd}v18)tq7Uw$?At0ILiyt+92zZacDF2Q2OFmX z#|%k}^KWFAqA7q?doj@eNJUY74M4f3fPYR!0A|j!6v;8$ZMil%y9?RR=ke+B@08Oa zet}kQqJ!u6`uZ}K$M=8hrnQUAmP_~D^p~Vvf2<;o$0NfmpTvpK_3jh7=0|Z`3~GZt zNZjMk-FNxj+U3Tq3;@=*pp|2&(IU$bVEC32-Wo|y@>eH;fV%*Gtw%@{_-MmjC@>C$d4XV()V|7f z3E&#cS@^zi)N}Z0sa$h-8MqIB4N!~*kU%g$uDOFXYlQ<2%@sF*-x@$q2l{%&?G%>o^~@FvP2nO}dKDCs z6xC0mv+erEbz4^6s%4?B(4y4?av?`zGEEIBeo--cHdeY35l|onPKByhAq`vm~%TJiFyJKhhe)nyZ77ESQTDrYSGyg@J zl6mz|26QBNt?@OT0|wj|0FH_y>+{=>8L)86>1mt!YgWb3Hg$W&a`4JL_AoQnMkCgt zC3n@`=kuWRYV2Yu`QRtv(nU+_CKLDe^;qT~D^2@%E!g-!90nVi26;tRZ?ie}J!Pt` zx=HiNCy$zx+O0;j9Z^5GtxQ;ZywG%bg^ zB+qgS<+!(xPO-eITp9gSaAyX(wV1PkmA7K#KLTbFqMMq&hUg0#IvY>S2 zY*wwRX6$xV2QB{R6N+ul^$skM=;8~J%l&{RiMobu}&9tNylsBrY|!k=9^QR=|A z%)@%w&j7{$i=3Q~Mwnqc|M#e5NO`W%&j3{knUS;$E7-UHXl9~-X;6{ePi_n{0``G4 ziRkHEu zFUqY)`X$s$_vr>xTJq*F8LDvU(M?u%DE&}>gi-{a0p|}Y%3WWD=%d8p$N2v_qPnqM z%>xsCwIlXkkX`nRc#oPT&fo3DIm{l!Gt3US*kKZHb2(G8A;||3Fx0EbR?pa$^U2;U zxJ|=+e$S41K*uY1xb(m9l6}q#>?CZSe(Ih^lK z_?9*BihH3_HUv(W-OG9aQ4wG|S4G)yHnDtu00Sza7}>kOOfn>3AU(#X=beNaMA_C7 zN}l$9v!sD?-(xZ=aN|8niZhx_oN)s^R-CamHWV+q%ERBV!k~0e@Y>&1V+mh);0p$l zoev+$I?|o9hCbdm5;7yOU$;&A-)jD^z8R6DD|vVTCrw^ugZt4Nha(!W{C>50j|5d;x~IT1e}KlM85JK%lFp^P!|ew{X;~u#GsJ}pYViuN0jWSe&vi}Jqvj_E zMA8R}q`0UDH=Xb5F^LGf=TA&J`Y2i^(hmv?JTA+B%LUBmF$~u(ID@30zC@hbQOb!< zeCCA3&q)Pq5^!KL+g!+k{wQfR4PT~DR$FZI_v4+i+0n<)d0DU@XV|&eraj7Y7VsxA z<-72Y7UL(~joOp^LrPJ=F#md#q_975YD}O|YDWvte(27_I^mI-ns>!3*uQs%@%3h~ ze?QIggtdiFMrAF!)g>a^D*(TNO!h|^x99B)|3;@&W=_{~xO70k1SyuQYj%*i%jIoS zZFs3aAWN=5E5Go}T2G%nAVjxr$i1|Hb?a@WsCjXvW1LR*Sn69Lfur z`AT@{_gizQ!>&K7JaJOuLE*WBVwr}1lWlP49AsZp zQdXVsY=??b&dBTispML5g`X-ana+#$yuPy;42D#Y(QR!W&hgM87SMRx6D?^WrMMl)f{m4@j?1eCHsn5kjOdTN zICjYHh@rWw>O>&dN!g$`_fOqCXVFvXnjN!a|4~=TIr<;pHtws3?{L)K&QAaFO;l8* z-9;?08pi==KMS~FJb*;7(ABSiNC3nDaQ>G)^NzdLl3_rIbEKxOZtf={5wZ3Guf?3V zOJJ1I#?(ZTPT^juO)J?>DSxB+P+SDiz6dn0etG%-MDJ_u7HIPX_)`W4Il$fe>>%oc zr26&!hyBR@^nG`Rw}T`VNmBI1p+XMaDv=50y=K~RAi1Q@F$fi}qV8x8Hz4jcZz_pDWU(?(M8{j1H{*l?zV;x;2+B$I+7-7+=$EEP(~ zz(E<)xKc#i5hT|A7&`s8e%{C_|1_-hbsLrf^#dE%H?b@5m)dqw(CY?P)~>6~r9|Ha zF^n7|PS}N(U8h?@;d@=7B#3cLJ|ma4|9~!^7x*&>#vvs4{M!TMJ{tx`3&iPf%G*hv z0zdH4-+=lp%qgd`s#NI zJ3=W#A128ORG9Y<^y!?CKR~c+=;Z!2eR?~*-5Nha{NT@XdH#cWPGr{h&pzk>V*qH$IdWn5P}ekZ?F;af|#Yp~D&VEzWU|Thj|Ozi3&+@b(Q88j7>n&wcOO4;9bn z4$ilxQOqJTJc4Z}hnO>g-fHgNkFlJ|Ti>&s`$-ImNV+X?dXZ3zuE0!|A|l~NE(D+c zo7=JGQ{EHJ`?zMj)=j1~@H1#iOiUHtbD&G0IP-o1<5m!lpo$uiO;HAU$r&5&j!B@ zz}oVrp8dDsP(9-1=^5Dl&XFtxLkhd$(lK$BvU(6bI&S8Md&DxB;L>?DsQXAyVr1nk ziYBQ@uW#E;M0)80B;IKcJ;yUD+CVLRm4YQvw=! zAZUysF7?UDH!3@07hIR%pz2Xs0{G?^fawu%`3(hR8G!R%H|nMQ5@o!hCyDd_#x1sb z?{Z0&AjtDwvx5&M((i11rIf!E{N%zsAiCOXa#TNLJEq=IoCr8`Ve~pH*fl+>`aY|` z7&bbEHN4Y_FW0O(>&T$dZ;{#cv4PR>( zZKAN~C4z{2(7OZ8QJYJZ$WKDWoW{C_TAsD-LlapU(=>-4DztBvpFq`#gX{42NkG&q zB>1BVD-06$3=A#O`4|4HVz^2(u-5wbm>6}Q5XWU9cl_JpAcJcPVFp=?sg3NF>dmS( z`&_)#2vCjQMLs~XZ2?Hyc%;IUbvxeoloFE}S&&#H1h zAAD2Crc9z4bZ?4Z2;r}eb?BXoY-=s7GMrP2QU{fq6qM-_{R;n`b-N_SQDIw_!sWfi z0Jq@mK1GPLs_?r+s`~>aBD;bGR-#>vS7Ry$U zOtyP2gZi1mkrPBfnX`tCW_pIJpQ6XEVX(WX=0*@I6i4PQ1s6bFc#x@?sB3l+;nW=(Ho%I%WB4RoCwY|*#gkC_!|499l? zBS&|ajgFQlCGyYNIL4L@f>gL^yd9-SwzIkN^R_s3UX5c!7#^HOA>hPx}Bxr(0821B3U$%GVk6+pt zd!vc)>N|_fZ^sQ3rT-e1rX@QbdXGfVca}s?WSX-;!uCE#0UA878(yzCi*6W)U`IC? z#OVF+A!{qry7wR!Yj){{1ddS zRGN^P)}~CVX(gEt!T&g19{a|?pZ^Z{-*(-#_-~frzgxxs+2Ozc?+f|xU;O9)mVf`p ze^oo=KhOT3@P9Dax&5ElDa8Nt>?;1x2jl;T;{759?i`6*KaRraYF7K7ty}TAn8%Bt zro;pBB#bYQm*eS^GzA2PC+L#v*>vj1K{Ow&n&Q>lzn+}DjQw#SuKk(t#pU8k%&+~q zn8wI4=QHx!H3)|OVgxVbwd-~g{|F5?#cTrL{7C%p%^MMtrB**kt~~eSz{g90!E!nZ z`yn(3pu!n+dqsroyV^ce$k&sqpGyE1%gSv+mavij(Vnknv zJ|R|rARHUu_}e;A7`T1^f!lK6+n~0#Ld8|IfAs(IL&$$tQT}r({m)MRd9`+}Q|nge zIFT`NCe>$sO@m01bW3VydI3=H)_4%y>q-EGys{ zh9z%MpX0=SIf(#6gNP*0>tHOdCvkW;iRK`XtmHto`iGEx2Ni0*A)olp^+VyP+O>QK z6R^H?{kCmdoNVfEn-+&klnn>(5{CUXlY*H%De`IEq-=ts>K3O&0p%>9ll^dB$D z<4MRPngDOL|8ny5zhk-8{lAL-e-!)w+nfSmT)K}3lGal@{iwVI6tR*r(7;#0Tyw86 zIyw1U2piqu1n;bk7UKvHMTU|j(VmkWx=luOmT`~jwpRU;=xjvQZJmze-ddKS4^x0R zBR5aF(^RB;=lf3gs(fL4H{A4Waml2np4<$w zvm1w;ODT-ir*f>(x=GQU^zAm&{_JXu^_^MJTClnea+HTuvJ zgNgXh&40*Y>kYeFEmoBikgJVt7HWS8mYJvjgJAdi9}xE|{ZB>zKR)`8XdXaUWW=Fy zF@8Y#v7cy~^4O2H7yhWf7$MOB>52L|V5W$WGrr_Q!K&9{|94s^jux1*#^Norr>;?UYo#6%SPRv7An)Q_%ld z75%U1|6bGo_tybHQTQWfXy^r)Q~#nt2Vi{| zA`B>uZYDnl14D?HP;?Su3{*N{co{CyovI&SEu_s^3^R%28qpC%OA=X}d*l>J$!1zi zsfA#IVCw>3IB5v2+1Tu^@6Uo+6QF3FhVsE|hOxTnBoune{YO=bES4yB0N}d-=Q&ST zZo=SJ5*DgBp2X?o85&!0m+t0GmEuRz0sCP(kIakif_Q?%SEIxwr}jNcrGOm-V<2c{auiQHEaWzr;H0YDnA}Qcdug6L) zY?Un&pka*omu)=X`njk_6FyoOgUR9&nwtHzz(Bv3uq*sAfGr}9X)ckPCt-$kLqnDv zOjvL45#y@P21LBU=a$E6a(NG1Xd zK+DAp>Odzb4VUZqOnE$pCpmO@HWFx%uDwx~g2`Fxjb`lc*B~^d%MZqbMhDO)Ekwls z1o4|ReZw#Y3{l?&2;Ozrzs3PhZtF9qKZtNpA27Q=f$cvHk3J?wOCIFw$}g%{25@P0 zn&&{HKVzUVFf&m&f^`bAUY|{Ik&>{oRH<2K4J0SNLZwV$wc>DDB^4S?CT=7;=gP+< zP9Nfe&9@?Z$e&ouSJMFJF|*7U5zv|$Qk_1s3P5n|nH&<9PI?(eYFcnTr3z)nmbm0x zHPqLaQ9FdU({JCuI6=yV7qTo-$1ecW2D29U@hF6E0uwt!Z)=S_XGC@&xue7b+36N^ zx+ObFebc!mRGCbJ7={)Ob^0`zz-I>3gI0jP_z{Bp2_m7~=6=Y5;UZ1i2S~C9ITiuS@L58ZneymF4L);!vWRFoUjoqs0c(H*qtD2V z4OF}RNgM~WDPSv>9)tiMR|~YTc4gt0-L|$gsVmG%Te)Qe^#C3*YITSK03!M>xIV_X z@uUwtrZBJhZ%qrJ$a#vmlbvU7tL$U{>Z!)Br6&8gPc=@;{m^*pR421VHEF78)zds{ zs3@_?I#*9Ey9BMaAgE8ooIj-E2H0BJR5S6_X8M#lHw-G7aSFGHkdA>O;$$X#7J2%{ z6<_!W>plQ3GQeFYS!gEWwzll_JbY_eYZl^Gl9RA5H8&f`&K^wi1H z=kXks(c}kUDos-fEzC|!id=NudLMKKP!FV?W$29isfW|fb?A)4`!%#PhtAX~4WZ?- zHnl}_)(rKM7e0Va zaV*<-9h({lJ|cF>uF6;=YHi)@Q={3v+E8xv6H4tz<)XG)ObOp11N?-wD0BSs&HCmY zM5bFQ>&?)lN9tCF;oX0}_?t_KDbL&P(KWd{X*sfYmZbXfRc%%r#RzEAf}59Z)4Ex- z$@V{`Y!9AV>NpWU!hXV3fpF&W5YS}qKLP|i4vur<#Mxi@|)UaJaChtG#5nkwJ{`e zCy!$NUY$pj)%oB~-;j(;T_2L{ioc>IEwuA`+Usxx&CGXd|La(Gb^cSG|9rgmzh5)b ztUt1lSXGw?{*y64qSGFbUfFO81LARvT8es?fY-{Dv^0QON3|f;whQMZH`#VmUj&yE z6xbwab#sO2noOKY^RpuaEdS*E87K}>3WoHHC{=*;bvXk%g+gY(zli<*N(9pxbd?-b z$zgvk${Sdbo*7k_mqDVpW!O})CF@nT1CMq7ACmd1>pY`_!YUAJc!ayGXw^8|=~Jy-(L4L%9l1zi+wij41L z?xW{rEJ^3FAHl9w$*Ck}((M9HrABZ4DR+b~z(I#UdRxyrqQMYN*UVneV1+^h~OOK7CRQJ8w*ee}?$ByuU zDMcY#eT8>HcA~r*eytGgBv2jX0c!%bglYfIr3ol}7BeL3Jes@jWVu(jxPTKc=%nUENT?BVAk6lh-&H!%k=> z77=?S9t491boz*$7XNloHh}W)2TJ#xtU0qf(j6%K?1lL`UMUP74C=T96lb@_s(T5X z^csM;tocXe&HDL7wb`PjBWsaeI&Z?b-X(I)#pptHu>SG+|ATG)V8`+QEZZu?|LT~P z|Nn~q?=}6`bR&o~&}nI+aTP>-h9>P=!Xp4LEzld93Tt5Bcs`bUJ^~Ffnh$a39mm*AhCCzHMQ*v4tQAm2EK{$ib zmkT6cwMCBQ;AqG{Ra3;js+9$$GDqAQF`K3ZkBQBU*Y8VGwWHS_y!&siIbvVu_S+}t z-CRHQeyZ6JWD4sZz_tEJ-8R)%Q6Qu(L$)d|Z}LL!^R6Rfa)7o5 zQ;e)tW_6G%&e!U#l2Bi0%%Du`HG77nVi>iiBXnaZTY|E)Pc}AH2q#t?7FD48bDFh9 zx}+$XK2^r>;(b!OtanM#Pqvg5{uN9`wnoLT|CIcH_5u9=IwkQxtMi|V|9>R=zf>>w zo6P?-cYKu2xpMcVE5Ph6^jdhh= z!_lfq%SG34)>g1p@iV-khMlzU-9uA$DLGaT65Bk;4F~8^WWGwkP+65)F}QW~f5-kG z&9+z2|FvDavj48=|3~NlaW)wT>bh^i0px701IW)yJV3t0JIzfM%Q<6qY#2tZ;A-A! z@+Iq+x@X9|FUZgc3?Z$_fmxVM__3M*@4NwBLq`+PnOqLLrXWa9W-0llOVIw0L4;SJ zk@28)HRc#Xyzt2z9lL|TJo;edeN0{ABM%X{CenM&Dd<_z4VB!G!TxeFFS`%TZAEy> zhSvpQs`}R&lqaf>i)d~DxED*<>ZBc%cOS2!NgNEC=oey?v`^WW!D>RzHgNyM)vyLM zh0Aj|ggq^~eUlsv{r=lc{36XWui0*Pnq7G6HM{WCMl&_yXs3xY#1H@=x+w2;@}7CJ z1Ei&+`6q;r1hbsxC9dbhEEo+%eHNhmq0!{F!Sf}T$oc1~wb-@t*#eA`ya0q(*YngR z%jaxLPLTY|$xYBuo)#7EAu101cp%;a$$$j-x*;q*Sa^LN)gl_8FFL*$-=7SEOyl(J zlO7O^IJg3MP~A2JtiLllhhr9^S{yvG{pokjP7Al;89U*}J>HkP9OC$*gCQ2el_S<5*Mh13=&C!aj)PbmM< zan||6C%%8Ega2+a$ArnkC8#E{4j;EsI(r$%4 zeCLUI6>=n6d>NM1kew!?6A-@$HOFMPfOV2aqXRaH?}=w*qG@Tv?BO$ubSe23Dg71+ zfgxJ*Eb*C)78TO*741+IhssP9c9Mpi_n54rX9e?|uiXSmE`3}slc)%^nRqEB*+Dse zLEpiNZ)UzH1yhz_d>uaJDb$a{K6w=42^U%_mKM@W4v7W6%1YQyldCmp6-m!H5-(C_ zUu5#>8cW!GI%3_6G?QxCss!E4ZU7UvMcQ1(8s5X_pla-m^<5BbbxQ3I26U|o?}aJ3 z4yJ_^x)Vi-@MFma`;5EfXUL)pPPFC`C&<+i;I2wQ*FPBXfho1omdNo8sW?iq(DUJ9 zgj^4q_?-Nvfebxo?9_3jgHqC$8H1oG9bc-NDo2s&62c3CaUO# z4VEwT9Z>`~1Jif{9Ahj|LpGvK7f}}SoCJ&0x}G+Y zEr9+#pMIT3sIhmp!2wwk2yA*XyK`~U?oOQS*nJz1hff9pYJ;F9VMC^SKl6Env(~CA z-2vMnHcl%uZKS$asn)ettyLXus=6T$B<|b(r>Y4aIjl%x3!2e%K{GYTxPA;s;vD4sU+N4mH$x{^*cNX_H zo`@|}Auq7$0k4n)? z@)~<&A$@2f=(|rmRXLjwccXg6V~-T^Vv2Y%8x${wcB_p4QobeRMitX7CjB0l*B{M_ z_s_#oN|+;`#+jkS0=+!Wh&N&bhGB2YF^mxI!*E|_O2wk!gs7-(rA(9<9gCz4^*oP`830|Q0m zoG~QRY+kY0JU7CxMRcC^N9T!%BR?HXQbQ4^=L;WGrB%Dvo)g$>HUL<4{;y^3 ze>L>N{=Yc?d363?mmK~}cYWM=@MnZFiTXe`mQFv=aS~atMeaY);|Bp{!sSPvS8_Rs zd@mno(sexf5zHM0_wbP%!v!CMq4p*~yn>i}kK*+F>pK5;+_hi)y&?W@*3xNmJ z|34W17wEjS1?U_Az31u@qr9HF9TdelPGSFHf6V?s1Ttu~I%B^yE3>D-eroJb1>;C@ zgIGq!?EKZ#G(wUFlu2G{5Uws*A6PhF8MOhW)14Y9%-jEPM2l_z`_m13Ge**b3=0z`+)oQ)?czAqT#@b}<4fCroVBalrULn!kk3=+{_<}I}%iXFl ztlJV6uiPf=@v&ZlssJonH<^sQv276DnaWzM?vO*3 zoy{sy-mHvtJ07WJG&@g*Zh_X$A!)1nl2!Yx#;PZ*&Z;m`9d^>q@IPMP11jL#-aXs@ z8Vw`k|DkC#3;e$!_}_6m!)cc;6ok+3aJNKnfT0yYSmRC`5%LFkKpq`|y0w2GkqY6rO04vd4wzS(HPp96irZq#8bYBj?& z6)21r{p^`&1cpMD<2VQrMEW0Led-<%N>o-f@{B!UYJ;$oXg5*HclslXB}(z3#|!Z} zBnx3uvJh4a3*oP-_ZhWMHE?eHuWDWRdt!ko3J-iPC|^@%L--LWO^6VH(D45tJqapl+uXh>(@m(|oYBFaCysK-=8&#*1>`=Ja-{E4v17A&5w_qbm4a4o$V$X~Z zWfAc3T^zo329E#xUUw`uc^tMxzoJEzN|s|%ekqn-1$0wJSYZFaFqMlIqUX5Q@FD5- z7pDKT={G_CFXF!!^uM70h5Uaz^1n#_r-C;VmjyepeTSCT*~n+Kke2^BBXdXjf6+LW z%l~(%C8}iWaFTQ<`F}~(yVY7M|F7yTPyS!PWIh=E=N!?!)BkMzAH9hGT+si5{ulH= zLH}e1{AxJ%)|63a~JeO zV-|ZznaSazq4WeH4^8e6V{qd-k<;!$dy*c9n+EV^?CVt+N_hx$L3=U=beXcA;pv%%j3$ougmKM2Dz{ z5Ny_N&Oe4mgDfxSUoP@qTd%}wFxlN!ti@-~*d=ExSr6?g`Qsmd_GAGz|Qo;!iJt_;ksr{TcJs*t-&$WJaci^)8H7ucOr*{MOWcTmBN4$nEBQ6+T+5 zCAI5%BLmZ61Tgfa=7fHpF`>^*3B|Di`phipcGZmQ{MRxxw$uhO>g-?cJ8qRnE=tE! z^n)GXl2WM|jd;dU0g(FN&{iHVUd>e860AEGmWJWj50K}ELC(&8JLhe1=#Td(n#YXW z*d0EL86JKa`rr9|a=-U~8nt@%{*PYV|JfM&KN@$ZLtAXg|NVeuY-ssDJ4GP=z`f_v z?nY5Oo~ZWGReh-}-~)D6o5IUJ3)=$W4iW@nPM1^vH+7T6p4%c?ZK~k!Ng{Gq1uBU5 z|5ZU9V@02*oCo8r9OOIbl1{qtMJ{pD%iqL`aEhv{X1emrbWb`LpLu^qUVq(Ge;pB_ zx2%YgiPgnxN?UJmiT$0>m+}_BHb{SEyP=hKgorYq>cS2{B3aj`Iy-|N4LM(9(R_bj zINw!YO_$&Iv%j37t@vf`FN^OlZ zzQfJfpL1pLE_s1pH)oU7Q=6o|a+g>`dyTt<-$UmAuk*bU?ilood%hCV_hIf0yvn>9 z(z#C%yrs@}P3<#wO|_=3+&74>xO3l|fq5+i^Ju~3_Nphg3MNZ#KU+nwNcrHKWU?+= z%2qM*s~9}CL)1&&&Vn70*jfC>c`d%P4pi+a>Ua<0XeiuaQJC8j-mU?Lyu$Ej?v2j6eFlmr3|8Unn=q8#!d3DPN&cc!$3-A?yH>!{_Wk{qgWB+Wl!M zDY|PSq(~C5B+r{*=wxOBa58ersNbG+9kjan7FOh2ToRax?4FA(IG1vY|GbM-!k0AM zfH868C~!lOqZewtK_uy06CsVji)Z^zcWMp)_(QO^q9G|9q*fy?7G$#WgagF)^ivLN zBt!+0750U!(6HriMpjT^)c7OE>$lypH;NivE-)lJUMMzv6Du5-9{9iEsBQmFsq~c0 zaoi{mpJTa8Wsj8-2>&M$dwL?Fu#?VsYxT*AW>ti6acs}0kvyM0yGYea43Oq_vq4hP zFE-GZvwuA6roT#hsJPTO9xN8V%W&gc>ca#WuE16vz;xH=+ytdWp*uD?* zZ*gSX0^HJ~2Z10{r3eGJ;#iT|qdx)SQ3|L*!~h6bFyZtuLTkAK0Tk1h6qC#-6f>3< z4|jW6AcYzs0?LI^yr-m=pcmbmS(I9Ma$s>dkSJ2 zX3X(jFNY7P_=OBoTF=|vg#)TEenfD$L4_K3KDg?HxDcs2uk(>rVK`zOsMuYmB@dP4 zSQhwP969$z#p{y3n-8FLrlX(-9Qb)z-j$~u8xX#qy?iX4ePE?#REbw4tu{G+M#Z+G zY=?lJGuj-*Iq+n^P5Z#LwR>omjsw0`14~N41P))GzW>;M@%qgPHNE|1+BTBoCMH@e zJFrV61nb0VqF*ejGGDIajv5n8Er|#@*~HjNT{UvGwHxTD$--VTnv%;5b;o>5M_tUs zIarj^OI@3PqMAMN2Y}7&#aLuKO{y?{4%aIyx4RIJ@dyig`W4XsHB!4faL%^G0GE}HO)y6Ij^CXez5DR?@E6{~61wO6Ux?nJRE@OGxGAlZnjZ>GBiyB| zB6)EFC&(Ge`zYr-QqG56Dx~|cIBcz&%@rlidDMZ-@0H4EJK0ogc;PBLQ@ z76jw?JC?kyI~j`R`{4NB8;^s%u00yB^#ph&{x>u6zv^aj|7&B;e>pP5xb}n&5gnf^ z85!pz?41mDH6->gC+x+5z5GL%eus!(;Xa_^h<~OD(=B|~>6x2!adFJrE$M*rXC#dY zVeA6v15ez=bf5t}(gb;VV?Ci9iEan;3(x8LT(H@`WwqPZ>e6p8c5hqp!W+-Kwy3s1 z{Hx+8;-++EIseeW2}8Im+Y=_!=?o?IK8uloqxb4$Jh5Ha3h0)KRZrP3Ec7w;3n1d^ zuRzMt+Z1{)yWT333eFkh*Ns&FNlj-+C}fZEqX5J}u`GOzZ16;4FM-3wV4u{cL>Hbg zMArN-x1gdIoBvJv1(Tk^uon`2=z*jkdc{4()wj-A4QU26#m^Pe)ynzPBPuA*8;}w< z1);eWbB|mz|EBZmea_E6f%cCJ}`A$D{cI1;D2Ln1Yi~Z2c)0D|7Ico zZQA({_wumK4u#|N7UJjsLvLx#gZ)|sG4%g2LpAFSLx*CFl`iJ&eJ8N4?D9>?g(MT) zSYtW*uhLaV{6C)fh-Y0~>hv8K2tgSrL4%B!1$n52RjN!@*Lf|>q96uzv{dH@NcW7- zQ&@SCbw_k6m5<%OFalqVRvUK`Ip}-G`^HaCA3l)?dmz-s0m0y>F*6M}V7U-m)`tIU z_lN(rEdOs7{(m1e{(q$Se!{p302vQQcvcO@?fw<_mrBNA%?b;}h4Rg6g_wR}`LNAp zMGW59s)A)@Lu8TF#A?DYCToP`rRDZEPtNI{mk{L(oWBTMG#-=RK=Om|37$%^tI#0zjmeVn>>9lvsE*t1+wR}?u7NGn)P`B!rRq-? zRMMf3sM3Hci8{MnrW6Iqw*va7fc`%LVi~)(P&3CElUD1<4R9a>a{JVs^kgH ziCYrts#@cheQ}nbc0a+eJKRX5J8K6O{EHHE=@YtE{D)CiVe5D{civ>3LG~FHJV*YN z-?+Q*Z0izjBdIRozGu%g*(Mefm3rq<$MFQ5ZzoFNMvNA}`*wVUUrqG~<7@fN>cB$6 ze*#H^5zx8_h{!as*3i#g?_@~{@x3$pb5WIk}Wm1IOCadhT%E38_X$$azfK5fY zI;$b$b8w&@Py*F~E{s?ohKkLbDz?3k(5b5irl=;SsQaQTL%9Ohh1|dx90#e2^@!Bq*U%}2%Uh{&(Ash_w(_@2q(~|d{SWPc|L!yTM zhz|$Ta%&j8N@7Hss_)|^)CPLaQq3m*J!oM3Urnu3xMibGX$O?Qf1WUB#!@=v*Qovh z)o+OUCe_z#^q1=In=t7SU&IVzqS52DN3@W_pOsM)qaNiWGbla^T@9HHDqOHxkB0yA zd+Uz=up0jxhLMi{P-_(Wzm3HIfH?>W5stw>b;O$dCSR}B8rmQ)cRjoPVTiV0i1pJ1 z;g(HYr3R3oK#12OVsxHp2Tz2k|53(w;4NAh3Hx_bTN3A;y9_{MX2BB9by*hMxv&GP z?YJE%2n3T{GtA1$+{i6MgR99hwcIlM(hQFms^=D>eyGP8lAF06Dq4ZP3mv~51uNC-zWAJ}IM-Sq1=nDRrHjg|OMspywYXw^z zdxr-`&_Nsc;OV4P&O1p;+|{P@CVpKz;j67CuW(}i2yXuyVWP0k&@jA|LFj0U3_3KARY50#Oe~P|6EsS~4l81YHP5S%5w9w@Tz3A}rNxqQlX1`+%?a=rik6Ucyj%K?v z_LR+c=kWK_?kAr74_+Frb%x_>?rc>x!$HQ~cTheeFe`roKcPdz^!RTx?(_ivyalr4 z_o`F4a^d+lfDj&Lq4q(PnNw9)w@FQ9lFkBkYl|niB3X@9&V~Ll^D-b(3 ze~EnC&Y`h`cfEJq9pd0(6R9x157;4o8~7B+#JvVgt9ET<@nUl<7uU)*7XOdCwsVDe z!1Ct)+z-%M=VX2OzfrF*mjAW%`A@T^7xtg`@(&OS4_C-`-g_5LU=3NOi#&-n!UTfC0YE7#h>?l}sHncA6A=^21Mm7U+xq}tFV8G7a&AoG8W-iLe=0Dh; zGw!mp@4^CxC~?^7*e;0Z9VeLYD3sUtoGvd!SdX2dBcYbAo%8NR;(^vOG?6wpqLTF% zx{N+ow$p`8EWMpAZcuUFPT+KW=8i`W#$*#sV;VgU$9+z-=BZbM*Cq#x}*-W2@l@gygs#1`JO~n8AWdCnA8d>~bGYkCxQ2hTkKTFaN_MT6? zJ;1E>@OyD|V(sH2y?tG8Q<) zz8Vjw6iaU0gUS{N_0tQe;M&u`gH-$`ti(5CSr8vp*pUvIXG4R|6`&B1)$Oj+34CZI z7yzn_k*sl?ocTC%bi8Vj|6AZ~p}K`5fD+f%mF-t>9B!9d2(HI81fN|y!y)U80k!); z+Z-407*aaI^*|wEV^>jA!QR<@t2(!#iYgYvX5=p2XH(zqGPD2#By6uDs~E|+Groe- zU1$gTgVnFG(u=9r2bzqCe5h8~u@y|AEzB#c)R%}aj7DC5v=T56XeGX=Vn!qMug9z& z-oCOf#?yf9oAEiY*GMU_39E1OZ-g&mxO#lT70MYpJ)4$B%uker?Et*LGeRW`t}k_^ zC;Ou9^U?0XoPmGvTeN<<%JS!2H-KCMs$eCWJry=ob~3OgjyGoi%}(v<<#^~{&MR!| z#n9?pvcVX>UIj0s!7l7xTVBtnFP&JIExGF%`+hud+0P)r%&FK3UPCD92m9EYvix!k zkBjw!KJE!EfszSvG6DiQ5`l!i9pOR2NEa`3bR312BuD(7ZFS)*wWv@s#o!9AH}CxM z(DGi^D7edkVb%|{gL>1hHWXauUtyKeFted)yGDJt)-ZQXJZ#(0e7cF0@w>|i3fG(Zb(23etaZ9L1Sry-94x)G~ zJ*d*;q8#SykRKl%E7_FUop%GOO#2g7&bh+QXsb>x!V-FZ2^)=FAtB{$D5x;lU|6vt z7K}ohqXPpRY5YijJ2h(jyi`n26oYb!wPlR<25(%2XIUzNBS)~`Y#$37vBJ28f-!XY zj&>J&irKt2B~vx5G^PCAk+CO=QyD-$(++$gSqcx(MpJHSI~iY>cv-sCvsdF0eR^$M zYA&z@kKJzX+7%3xWR`a+98goDf^J~gpqf^f zrEiRHxM;TVTWZ|)5e;3p_ms;pRR2`->rIoBC*~jVAS>Y|C2u zzOKwuhts%6ARoO+vG=bInYao!^^tA(_`_=$g*EJtJtr89l;l0R_sBuFT8S%iiHyLW zv|7|gh2%{x_?=fc%Arei(FHlF!xT)AB1kZ&$2l8;WIGbR6)ZzHc6KHHSGIXtyzZByPVEdo-tDTakXYzb`;(rDI75!!mz)y zK!&>k96G4P0ziocoJ@xE669eyDlso7t>s0HzvXxm4$DxEya^m|(xD@Z z+&Y$mr?6V(Jvq+`q4Yi6I~79PFqkkiis9-5P>Hp`n+Lu)e&TC@KH8=vR##z1`wqzS zDOFoqX_{F<*aGeL;qj|>d!2ToDfy3l|E^2#cVqou*8a!T3jN=`>HkE;tZ|T3xsm!x zQ)^_SQ>$^s7GSsFILZK=0E)A|A5?UU3-vKl_o0O`??yQ`sDB6qFt*7eN7uRhx zQgwTEGgTW>7SP>LPu#QwLk`u5<$&V$XM(4`L2&Z;{-*6%`BI^oA_&Clm?qUY_%&jNcan-b0_=h<1 zfV1G@rZEm*xU%A7im}{GUS4%L_bnxWpAJ8s9KAng|9Hl>bk$T3v@Iz6{Oz0fACFGI zdv}7)Ew_LGqnrY?l>Y!L!5=!8{1*s3e?USliqez9QvbGyq+$^wkMcpZZ%`&0kn|_w z6Ko;oHBb&vM3~SA7+iKld_W|}NLe=_-!7JExkU-UCx9XD*{ozsm>|4fNX^F34gchb zGeYCM6y6chRikx^Vt@kJBEXB?jIKh5efE@XksyW8nF3vo0)*W)p9Ke21e8@60a8{V zgK%p%{T4eSZ)>OuePVWLds72Wd4;^V{U82m{zOUeE|ZRnm*L>PNKk>|-(%+gjCCsj zR>}W0!_4r1&EoubqvU_X%*y{w^X~G0tnRJl|5)AI%m3!*i2vs2N&n{O2>c;dp*bP->?5C(=-eJPn#tFgVgwn zZv+OCJu$10n7$UkMS24di)K4Jz-e0W3qKdLf1@cvkD+(X@yPC5EI`u5R>L#{iu9t_8pZ6MN#S025v2rvXlw0a8mqF4@I1L)E1~C6bOqYm zMPrUuD;)lAK!F7AcG|c9;Z^#ykFSG}ixi6sAEt7_&({<=Ief-hnedyHo~#PHsPKj0 z!I}f!jywXD=XaB_@6ZR72Ys5R3@8vdSHg(NemL6WPMAqoLHVSwh@Ihf#%`CI@+)`- zJ0l&!Hv3@cB@N{=!1D0C!(VC(+<|KRR;pH3I^(Gutc)_34J-EdfNY=*(l<_ zJgEIA*T$T52Hx220AuudeL^ zikovoLUwG8>{fCN|Gc(4mo0XJtvWU!J{v{_vvC12jO@<9a%pfr)q+Zn>Ui*?@& zZ5Ywi?Y9!E(U-|=JaA!JV`z$yBp{w^0ChVc_4x}2$2f*rK>rv&n}!$~J91mcyalSD zz3O*>>4#nRvtxr;ZnF(oOR4^{oVyjSYZ zcDs0TN%m3;Hf+7Z4u4l!$yDpjGT&{`RB2WQN)8Y0ZUE0MCVWv=$-t>g(~+|w8O5`G zG=H>wVK=QMm9w;j@qflHCSlP(s_B0@DUls3ZZue{=pQRkD*qh( z9X@=~g)y3!5AgWE=1Z{#->Yd9GYL#s^MsA{jGCd3NHLrk-)b;aY)cnT-2%rIF= z?m&M!5{1I=fF1z|U}))a3?;spdq$b7g1Hp0|>lSmrfaO$8yu$^z7v1^z_X97DP-2NiVi==|$E_{K};AWh$2= z{|>RX=?I8o(%6%4Vi^d>MjWr&>B8Fc%D2Wvh&?48}?eehLC#SF8|9Bd%2G)OK zsCti;o*3Od_^-^x7`=+}H4q?bi;FWcK}r`gP)d*zC&^rp>hQDg3eJcGqd=$9@R7=* z`b`8QDMUdj_ld5>C9uWYZ24CEa@#FF;Ow@GayMAOyS@Bp+_U^w({k)Tn?@o3J)Hcv zLb@}AWVKkhGeNj(#Du%lEiW$ke4gC1NbGr7sVBoiMb7C3uXKA>3AiuJjWl)L*&fLR z%UGg3-eZ02km})Oo!hcRtH!h|L&S*{BMh6**Kn(r>W%rO^Skgp$n;J)kEM2}lcCe$ zVe)Rn53OK_1SP^y0ZaKC8j6ZH>t;q9`oFfk?%$Lz%QK`IhUBzMa*iR5p5R)!`fhh3 z5-hLJlAC8z-VO9ULssCE=PTTv(DQC$3&7VJS{{1NtwLi-HEc8-Ig||7KqHdhqkLZj z!|fuzl;d96o^StcT`bQHIG)swx-AP+9G?D_&wr)!)%*1SX6Tvtzl~bGIRAan^Iv`d z@K$Vx9iMfb9x-pEe~r!p0DqwB#;7oE-<@KV!ZYF??5vc^aYmNfz4)>3FclDflsfkn zks#;tjZiejb=)iI)ftff-8nzmEe&@|L%cwYAt6V*Bbi^0733!5>$=2=Gn8k#bSkRQ z!YF)QvIn?BSTYecaoMr$j33*g$M=Ki;1)XGU*lQreDPT=U6xj2S36;vfGc8`I*DQG zMMtwU_IEauBxkndXcqEjDQodw&Kc~Cn*!g|oIE6Cu`y4Equc?{Lf$E7=ul}E_pBUq zOg*BnhX=|?W|xf|8z~Fc@F{f`KM;HQ@qv+d*j)*+v1m)p|ugynOGNmsT!<$!s;yc zc~du17w5?3E&TgEcKT0$?^*w&Be75Ef0~e8(EkUb{|jjNipyG~Y4WC)Akr~k8FZne zimpU3Z(i~|Z?7)YISzuAs$+wHjHY#xo()HEeXAIBY^EAyN*e%UK& zHbwq?9C$faZmacAQ0yQ$bMV`uW??R#=bU$2O{v8-E+mBWK=r2=S2X65Qx?u=gJfzZ zu)vIIZ2Lb@YGlp+B!aj79~`afS|j_<9U^E*lnSbJEBwFi1AtZXUp;I8)hyz_Yz+R7 z332y80d91qSM&raXIdq^!5gOVNL5ja&dXGdUFwieW?Za5n8UD|zda`f)QtuMd|x9zuM5afOFkWY{i zHZq*T7iScfU;SQ9|CcZiG1h?3ZUI9uPbb`nMG#*i%PAD}-|slXp@sWweLTP_`LEu{ z#D8y^h5Yw`=l{=L7VQL5@{U^E9V9+qwirKIM>QiXpG$5g*hLH7&^kVU>!IQ)usw7L z+8cWvTUmO6h&*(59e*;kI`+tR>EZ?YvI8zVr1og!4r4%%=%Y>v!*MWK!c9~02;shoC zV$PYilAtU-J?GlP>Mz`@Z1q?6oV%E~?d2Vu*TS1St7r<@KIc0UNq7^PkvEYU_vKg4 z9MI<#FMpk9q$&LG+uiYf>;KHUp7HEs2gLs;_OMs=osl!NJY@3{4x&%&u6#X(*gW4$3qCMc&#yr{706=VPfwDyC$yHlJ(i%YRkXn`8;?ogz-OK^8e zf#OgK1&T}2;OZd%V&`H|1d{*M(36tVKQa;}3e; zumE%9%j|q7_0xmzd>@g{&ud~u)Hso#gZ5PyU*$*w2kPiWZ1K*NW*r`$D|Yo>8p)Jl z6y!0{&fMDbhYv3cj%?^z-!J|2{j7CmF<^{`ZxGn`EmZ&D){x0C5>^Kx_S z`Rs}QS{p;|iUy+s5j7@dldJ66j<>C}1zg|dVITC+iFgjTHVY?x@w}|s^A|&S5gvTU zjF1BN+R&{-#wV?aLs9}po zjBT;x8BzC#x60o<;1->EgnLcS;gn4vf-vRvW{CAEJ!5UJC|QeCO@x3Fe*w*=x4q<> z8UI&$?^xDjdMne2GYfHC%}akzy2^`-u3P753tKB#^y^|3gB;5hHLgI8k*MSw8ctFt zM3c5;YCOJlSrd}!fUg#G?g=dN=_9TeZ}SHZ?|-DP&O?xQ(IIT#lB%o zc1h4$TF4yJKaIl|gyoU)@^rYMppy}E4YP%7qA0wB5fRmWP1JLoRayFxxZXa&5qO84 z4H12H1YT$GW5w;7>Pw=qDa8-x2(K=IvoYFgoOStHI3+Xs5mDxOv5%aev}3JX6twM3 z7dmG~e*4Gtu_C%VGlu;^IQK=&xRm@I}RscsfketGS)rO6qw&4gJUY5(=MR&+RHc$V2= zCCT``2c&T7_JP`oAM+Q@4Ho}9$lT!ell&2~qZ*ejA`xt`35-o%>ni{@ zXAP7|=%@A$f{f#6PEs{i`#sZ;13v-}Am2sx%UtoTz|s!S01;R2GfdSVsxjOUA6FXE zDHfaPagWxiX-DZeoy?qo)}HYPznm$k{oc?oXs<0N46);!Qv7cSL!y=bbp&jX@iERU zYT$deQSH(5ZGtGDEpn$x1z+1=+Ng1_GWA}#-+LCN#YAF;?>ZjXZO6V?pQu`t#Gi2M z-^P7?x^=KXrNXp~&1qs#ul4m;t0l^v=GYD#WklUlgN9xUUsB{>IGy>F<>6G*kDr1} z)i3|4QqTB5xxYHb0r$JSysk?bASN+5!vNhRU&RoawfKjXb2te`l`R;R_u^>Xb6kr zzjA)9#sqE)2!vlmriy+#xj+B>*h)14zR2QWis)Q6>W;)Fa-pFs&^m72j*>9T59{xHxrj|d1Jh&5?|eIpHCeX_2``Jm7J|Jv^!V?JWI{>7Z1C- zty*?UT=VzBjgEBsJ@uAK#8kYvJ-L11okx(EAlkmEZ7af5_BCA}bHRg4Sxf6e@T}L( zhnR-XXP%ytypMdMDvOFJ@j07YHv;_IlIaVcHKG#QO{$cR6Qs|G?a5Jzi$DoKeE=(IQcIl)u=n!>-}9CnlO~soei^a6 zV_@`7-SkzQVlDtxWH7wYE90ih>Q*13TzP}H^jA*c?irCZPAT4HwJBi&TeYWw0Que@ zBf7cgny0Ya6$6)5A1cNb@E>_@?{~VIF>GI|sWk$&3K|V4W5zS3pm!RTA@K~2Hp)Z0 z<;*91`xUkYG%d$37wm!zcqX~0#jZm2_TYG*a0(#fH0y7>IPcw(&WC ztLW2dmQAZbbuHIh@wyBFjOK+@ObRpnR7?EcFmxIW*|@^0oi&Np>$K*yEOGpFr#MJ;HA_>~R2&5gA4GhGL56^OVCD?Rv5 zud4LJekaMrpv6ahz>|J?@jHQ#wb%KW$6j|@^My^EP>~!@SKYt*@8|kL(k)Ef3Etz@ zz8D`)$KCIMX40mzCFP4KmxO4y*cHdoFETR!^fCNPQ?^+vg4HdetF zB~?+;Z_kRe@jg~-3kpQs)A)rlJ#Js-B`D|RF}8yPk)oE(%$LhfpI;kYyb0lp@E%dX z^5QA|cG_FI+BC=vo49aXCyg+Jn|@JYvmns)>`9DblWE|#u4CA$p%y@h&Y{3?by@la z{U6Z;Jvv!W%-4+Z-7y~331=+r#f8y9x*guO@uqNL{?Eg&F|D@brGrLYx z#yZtO@+u5FTvhy7KRi#EvCgj`@pCqzC7{2AldA2SCD5;Z)Mx!kj zbFB)uQ=b^ne|+vTp4vQ~V``|L`K$POh@=0D2k5yo5wp$CpIb(tPHmzeQbzRv{nQLl zb#OEee&aWEwG?k{Tty+t$^g}CGyRe_^W;~zQNx#N3qlA5gC0*980sP z%ieDZ>M738l=s5uH6SX^B>kaP;NTry3AAy`(w~bI309c{4Pt`r|fu+ zSNUf{)q0bPHFSU^fMAi4;q-UpVNVc4Wf4iP^gHe!VYDr5% z!*iF440?HdeQa#u9nG^92d{9^Dc&O9=tb#li(A^6d5tP&9Wm7Kcz-<=ueDZ!rC;`}KkZO_LzM zETS?}&=w>%OUg?;Au1>+%kq@>f+M?6UK`Rm5`5{z8cxx&y(ZLdu4xB(SDzFFdBTym zHpX@+%|~NscSCa{fC#-_NPrX!3c9ms{Vaex$n7rwh56#DLpINwTEqw&5DN!}?jDf$ zz<>3_lK=u_+(SB2&2gpVMJ`RjdP+TV_DSgCF!|+s)J_bV7y0QYs1$4)(8v)F01o;6 z+4>$}LH2M=LF$Y2pMiugyg`4}o`s>6nA!>mC%sTNC_IF6rvfecH(udD0fp4yy@>*H zfer()?)^~pf(`hS%<=&wlm{QcJ9rNAHd=*J96`fwAq0+l#1Pt8W?+Ok^kN|l=n$dR zBgzMa>}ejVp7wu5e4swrJT5@{ROKuG1uuw{-A>b-`%BaT3RNj4h_9_nfr=hamJcGG zofHE6I@3WL(W^q~e2;|YMg!VB;{cNwyhp*6w<+F_yYQSN(oGt5?YAMfF_|2kZVKOV zh~wOyS+NVg#?^_K}E=Gp|calPg{n^h|1U_>fjA(&0`<1>mCm(`&7V^e9Mb+GIBJ^J*fKE{X zG~KsOh>x8oC%|`wA{fxX4Lf78&5DVgw6}c^0yh{Kp>FgdT6_P!H^Okfw(%zF^tQI8 zpp+_KJr|U9MH}HksZ97J-`e*(Xcq>qU#S#n_j+&=cH4)b0Eu$Q0U?AGW{?xR^0H1j z93i9jAa8NcHuzDKm|Z1`MKtSPpeZmeRbVn?UdmQk_!V) zG!QW27Sd(gPTM*602ZA{fSe}LOR2hhcLAE<*gakjs-KzF)KK5Y@(wGeZBAp%~8t9RG{Of*QS8FkY9>mAl5fYyfR)%~9WM5=Q?Q`q)Hep2b0(!g6ip${1TpjBgF-Inqe zB8G`fMMCUg&VEF)1D@-+**saRy%famE{X^!Ps?X2dTp$#P?gt z;3mt}VLhAe%Ydlh4G=E}+;Z&$Ax98%))%|*I;&d7P7SL#iZ7j)ggE%z=ovoLJ2q=w z@e?>-tEiBUPQnCSy_>nxGiNi%1qadE8|c%j$A$AqB?*GDm^U7YPbg?iw1RdOo7U}K z%R84@8nG5Yv3*yOLPVPu=fz>RqcL0)!; zFR;yBjeR`i&J$B`&nIRV=ksluAyGX(pvb!noN?nT-zuE>)oJmc*c5h^!$gx%Jd$wc zhnafg#p8uSrh|w4Citd}P^wxV$P0nUm)m0iPytvT*6nXfENg}+D7wlVw2^~73H!U1 zm0vdpb^mgR<=-n@6;k5vIpq zpF*K6HkgRER$WPxHuH7UApg6j$^ zEtrY7R~j{2-i`h@I$GB2D))Nk?-=Ms-rE8#bbUWH1Zkk(0M1=O|+tP0>_CQL|tb4oY+H%aGNrXGOg zJ3QkY==iqpytIUBApS83d z(z~BAlcl85vl(R>{~lGtl|I%{8?hgf{F6@m2sV@DtG5|JZe#n-#n{Xy*Qg-v)kKC# zPIXIlzW2xrF3PdM68;+BiaRE;5yUp|{ps(S=DLE?u>y{Dxgf8buQWn;8fc=#H7^~g zI+sKXg?ry&mTQ*-*dN5mDwv)X3X#~JWLLCP+H5B~0nee6=WY{qNk47R8*RTz+$@)N zrgkX?C5rf0%%u}@`17t;P&2JxbZ5G-g!-kmeB_L^F=)}3DJ##4`}Z6|E*|yqp*@k= zi$>0($4h(Tm5`a>kMc5KStN?xeZVMgO5IEN`sv?Xw}x?k)AAF@(>?%YLEiq`fOHM* zZIx;BG}^q^C3Oslwny4xX-qV<#%+Kdthu+E-?c}(sCq1`5lm6e zbXwsab_kMIlc;>7;hp-Nd=jWbkjLhQ#|IT}ZwtW)wB$#wEGY%x=< zz)tQ?WO%RpUe+)Pfl-ADe-C#w*Dp=bw7;`qviP<|Ka2T(zM+@?AVKh}K&fhuT*kJe zQiB5I=GXvXp*=(PJ`ovl)`aMoy58j;;7p_Z zNbe{CRDA*GD*_zzKfq9tVi-t^Jpi-3_V3HXpr@QJVSD&f?gQQ3P~e|rCHksDi;1te z0!GS|+fio8eX_>)Fm2)uj07^7oX@1sZ0Vovq)l1TWH9k^Vs^)o72$a)yp&ylT^0!Cnv`0l67*e$^Er%vnm zpr;FyuDnn31FiQTO3U2Bc65E*=0K3r)XkFQ=`($c;5}XWym-Ik45r>p+R)*Qp$JdD z(uDT1y(=sf`Mji#_rKXg8_cX~F@x<=1x%Sue!}_-h8u+FYw@z$YyLCTXkv5}E;qEj z#6KS1v}Usm^EA{>i<9LUpj*?^+18m?vCx?}_^lIJl?UA2Xr4RL(WuIwuLRyGKYk83 zL$UP(W&X(5HhOH))H-c)Q-A#75R~~8j_`9ak#CZr1{j0n=*@%8k`SKW&F|J=x7Rw^z_kceK`M79)YFF|bQY8m;iu3!2r48)y8~8o zAv%Y$TR%x(vo_;}N}aA${b_?{!kk(~iLYzC65)AzBX_{(`Ty~usAL1pl&%;Pi%Z|G z)-=ovie$m!r6LxIj(2`ZzUu>Ci`G4vRc{j?wBM2>DrEefD;nVUkciE-dz|&P*}48j zg(L$}ra&HV4*jQmaH*@7GE%z23GRDU^kniKgJ36u3JP zbrh9H>#<1`Y=5>fQ^I}iB5>1>lr0IA+~)C<$u-xI9$<Fv;pcII?0amYAl_HY`~JM{t)#uZTIJEA z(ekAO7VOQku4bH)97+1x>HPx)8t!aeUm&}g+<`fzlv7-O>U*6pudNv#5&;Iz&mL8foho{~3& z)7qDSgGYH8Erj99l>w=pEq0-(`=c~rJ3^xo?QRLfI7jnK5(@m%Z68I1X(tiQi z97B%65$i=MKwl$wX7d)bivWGn;dc^Jj1CD=q&P!70Mkzw>FlTW9k6PP{j{ z)L!)bHo`Q1u=5byP=ZdwR*T1u-OX>U)smAb3_h^xDhH}2<8{h$Ayd{xqm>|ht_=;( zU@x5Lzj`kgr8>yObwt4%^d^`PB|GMZNplc6VtD~}5 zP~v?bPz@I6Dz(0KBr;J9dgolU^=B^31J!t9bHTmQTQlVZ7(7F8N{tL)x!87419Yn% zYC&l?fVDobTtRUG{2KzAy0TxnfR#t+dq72>#L*$FtTQrx)13WeuQAZRun`+lskgeR zi?}lo6SuJMytfIPvBp)v7%1_9-*K#(x=0>a!=Omw{PH`n{KQ}2srmO*+_?GVVZY#l zw)r9PYIQ;5DMZtoKF`+OJ`T%PXeqDVJ!4>vyv^LO-tpvhX=LeZ8bx16&7{D5q<&5S zdvG?;*5DXS>@*o16=!(2Qr@?jbb5hHy%rw)zA6g38Sa^)R%Jk`IwdNMG(~xy`C&G@`ES0=8p)MWmwV2 zwlK6C-w)-tTXRKJ$o><;4ZXiK7?;r}ZX9|(hA^ihzOl!8ppwS=$y-VZgT8`Q+1x{w zwjRCV|6u7ei4)7Pe6s38dfI(%OhMnl5;kTL3A7YHqRmLj1Wc~9Pc)>n7DPDC-xMbs zw)zKhHkjIHyJcX(V#a6J(79XS`)35Cs37a9zQKmioNPuZ_sD#1FrIOA<_pH(qWL`^ zTa?<9UZQxG*i2{Hc+JY7_yvd*Q*=gf(k*qFuB@ud4L|xGdKUhvJq8~q>S5%F8n7}= zd{X_hYo&B7S zkdM(sf{w?G^#^rzoLGV3U3qTsp4|QKiA1F~r0E-r-{ik7I@pVM{3Dtf3Ij98IhtkI zEf;y+aW4g`5>jP~=Q2K|g_x=BEmD7m%P*)^3TakijZc1C`+?;6cKUzpBbXh7cHy!I zuj?yz-%cr>q<<4EBv_bFnufx=I?;G0jgoWC?;n+0Q9k~NKXfKyP)#O#0dgc4&oR~& ze>LqSXSF&a5P@;Byh_>EN1;eQ)$L`rImf|^kCZNf?xUdt-FbJ1mqR+H&tdEJ-5XOD zS{z_Lj_xA|-Iobc|iJ(JxXD$Y?zdiEQy^dPi5frs=Y(b6ZB)J#V%#9qS8f3a( zgo~mM+C(vXz1L&Z+>{fhXyZal%|X!3KOZ?hJ4U)D*H_2a%*Nh))iK z{564$mT@S^cFbpxs-)DasVT{LxI+8r4z@R=WYlO#7)_S8_=k)5{hPV6pFb=vCRm=C z3fw=0z~LW5XEUpvm8L_g)g69ecX^$dnHJ<6d=$@VC~vsFIDV=#>tanFhOFj!sPSJU z%#aip3ExD098l|I9KM#ePO>H^VQG3Lauv#DK8f zd_6I>tx>7CnmSD>X}Tah)7k0h-$;N9qQX67rfyb99HRwGZfrv`tPYnhNV^g%(I!uO zbOXiriXU@lwEoAnmtxRvk0I$W9H$*W)yLmh*HgKaL{KHr9nQ!R*l=!gu>k}gL+yPP zp0@d{aNxgil13WnwmjPsr;bH6Z93rPY^J?fDMwwskWx@yTOvbWDf^;W(mb9UjwMb?Fx!rKaF*k5g|DwcTZd0fjB8v~;|!r#5FIFRC4 z;5DNkj$a_Y=G*~{xwo?wZJ;#!0wEv9|5uE=g?$49dD;KuY#e)8F>bY-o>$s%aYmIej=Kakzt}2nWsqz$Xa%q2#}Qw(p=>h5Yx(t zJi9~raCuqHR`0;9(DiN9K>R9H1&LH?fAzR7q*&6+w(2FJZQFce{?JhC4CtbY+SRHILy}N~+{er#H;m1kq?SDmO8ufss7xVlcTMz8Yk{XLj|h{V zQkyO4Jl-QTJ`yNXuK9ruH*ndfG{AYi5(ARNRU+ii*6@s!OQnK6B*B0!lnoQU4Zx)v z5f*wO%rRD+(BUSF9Zp2cir|$hM2YqHu~9^o{uAne{LU*puY|%E)QSE@ z@745HhU_U9flF6%ZhEoCx8(F)^L$IqsPRi$L@;g6p2!25h{S~b71ok%<&F#b=qSnP zF81)~rVrtS8>=GalrCEQxlP!pvFgmn=dOr1Lg3CzA{achCb>|F5Do#30(u~<@<7%T z8|E^(3ykRlLWcz~Rs^b4Km31hP;cf zo34UADY`(5ixM*q5IpC(GA_Li%ztD(0j3;gpNdzuJ29OQsPbf9ghEHb`<=PJy?&&x zOu2_AMPk15YK*+7t4e8nP5FPWe?vi)j*M%!&96&IL?ZX9;ECc}98<8Xqy>6br}d&E z`gNl6Ds?0x@1aV<;3o=+JndW0Kw5I_D4rJmK*9I`P58T~@n}95PYkG{{M$LDOL!5eH-Z;{G z$KYHOvg?L$0^3-}M^noX7g_da-wfS{^Q1OPZlLcf1i7klqP^oYZd5 zOppslOP5dW6~Iel!z5EFL-U^Svwf9^aN+N?B3MrJXyc1o0l~dA5#9=4#-|R~Z<&0h zi4L#F^$LVaUH((o3?OocRu*4B?l-tZ)Zh+2S2cKcjTR8dBWj#2u^01LNqqZ>`-IaS z6v2_|V|(TN#?_)}HRXdsR4EbH*f9D~ z8PEewo&&@VGT?01?nkcF&b>o0_m@r>SQ6xg?PYw&<@AVz551T4{9ooaF? z@-Ynx^iFI>4AiSVyaxJilkb4tCef{` zS)31l>IaYkv3~#q3?SlLGhpCEehv}EL^?U!61^c6op2Sz>9ZUCQGUrWGdB4BEKNR$ ziS{zbSL&G}R#0iFT`3k8R)Hiy^C-#2O_Q0IXcPTTmTPHZa;Apz)e_a}{?eB#zRCD+ z2MPudK5|^4sp#ei1z;%7u8??cbQv!W!PUtRkIdGgP==QA&8GO7tMAuvyzT_jn(326JP~#px z6S^=iRFsppw0lu-xT)Mc%3%LT>!sTO?P=ObuP#t~2Lx_I7XCv%>yrR37CX>4hW)9z zh4=yzSu7dqM$vs{A7rf!{d>HvAid2CHXjw!VS^z^h1Vp_UXAO*+#&Fv1M1iOb5X^F z;hbW_rR;)s6v;A)A~HmDc>}b0k?|82@e}c?lraq(wvI7hmkER{2wwS02+ikO{1D`` z7&HEv)gC9}m-*#JaQ@h;Wod=WjR(lXtxm8p2wv5qQOJtD%zN(87Or-(g)u{Nl44M;%C?3HOstM~(qXNA z&jJ0O2TINW(f=@Jkjrbp5=bRLk}NB(AR_bjFbs3ydvjdMAVyRqnrSy;Xc6yp32kimekf& zU2nn#r9$bb+)9&Z|F(ykbXJ_g9p!V2?sC_A?hv6D}phuD>4Xg zckht*&pv4+C=-Em>rI#tu6Jbt@5Qj5NJu?IC#gjc#)}0e>b`m%YI#S+Dd^_ZDY z@;Ag+iDKbF#eo?&sRb*c66Mt8Cg6J5h5)J;wL#l}=jo+7r zt?8T?>u4LnJE&GtIYZ~iszgK7dU98N9)n4~Y=sGbvdg|K+AC=zCI9(ewX4upV;$p<>zzBN1P=CFRYo8~ZeUL6 z!sHXrF;o--;L&NG95#{>d0wGs_DJEWY9_)+nD8ae_?Ii+Gl}Zw$EYOT7>|#25aOF4 z(jjw*f%@q@T9nv|=Srn?Y||IDBdVyVeLH*IdUvgHraqHkAhrkc7(>}ImSZ|9L&i`M z2Gk=;3KklknkhfKA?4TV4tRu-ASa#d7vl0sQjuagYsB>DWBFoNq~T5?#-wZmv>o#Y z6l>r2V$UPOD~KZXhtOZV5inEZdESCQD6|U{UNCh751)Zg8|$-z$n{BfOB8?(6&P4S z!H2W*RsCZ@B6KmeK3txb2&}cw`Fh+v+$TDzT#7$v*gkV9e#}_k@6TG~8d7?N`S*vA z6$Jg7+2SYZT%pb~(fjnlP^HbH5le~I+6awh8)+vgbCniLTt7Xtfn6u{RpW~R64wT6FM<_5i+6(DTa>jszwUJDp-LPY~0&IN({D0$|%l>n_ zY191hl{8xYYo{YO8;UQI4#HjEbQA`3;=LdeP$k%-&OtW58W_wAw_0r|YqIiz-uQdUxQ7{&X3yl&kb68-vG=Snr7%&%pv zCCc6JD*h;`q$v*z)@%ue_K&B>nzo$qk*TV~m}JpzRe-0HHgbs<4s{d-w{5p)w>GR#Pr? zn{5zsoL1fWl2X1L*Wu_uXqal@J#(?6hr~^Fx5GraY4nYy?W-^07>!~V%Nk6B0-9>S zH0ds!G`M+xt<)Apg?6qS_O-=8GD0#M6tR4py{shfR zrZ^Ih(ulnEIPg=j`i$AAn2mqaQf5%|(r1-1f|sO%)*pD<iB9zlQU3UX}6#0snEh z)~>0%9X~^ZzS9ZMfTPO&-+^~;d4*tu5sKebBW5No1*XDQ{*Z}gbHr$%`FOk;Igj}@ zF3JF4&D;KMTsfa>e*~tl_4@%QdA@Oo*e7IcEadUgPQF~&zN|ot(7mBji}tY$lbhqG zu-%=*{4AQcOecBYtR_RP$K34mYYFQNtg8yM#wAsI{o=+d(M`VZfY#T)C^xEN0zLWK z1ZcGrX}RI7N=)yVn!7TMlL4V_VD9O$3yf2+SHFxXnAn=xI}3CGg9CvXH}8IYkK?}! z^KD_5mHcZa;d3Kxv{0kW*5~~C8VNMW`Ojr=zVYyRd8q}L6d2V4$EfWvTe6YHtwov{ z;*j9ISWO!$&Er=~)f8pO7+s~RZ_aeNR_pfjxs-BE{>8*_V2={)M`@geK!pRO03S6- zh72fpT1pcTU>%5xbh@7aSObA|Cp_%?*e*zu-x=IQ8IGm#RFHPvK^;9Mt_?*!ml}4P zna}QrAzD?zB%tJeZ8pC_Px>=+C(-Z6Z@4@8PlJ@kb5%ILJt?lv#%794CiVJN()N~h z!Mj4SMjsz}sdZ7Zyl*JTFE+=ljaqG<28sE{o1c2)glo32fywOMT9dT^(*(SAT&+Xa zJKN*#Z)>a>(pmU9323XYH4=z=ay87B4{K|_>J^aL+TnxO8n)$kYw+47`9YYevKBzs zpYrFE$+&T!xM>?{<2Z}Uj9-@TcZDDjXORO{`Niq8ukb1d;_7re%wGt6#g1ov>5A8P z!sRZ7T(Ra1Jr{XFfV;s@fHz+-@-jcY1V*LPj90}=Erd6dd;X>j1LsPr9b1b0f$y%& zyxC6ql>kLXV+QU^Wz$!8Cz)L!&Q2B!a1W5z?&b7lS(`R84|>h5Yo*Nds+L9VTMox3 ziN@4=pWNPPvJ&Zl%S&6Xm`1%1U!T9P-F3TIT{l|6PGQD5{P!UvbDsQV>}sERjV=gJ zH|I77-~$Rsk@&_M`}z+rF1)JVAf4cb`=MtZaH7Y6yT6w3Dh|xY z_g6@tVUil8Ssy7HSJ7pCOA>#mZj_JP|0`7@frr1L;w@y$fUQU$DAfRY1SU1 zyhD8r0PXxYD}TJIch<7SZSf1Sy8Sdg{wQ2arWB{x5bl0xSuOU#S-G|iSPYoIDr%gx`nJsx4 z1eC57Xm6c)ehPVl_sZ-;Mxx&Iby6;O|Eo+siSFNyC?`=s`{blps?(w~?c25;Lrp7O zh}krGkYx2!h9fHG2Py{yMUIJ}Vu}bmX7Svur{)+>jXWLQU7BSc*(2Xw2QS; z!6$-jF4fA_V!wkveX8aAm&luVtkdwrBXuYNR(tq|VgdJNUddU=G-xmq*W+U^DfC%w zp%dV2wu$*xO&z7ABiPq<|6EH;6aA^J$3n@bng1i+MIhYIB)104XyC@|BY3>>P^lJz zJjD$MZ0!Qn{g%mc8~{Td*k14pRb%)Aap=(Ase2xZuX3h5bh zQmK;gsd8ij7A{b<>jxJ!dN2!v-z;Pi@SO+aZxhVc;IkElUUfGO4C#+3lp=v{#TSZ?#k zy+OFk1kHkGR9A(pFXrG4Luthpjb=}zbXzoD-<~EfHX1>jHtFwYUnPXkmvAIvcv@&| z-G6RY%LLKk54^5v5k`C*^cZ;qX3Fa~s`5JZPRkoYFWOgY`}Ji-eEXSTW?g3@9dVaK zXnjJCxNwG=$_#CAH22GAUp@&(BesyjI2E8ft#h`Y zd5>Q0QGWY9Llb!8(V7;ct#l`DJ}Ir(6KyX^&NG4bj9TxMdLK)5wZ_Usf(*b4BE;Lt z`Fk@_^2-sMMCI$T@8@1W)#1wJS={Ac^J+Zt0a*IC5-%?kxjE5 z`x1BJ-~X)nDTfjJ^%Sz=R=oUfV9|-a&qLKe87@-fPtHLpYkoC3rI=OlDQM{my+yCH zh_utMK>>s#%rRfQU4Vl2{#Tq_%}H9#cGk57&PAg}RVwTY%z(`l7x9mOlxEqU6)>ZV7@9Zr@PKGKStgg) zF=j?}@{b|aIbND+a$;rX;Atz|C4upk8u07lePTX5or#zBn5JB#12GP~Un1lChLZHS zMQ`JYV^KensP;+!U4FwPIr&U_iB~GRgci+SIf*P|E)Gp~=kE3s#FnIk#j4H2+}<^Y z@6SneY#c2awp?|abxf=GUV)lDpgZ=idGxwPO!fWEVUoaNW#=5Px1lTu3r{{OQbw^g ziu_F((nB8DJN!*2)Sa6BS?^!DWeMNv?A$)8>^?L)LYmK4rRdsduK6sywp15HO6Ge7J}k`_Lzj}M@D>oI z#`ZLPuDaY=m^6mw5aQZz$)>(4MsFsIZunj~;Oko()5L!cNuWP##H0n!l2nMWg(FNQ zZ+=f1HTis z10qjf^aeG9PO!--Wr_=Pb2Ff*&-qSQJO2z^8wsE1ZQU9pxbu7d9{|ljGQWR)B`<_D z>y1-`?sAAJLDKX?uRo_XBJ~>D^-uz6uc1%{s)!mmR8X=KP{emScWCQYJ;U{358*+IQmht9Z zs1(69TngA1Dnhg|+fT|On#^)^1(!yD1DeA1X$}@J(xsYTk!ZqzM8c)gPhl*%-xi2mYyt=L|@bc-hz)LO24Yfm|qIM@#0a)r_VGI8J z!Xnkw5EiL)s<24qQrL4i9L>{bM6w4Nn-A%17<2Q3={EvnCh)n#{5u*CyPzvUIohZ! zsl7}mHa6#h%49PG`fVEwYtp?f$|EV;6xj`){m3`~W5%8Q2qR69*S`CqF#0$#fss` ztp|(H#0}^g-SC6~KR^?+mICtYDY`bub}Lb-Gqm!=fAJj%CI?&o@Wt~XZI#ua6-Ej>F=i9EK z6jr7%>zAnOV4b_Sy z!@RtA%Fd1BSVYVRj0maRBQ{i_xjOr-%s>Io=uDShnb#;O(ke61q#RPAO204t-ve_U zqjj%x0?54o6^sA5+b-gN7W)5g>;KRAxG6$Gq$a+2C9U-8_2Jw1`zNoPBtiT%X?{sm z9fJ`Jq_P7H=Z=Tl%fqqaUDQ%6+yqd?PSW_L>qaD^U>|57S+-NbJHrk76x$=?l{K-R z83%ZTzHgjOJ)3N0KQ_D}aHBLAKt7*6d)6hTI{(|KTsNvvhN5G6&z^}V-p06b*%ZK; z+AshPEZ_tSN^|7GyiwY1gvnNZKH#<)TDwRypw#F%ClW1dLZT$|dZDGqab z@eD8}M?o&!GxF{etYlm~Og<+*t(oiNn+Bulcr*ky6Zq$2YjjchdJQn=_#V~QYvAL& zx!ok+F!9dKZS!<9b8k%lyJu&s4gi~_|67e_EdKv)@&03>|L;-!pO8XMQY0|U+av~T ztU3mSxZc`o2azF0!>dlaa?Nb{0W^(=&urWA(dEkzmcdV^V(Xa1)Mk_Yz?H?^WIuNB zM~nUl#rUC6qhR^5KbyAQgS;`|ifRozXkMZ%R?MpL0$bs~Tl9B}|K6d$cd*Tm{UI=$ zYE7|Pl2jrMo-s#lh+42!Gs@eC#x~yR-R?t0_}|>vzBVvuU9(&)PS&9R&s_Lnul5n( z%=bUdSp2tkyHV)>LjT`o{r?G}<8U#`Bxq_GPp$Tb(=H64l2tRhHN&nMy~td^$5(|_ zBKlnk%Ed)R5*!i}NC7OjMvRUB{t&QStpS+*{%GRYZc>@#yTyV-5?gHS_&`76`-lv}(yUJZDhk+++sF8g_(zdF z9!|X;YIw3m#R~h$1_xzW+7mxqcDBk}q#{WZ#{}BuO!PCXzss*BbC}Vv9xW$5f z$jVEK@R1m@ARgmGBf=BLUp*U3*y#TY=aWd5@GOg$x;S*zGPLLyY zEEk|-*()s3038d*<;aT4vHoBj`C@R$2kPW-G|QYw3!g^=V9T9I3!R%`1MUHY-dH%| zMi9;DmLhK4-#{D7#n)KL*)}P@208VGhXB9R_!{@k|99?9NB!Yu&wrVFvGxDXZejo5 zdHbKqO4RACR#Q)`^1mwsdNHXMNt$6%x9BaAi5lEEs#Oti<1^N~>{B{1W^ zr4l|6PpV#z$rMrk+IZG%oR))(InMwU+I|NA$(a*aE;xo!{tUD6pE!5FrXxWra^1JO zPTweB%P?6Q5K+0VN*SXeI_xl_Xe&rO2c<_bjqC~G)lWc{WXLz$+=iJGHGKF|L~{+?pf7o>P!qRt@6_&3 zec~^d<9!HAKDIF_oA_Wq}ag$(NOgZb-|HJcDU;oHF|F^prJ^$No7xsUl|L@2B@8AGn^i>=hyywihGDgEI zhh9@_HsZ$wsmBHe3HcxUKlKouSeK3gV-n^Te5nnTCi$^CEvOj(uDxmhZ~Xn=M!Puw zTa5pEG5)WH;ph)me0VpA2N*_lHyQzgI)k}CIC74+Vj25KuZ(WBWYuB8UxAM<`LL?$ z(jPw2!ox8w3xAV}{+pM*=7p~lOCNnWF(7ATSaokWNh;CB5?xZFu+i?b{kQS;e`|L) zzWy)n{}%fHVXXg$=hoPrTnuPbfRG!270@xjrp(utx z15oi|u1%keNozf*6$F%cVqX5 zJYRI`9-_|I_&-p2vzIoL?WLZw}(x3Uhk_pLHJh=@GT4B1`5@|~cz1_#xae7mnID5kh`$3!P)+b}j1No>! z4w9#UML}?Y0j&4dWb9r;RRV(a_=@J0sQ&jX@?z#@?f*vH{%;rW{}%fHKIs1=IyQ|# zX#gBzzrNxNQzX(y6CVe|=pPtF@|1^l#%qs3&ET+N1w<58tt6V?P7C)l5 zV+LkvfM>kVXIS2|;8)%5TfQ$ja^y?>@!RffcA`&C1dYj!09el=#|CK|o+Y)x%0q#! zWnTuA&}i3CG@yf`gR_j-E*;IWe*k7Q2sXpZv*IqP;669j+mTx7@CR9NF~IM_OHZwm zmq}(jU+IH;W-F|LfrKlN&%v-q?}vL4Z4W47KF(XB%&c|a-APgX51j68I{s%f7XP`u zyH}k5DD?k*JO7cSUUz~hZ{#{BIit<%P=dSk*;f(-&caKd{7TLlj!29Jg?}q$D~K9L zRMV^^D_8!oV<;+>nI9*Uz02-gPbf;=5K0(x?hF9e=~t!Pjbdd&ZsBRahx#AmI&9ki z)7)#s&;J$YzYG0;-~9hGP5(bLD&4V&r9}fYCHS#_rq7{cY%3O)N>!@CN(ooDqD+gw zrXHO?lmKhMSlbx|wsea=c8{(ejB|qKrHfLWjJQ>=t!i&?Qo<)XgUa0$3yHnz}lW1 zNN}GAf5d�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�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[bKx>@S>|b&7n`<+(N1E?w6ho9yeh^CY<( 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�&=X47FU+|bLtN1mCV8k+_T4pZW^ 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 Tl1=0H)O=ZhnlqnlAqwtp9;(o;S0YQA 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

JR6gC=f}!afx8K- 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 YF)n`Y&i|4BSl~Yv_>Tqtf3m>;09|&D$N&HU literal 0 HcmV?d00001 diff --git a/nmatrix-0.1.0.gem b/nmatrix-0.1.0.gem new file mode 100644 index 0000000000000000000000000000000000000000..401052dec732fe0a117a0b097f603b900e65eb25 GIT binary patch literal 1267200 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

6xQ;JLgKy( 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

G4%#=lO;vW_s@I7=8*g=?kD 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 z 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