digiproc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +48 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +78 -0
  10. data/Rakefile +37 -0
  11. data/TODO.md +50 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/config/environment.rb +118 -0
  15. data/console_tests.rb +44 -0
  16. data/digiproc.gemspec +49 -0
  17. data/examples/analog_signals/analog_to_digital.rb +31 -0
  18. data/examples/analog_signals/companded-signals.png +0 -0
  19. data/examples/analog_signals/companding.rb +68 -0
  20. data/examples/analog_signals/fft-plot.png +0 -0
  21. data/examples/analog_signals/plot_Digiproc::FFT.png +0 -0
  22. data/examples/analog_signals/plot_Dsp::FFT.png +0 -0
  23. data/examples/analog_signals/quantization-outputs.png +0 -0
  24. data/examples/analog_signals/quantize_compand.rb +69 -0
  25. data/examples/binomial_distribution/bit_error.rb +14 -0
  26. data/examples/binomial_distribution/dice.rb +35 -0
  27. data/examples/digital_signals/_coded_frequency_signal,_ts_=_1_s.png +0 -0
  28. data/examples/digital_signals/_coded_frequency_signal,_ts_=_2_s.png +0 -0
  29. data/examples/digital_signals/coded_power_spectral_density,__ts_=_1_s.png +0 -0
  30. data/examples/digital_signals/coded_power_spectral_density,__ts_=_2_s.png +0 -0
  31. data/examples/digital_signals/coded_time_signal,_ts_=_1_s.png +0 -0
  32. data/examples/digital_signals/coded_time_signal,_ts_=_2_s.png +0 -0
  33. data/examples/digital_signals/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  34. data/examples/digital_signals/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  35. data/examples/digital_signals/frequency_signal,_ts_=_1_s.png +0 -0
  36. data/examples/digital_signals/frequency_signal,_ts_=_2_s.png +0 -0
  37. data/examples/digital_signals/modulate_square_pulses.rb +9 -0
  38. data/examples/digital_signals/modulated_sq._pulses.png +0 -0
  39. data/examples/digital_signals/modulated_sq._pulses_alt.png +0 -0
  40. data/examples/digital_signals/power_spectral_density,__ts_=_1_s.png +0 -0
  41. data/examples/digital_signals/power_spectral_density,__ts_=_2_s.png +0 -0
  42. data/examples/digital_signals/square_signals.rb +90 -0
  43. data/examples/digital_signals/time_signal,_ts_=_1_s.png +0 -0
  44. data/examples/digital_signals/time_signal,_ts_=_2_s.png +0 -0
  45. data/examples/encoding/gray_code.rb +22 -0
  46. data/examples/encoding/psk.rb +91 -0
  47. data/examples/encoding/system_2_phase.png +0 -0
  48. data/examples/encoding/system_2_xmit_signal.png +0 -0
  49. data/examples/encoding/system_3_phase.png +0 -0
  50. data/examples/encoding/system_3_xmit_signal.png +0 -0
  51. data/examples/encoding/system_4_xmit_signal.png +0 -0
  52. data/examples/encoding/xor-dpsk-phase-signal-(sys1).png +0 -0
  53. data/examples/encoding/xor-dpsk-xmit-signal-(sys-1).png +0 -0
  54. data/examples/factories/Quickplot Graph.png +0 -0
  55. data/examples/factories/bandpass.rb +6 -0
  56. data/examples/fft/plot_Dsp::FFT.png +0 -0
  57. data/examples/fft/recieved_data_(time_domain).png +0 -0
  58. data/examples/fft/simple_fft_example.rb +47 -0
  59. data/examples/fft/unprocessed_fft.png +0 -0
  60. data/examples/filters/bandpass_filter.png +0 -0
  61. data/examples/filters/filter_a_signal.rb +38 -0
  62. data/examples/filters/white_noise_db_out_of_bp_filter.png +0 -0
  63. data/examples/filters/white_noise_mag_out_of_bp_filter.png +0 -0
  64. data/examples/filters/white_noise_spectra.png +0 -0
  65. data/examples/functions/compute_probability.rb +29 -0
  66. data/examples/functions/gram_schmidt.rb +10 -0
  67. data/examples/functions/minimize_energy.rb +29 -0
  68. data/examples/functions/orthoganalize.rb +18 -0
  69. data/examples/functions/simple_functions.rb +81 -0
  70. data/examples/linear_algebra/diverging_sys.rb +13 -0
  71. data/examples/linear_algebra/iterative_sys_of_eqns_methods.rb +27 -0
  72. data/examples/modulation_schemes/dpsk_2.png +0 -0
  73. data/examples/modulation_schemes/dpsk_256.png +0 -0
  74. data/examples/modulation_schemes/dpsk_freq_domain.rb +119 -0
  75. data/examples/modulation_schemes/psk.rb +36 -0
  76. data/examples/modulation_schemes/psk_2.png +0 -0
  77. data/examples/modulation_schemes/psk_256.png +0 -0
  78. data/examples/modulation_schemes/psksystem_1_xmit_signal.png +0 -0
  79. data/examples/modulation_schemes/psksystem_2_xmit_signal.png +0 -0
  80. data/examples/modulation_schemes/psksystem_3_xmit_signal.png +0 -0
  81. data/examples/modulation_schemes/system_1_xmit_signal.png +0 -0
  82. data/examples/modulation_schemes/system_2_xmit_signal.png +0 -0
  83. data/examples/modulation_schemes/system_3_xmit_signal.png +0 -0
  84. data/examples/quickplot/PlottableClass_plot.png +0 -0
  85. data/examples/quickplot/decorators.rb +13 -0
  86. data/examples/quickplot/direct_gruff.png +0 -0
  87. data/examples/quickplot/plot_PlottableClass.png +0 -0
  88. data/examples/quickplot/quickplot_vs_others.rb +85 -0
  89. data/examples/quickplot/random_data_quickplot,_dark.png +0 -0
  90. data/examples/quickplot/random_data_quickplot.png +0 -0
  91. data/examples/realized_gaussian/norm_dist_plot.png +0 -0
  92. data/examples/realized_gaussian/norm_dist_spectrum.png +0 -0
  93. data/examples/realized_gaussian/realized_gaussian_example.rb +23 -0
  94. data/lib/concerns/convolvable.rb +144 -0
  95. data/lib/concerns/data_properties.rb +223 -0
  96. data/lib/concerns/fourier_transformable.rb +178 -0
  97. data/lib/concerns/initializable.rb +43 -0
  98. data/lib/concerns/multipliable.rb +22 -0
  99. data/lib/concerns/os.rb +36 -0
  100. data/lib/concerns/plottable.rb +248 -0
  101. data/lib/concerns/requires_data.rb +8 -0
  102. data/lib/digiproc/version.rb +8 -0
  103. data/lib/digiproc.rb +2 -0
  104. data/lib/extensions/array_extension.rb +23 -0
  105. data/lib/extensions/core_extensions.rb +117 -0
  106. data/lib/factories/factories.rb +3 -0
  107. data/lib/factories/filter_factory.rb +83 -0
  108. data/lib/factories/window_factory.rb +22 -0
  109. data/lib/fft.rb +255 -0
  110. data/lib/filters/bandpass_filter.rb +43 -0
  111. data/lib/filters/bandstop_filter.rb +44 -0
  112. data/lib/filters/digital_filter.rb +59 -0
  113. data/lib/filters/highpass_filter.rb +27 -0
  114. data/lib/filters/lowpass_filter.rb +27 -0
  115. data/lib/functions.rb +221 -0
  116. data/lib/probability/binomial_distribution.rb +84 -0
  117. data/lib/probability/bit_generator.rb +94 -0
  118. data/lib/probability/gaussian_distribution.rb +29 -0
  119. data/lib/probability/probability.rb +234 -0
  120. data/lib/probability/theoretical_gaussian_distribution.rb +59 -0
  121. data/lib/quick_plot.rb +96 -0
  122. data/lib/rbplot.rb +219 -0
  123. data/lib/signals/analog_signal.rb +143 -0
  124. data/lib/signals/digital_signal.rb +181 -0
  125. data/lib/strategies/code/differential_encoding_strategy.rb +69 -0
  126. data/lib/strategies/code/gray_code.rb +75 -0
  127. data/lib/strategies/code/xor_differential_encoding_strategy.rb +100 -0
  128. data/lib/strategies/code/xor_differential_encoding_zero_angle_strategy.rb +103 -0
  129. data/lib/strategies/companding/custom_companding_strategy.rb +29 -0
  130. data/lib/strategies/convolution/bf_conv.rb +57 -0
  131. data/lib/strategies/fft/brute_force_dft_strategy.rb +31 -0
  132. data/lib/strategies/fft/inverse_fft_conjugate_strategy.rb +44 -0
  133. data/lib/strategies/fft/radix2_strategy.rb +84 -0
  134. data/lib/strategies/gaussian/gaussian_generator.rb +49 -0
  135. data/lib/strategies/linear_algebra/gauss_seidel_strategy.rb +90 -0
  136. data/lib/strategies/linear_algebra/jacobi_strategy.rb +81 -0
  137. data/lib/strategies/linear_algebra/sor2_strategy.rb +98 -0
  138. data/lib/strategies/linear_algebra/sor_strategy.rb +108 -0
  139. data/lib/strategies/modulation/phase_shift_keying_strategy.rb +96 -0
  140. data/lib/strategies/orthogonalize/gram_schmidt.rb +50 -0
  141. data/lib/strategies/strategies.rb +3 -0
  142. data/lib/strategies/window/blackman_window.rb +32 -0
  143. data/lib/strategies/window/hamming_window.rb +31 -0
  144. data/lib/strategies/window/hanning_window.rb +31 -0
  145. data/lib/strategies/window/kaiser_window.rb +27 -0
  146. data/lib/strategies/window/rectangular_window.rb +22 -0
  147. data/lib/strategies/window/window.rb +42 -0
  148. data/lib/systems/custom_system.rb +13 -0
  149. data/lib/systems/hilbert_transform.rb +6 -0
  150. data/lib/systems/matched_filter.rb +21 -0
  151. data/lib/systems/raised_cosine_filter.rb +11 -0
  152. data/lib/systems/system.rb +19 -0
  153. data/lib/systems/systems.rb +3 -0
  154. data/playground.rb +323 -0
  155. data/plots/_coded_frequency_signal,_ts_=_1_s.png +0 -0
  156. data/plots/_coded_frequency_signal,_ts_=_2_s.png +0 -0
  157. data/plots/coded_freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  158. data/plots/coded_freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  159. data/plots/coded_power_spectral_density,__ts_=_1_s.png +0 -0
  160. data/plots/coded_power_spectral_density,__ts_=_2_s.png +0 -0
  161. data/plots/coded_time_signal,_ts_=_1_s.png +0 -0
  162. data/plots/coded_time_signal,_ts_=_2_s.png +0 -0
  163. data/plots/dpsk_2.png +0 -0
  164. data/plots/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  165. data/plots/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  166. data/plots/frequency_signal,_ts_=_1_s.png +0 -0
  167. data/plots/frequency_signal,_ts_=_2_s.png +0 -0
  168. data/plots/power_spectral_density,__ts_=_1_s.png +0 -0
  169. data/plots/power_spectral_density,__ts_=_2_s.png +0 -0
  170. data/plots/psk_2.png +0 -0
  171. data/plots/time_signal,_ts_=_1_s.png +0 -0
  172. data/plots/time_signal,_ts_=_2_s.png +0 -0
  173. data/test-title-dark.png +0 -0
  174. data/test-title.png +0 -0
  175. metadata +322 -0
@@ -0,0 +1,84 @@
1
+ ##
2
+ # Allows you to solve problems whose probabilities are dictated by a binomial distribution
3
+ # Does not simulate a problem, but calculates probabilities based off of the binomial distribution equation
4
+ # See examples/binomial_distribution/dice.rb
5
+
6
+ class Digiproc::Probability::TheoreticalBinomialDistribution
7
+
8
+ attr_accessor :n, :k, :p
9
+
10
+ ##
11
+ # == Initialize with n, k, and p
12
+ # n:: [Integer] Number of trials
13
+ # k:: [Integer] (optional at initialization) Number of successes
14
+ # p:: [Numeric] Probability of success
15
+ def initialize(n: ,k: nil , p: )
16
+ @n = n.to_f
17
+ @k = k.to_f
18
+ @p = p.to_f
19
+ end
20
+
21
+ ##
22
+ # Returns the Expected Value of a binomial distributed random variable (n * p)
23
+ ## bd = Digiproc::Probability::TheoreticalBinomialDistribution.new(n:10, p: 0.5)
24
+ ## bd.expect # => 5
25
+ # ie in above example, expect 5 successes (heads) in 10 flips of a coin
26
+ def expect
27
+ self.n * self.p
28
+ end
29
+
30
+ ##
31
+ # Return the Variance of the binomial distributed random variable (n * p * (1 - p))
32
+ # Using above example:
33
+ ## bd.var # => 2.5
34
+ def var
35
+ self.n * self.p * ( 1 - self.p )
36
+ end
37
+
38
+ ##
39
+ # Will run `probability` method using `self.k`
40
+ ## bd.k = 5
41
+ ## bd.prob # => 0.24609375
42
+ def prob
43
+ self.probability(self.k)
44
+ end
45
+
46
+ ##
47
+ # Will run `coefficient` method using `self.k`
48
+ ## bd.coeff # => 252.0
49
+ def coeff
50
+ coefficient(self.k)
51
+ end
52
+
53
+ ##
54
+ # Calculates the probability of `k` number of `wins`
55
+ # `k` can be an array or a range of numbers, a variable number of args, or a single integer (see example referenced above)
56
+ # If k is more than one value, the probabilities will be summed together
57
+ # For example:
58
+ ## bd.probability(5) # => 0.24609375
59
+ ## bd.probability(0..5) # => 0.623046875
60
+ ## bd.probability(1,2,5,6) # => 0.5048828125
61
+ ## bd.probability([0,1,2,3,4,5]) # => 0.623046875
62
+ def probability(*k)
63
+ sum = 0
64
+ karr = k.map{ |val| val.respond_to?(:to_a) ? val.to_a : val}
65
+ karr.flatten!
66
+ karr.each do |k_val|
67
+ sum += self.coefficient(k_val) * ((self.p) ** (k_val)) * (1 - self.p) ** (n - k_val)
68
+ end
69
+ sum
70
+ end
71
+
72
+ ##
73
+ # Returns the binomial equation coefficient for a given number of wins, ie returns n! / (k! * (n - k)!)
74
+ # Can take an argument of `k`, which defaults to `self.k`
75
+ ## bd.coefficient(4) # => 210.0
76
+ def coefficient(k = self.k)
77
+ n_fact = Digiproc::Functions.fact(self.n)
78
+ k_fact = Digiproc::Functions.fact(k)
79
+ n_fact / (k_fact.to_f * (Digiproc::Functions.fact(self.n - k)))
80
+ end
81
+
82
+
83
+ end
84
+
@@ -0,0 +1,94 @@
1
+ ##
2
+ # Create random bit for testing purposes
3
+ #
4
+ class Digiproc::Probability::RandomBitGenerator
5
+
6
+ ##
7
+ # Returns a string representing a bitstream
8
+ # == Arguments
9
+ # size:: [Integer] number of desired bits
10
+ # p_0:: [Float] (between 0 and 1) probability of a bit 0 occurring (default value: 0.5)
11
+ # p_1:: [Float] (between 0 and 1) probability of a bit 1 occurring (default value: 0.5)
12
+ # random_generator:: Any random generator that follows the prototype of ruby's `Random` class. Defaults to `Random.new`
13
+ #
14
+ ## bitgen = Digiproc::Probability::RandomBitGenerator
15
+ ## bitgen.bitstream(size: 10) # => "1000010010"
16
+ def self.bitstream(size: , p_0: 0.5, p_1: 0.5, random_generator: Random.new)
17
+ stream = ''
18
+ size.times do
19
+ stream += get_bit(p_0: p_0, p_1: p_1, random_generator: random_generator).to_s
20
+ end
21
+ stream
22
+ end
23
+
24
+ ##
25
+ # Returns an Integer
26
+ # == Arguments
27
+ # p_0:: [Float] (between 0 and 1) probability of a bit 0 occurring (default value: 0.5)
28
+ # p_1:: [Float] (between 0 and 1) probability of a bit 1 occurring (default value: 0.5)
29
+ # random_generator:: Any random generator that follows the prototype of ruby's `Random` class. Defaults to `Random.new`
30
+ #
31
+ ## bitgen.bitstream # => 1
32
+ def self.get_bit(p_0: 0.5, p_1: 0.5, random_generator: Random.new)
33
+ raise ArgumentError.new("Probabilities must add up to 1") if p_0 + p_1 != 1
34
+ rnum = 1 - random_generator.rand
35
+ return 0 if rnum <= p_0
36
+ return 1
37
+ end
38
+
39
+ ##
40
+ # Constructor for bitstream
41
+ # == Arguments
42
+ # p_0:: [Float] between 0 and 1: probability of a 0 bit (defaults to 0.5)
43
+ # p_1:: [Float] between 0 and 1: probability of a 1 bit (defaults to 0.5)
44
+ ## bitstream = bitgen.new_bitstream
45
+ def self.new_bitstream(p_0: 0.5 , p_1: 0.5)
46
+ self.new(bits_per_symbol: 1, p_0: p_0, p_1: p_1)
47
+ end
48
+
49
+ ##
50
+ # Constructor for symbol_stream
51
+ # == Arguments
52
+ # bits_per_symbol:: [Integer] set the number of bits for each symbol
53
+ # p_0:: [Float] between 0 and 1: probability of a 0 bit (defaults to 0.5)
54
+ # p_1:: [Float] between 0 and 1: probability of a 1 bit (defaults to 0.5)
55
+ ## bitstream = bitgen.new_bitstream
56
+ def self.new_symbol_stream(bits_per_symbol: , p_0: 0.5, p_1: 0.5)
57
+ self.new(bits_per_symbol: bits_per_symbol, p_0: p_0, p_1: p_1)
58
+ end
59
+
60
+ attr_accessor :bits_per_symbol, :p_0, :p_1, :random_generator
61
+
62
+ ##
63
+ # == Arguments
64
+ # # bits_per_symbol:: [Integer] set the number of bits for each symbol
65
+ # p_0:: [Float] between 0 and 1: probability of a 0 bit (defaults to 0.5)
66
+ # p_1:: [Float] between 0 and 1: probability of a 1 bit (defaults to 0.5)
67
+ #
68
+ ## bitstream = bitgen.new(bits_per_symbol: 1) => same as bitgen.new_bitstream
69
+ def initialize(bits_per_symbol: , p_0: 0.5, p_1: 0.5 )
70
+ raise ArgumentError.new("Probabilities of bits must add up to 1") if p_0 + p_1 != 1
71
+ @bits_per_symbol, @p_0, @p_1, @random_generator = bits_per_symbol, p_0, p_1, Random.new
72
+ end
73
+
74
+ ##
75
+ # == Arguments
76
+ # size:: [Integer] number of symbols to be generated (or bits if self.bits_per_symbol is 1)
77
+ ## bistream = Digiproc::Probability::RandomBitGenerator.new_bitstream
78
+ ## symstream = Digiproc::Probability::RandomBitGenerator.new_symbol_stream(bits_per_symbol: 8)
79
+ ## bitstream.generate(10) # => "0001110010"
80
+ ## symstream.generate(4) # => ["10100111", "00111100", "00111010", "10000100"]
81
+ def generate(size)
82
+ if self.bits_per_symbol == 1
83
+ return self.class.bitstream(size: size, p_0: @p_0, p_1: @p_1, random_generator: @random_generator)
84
+ else
85
+ signal = []
86
+ size.times do
87
+ signal << self.class.bitstream(size: @bits_per_symbol, p_0: @p_0, p_1: @p_1, random_generator: @random_generator)
88
+ end
89
+ return signal
90
+ end
91
+ end
92
+
93
+
94
+ end
@@ -0,0 +1,29 @@
1
+ ##
2
+ # Class to create a sample of Gaussian Distributed values
3
+ class Digiproc::Probability::GaussianDistribution
4
+
5
+ attr_accessor :mean, :stddev, :generator, :data
6
+ attr_reader :size
7
+
8
+ include Digiproc::Convolvable::InstanceMethods, Digiproc::Initializable, Digiproc::FourierTransformable
9
+
10
+ ##
11
+ # == Initialize arguments
12
+ # mean:: [Float] mean of the population
13
+ # stddev:: [Float] standard deviation of the population
14
+ # size:: [Integer] number of datapoints
15
+ # generator:: Strategy for making Gaussian values. Defaults to Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy.new
16
+ def initialize(mean: , stddev: , size: ,generator: Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy.new)
17
+ @mean, @stddev, @generator, @size = mean, stddev, generator, size
18
+ generator.mean = mean
19
+ generator.stddev = stddev
20
+ data = []
21
+ size.times do
22
+ data << generator.rand
23
+ end
24
+ @data = data
25
+ initialize_modules(Digiproc::FourierTransformable => {time_data: data})
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,234 @@
1
+ ##
2
+ # Module supplying probability functions
3
+ module Digiproc::Probability
4
+
5
+ ##
6
+ # Interchangable strategy
7
+ #Strategy requirements:
8
+ #can be initialized with no arguments (gives mean = 0 and stddev = 1), or with a mean and stddev
9
+ #instance method: #rand returns normal distribution with mean = 0, stddev = 1
10
+ @gaussian_generator = Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy.new
11
+
12
+
13
+ ##
14
+ # Returns a new instance of a normal number random generator, which contains an instance method #rand which will return a
15
+ # single random number from the normal distribution
16
+ # == Arguments
17
+ # mean:: [Float] mean of the distribution defaults to 0
18
+ # stddev:: [Float] standard deviation of the distribution defaults to 1
19
+ ## gen = Digiproc::Probability.normal_random_generator(100, 15) # => return rand gen of IQ scores
20
+ ## scores = []
21
+ ## 10.times do
22
+ ## scores << gen.rand
23
+ ## end
24
+ ## puts scores # => [98.22222989456891, 102.78474153867536, 130.32345913801984, 90.68720464516447, 94.45065665514275, 100.46571157090098, 105.40955807686252, 85.9616084359681, 103.05219341559669, 96.62475435904693]
25
+ def self.normal_random_generator(mean = 0, stddev = 1)
26
+ @gaussian_generator.class.new(mean, stddev)
27
+ end
28
+
29
+ ##
30
+ # == Arguments
31
+ # size:: [Integer] number of returns expected, defaults to 1
32
+ # If `size` is 1, returns a single number from the distribution
33
+ # For `size` > 1, return an array of numbers from the distribution
34
+ ## Digiproc::Probability.nrand(5) # => [-0.4870987490684469, 0.48974360810927925, -0.3722088483576355, -0.2829898781247938, 0.12540113064787742]
35
+ ## Digiproc::Probability.nrand # => 0.7978655621417761
36
+ def self.nrand(size = 1)
37
+ return @gaussian_generator.rand if size == 1
38
+ rand_nums = []
39
+ size.times do
40
+ rand_nums << @gaussian_generator.rand
41
+ end
42
+ return rand_nums
43
+ end
44
+
45
+ ##
46
+ # Returns [Float] the mean of the inputted data
47
+ # == Arguments
48
+ # data:: [Array] data to be evaluated
49
+ ## dat = [4,12,19,4,8,9]
50
+ ## Digiproc::Probability.mean(dat) # => 9.333333333333334
51
+ def self.mean(data)
52
+ data.sum / data.length.to_f
53
+ end
54
+
55
+ ##
56
+ # Returns [Float] the variance of the inputted data
57
+ # == Arguments
58
+ # data:: [Array] data to be evaluated
59
+ # variance = sum_over_datavals( (dataval - mean) ^ 2 ) / (number_of_datavals - 1)
60
+ ## dat = [4,12,19,4,8,9]
61
+ ## Digiproc::Probability.variance(dat) # => 31.866666666666664
62
+ def self.variance(data)
63
+ mu = mean(data)
64
+ summation = data.map{ |val| (val - mu) ** 2 }.sum
65
+ summation.to_f / (data.length - 1)
66
+ end
67
+
68
+ ##
69
+ # Returns [Float] the variance of inputted data for a stationary process
70
+ # (stationary proces = mean, variance, autocorrelation do not change with time)
71
+ # Will run faster over large datasets because each val in data is not undergoing a subtraction with mu
72
+ # If the process is not stationary, an incorrect result with be given
73
+ # == Arguments
74
+ # data:: [Array] data to be evaluated
75
+ # stat_var = sum_over_datavals( val^2 ) / number_ov_vals
76
+ ## dat = [4,12,19,4,8,9]
77
+ ## Digiproc::Probability.stationary_variance(dat) # => 26.555555555555543
78
+ def self.stationary_variance(data)
79
+ mu = mean(data)
80
+ summation = data.map{ |val| val ** 2 }.sum
81
+ (summation.to_f / data.length) - (mu ** 2)
82
+ end
83
+
84
+ ##
85
+ # Returns [Float] the standard deviation of the inputted data
86
+ # == Arguments
87
+ # data:: [Array] data to be evaluated
88
+ # stddev = sqrt(variance)
89
+ ## dat = [4,12,19,4,8,9]
90
+ ## Digiproc::Probability.stddev # => 5.645056834671079
91
+ def self.stddev(data)
92
+ variance(data) ** (0.5)
93
+ end
94
+
95
+
96
+ ##
97
+ # Returns [Float] the covariance (if process is stationary) of two datasets
98
+ # == Arguments
99
+ # data1:: [Array] first dataset
100
+ # data2:: [Array] second datatset
101
+ ## dat = [4,12,19,4,8,9]
102
+ ## dat2 = [19, 20, 35, 15, 13, 17]
103
+ ## Digiproc::Probability.stationary_covariance(dat, dat2) # => 31.22222222222223
104
+
105
+ def self.stationary_covariance(data1, data2)
106
+ raise ArgumentError.new("Datasets must be of equal length") if data1.length != data2.length
107
+ xcs = data1.dot data2
108
+ mu1 = mean(data1)
109
+ mu2 = mean(data2)
110
+ (xcs / (data1.length.to_f)) - (mu1 * mu2)
111
+ end
112
+
113
+ ##
114
+ # Returns [Float] covariance of two datasets
115
+ # == Arguments
116
+ # data1:: [Array] first dataset
117
+ # data2:: [Array] second datatset
118
+ ## dat = [4,12,19,4,8,9]
119
+ ## dat2 = [19, 20, 35, 15, 13, 17]
120
+ ## Digiproc::Probability.ovariance(dat, dat2) # => 37.46666666666667
121
+ def self.covariance(data1, data2)
122
+ raise ArgumentError.new("Datasets must be of equal length") if data1.length != data2.length
123
+ mu1 = mean(data1)
124
+ mu2 = mean(data2)
125
+ summation = 0
126
+ for i in 0...data1.length do
127
+ summation += ((data1[i] - mu1) * (data2[i] - mu2))
128
+ end
129
+ summation.to_f / (data1.length - 1)
130
+ end
131
+
132
+ ##
133
+ # Calculates Pearson's correlation coefficient of two datasets
134
+ # == Arguments
135
+ # data1:: [Array] first dataset
136
+ # data2:: [Array] second dataset
137
+ ## dat = [4,12,19,4,8,9]
138
+ ## dat2 = [19, 20, 35, 15, 13, 17]
139
+ ## Digiproc::Probability.correlation_coefficient(dat, dat2) # => 0.84450000297225
140
+ ## cdat = [1,2,3,4,5]
141
+ ## ddat = cdat.map{ |v| v * -29 }
142
+ ## Digiproc::Probability.correlation_coefficient(cdat, ddat) # => -1.0
143
+ def self.correlation_coefficient(data1, data2)
144
+ covar = covariance(data1, data2)
145
+ var1 = variance(data1)
146
+ var2 = variance(data2)
147
+ return covar.to_f / ((var1 ** 0.5) * (var2 ** 0.5))
148
+ end
149
+
150
+ ##
151
+ # Alias for #variance
152
+ def self.var(d)
153
+ variance(d)
154
+ end
155
+
156
+ ##
157
+ # Alias for #covariance
158
+ def self.cov(d1, d2)
159
+ covariance(d1,d2)
160
+ end
161
+
162
+ ##
163
+ # Alias for #correlation_coefficient
164
+ def self.corr_coeff(d1, d2)
165
+ correlation_coefficient(d1, d2)
166
+ end
167
+
168
+ ##
169
+ # Returns the error function output [Float] of the input ie:
170
+ # For an input x and a normal distribution with mean 0 and variance 0.5, it is the probability a random variable will be between -x and x
171
+ # Mirrors Math.erf(x)
172
+ # == Arguments
173
+ # x:: [Float]
174
+ ## Digiproc::Prbobability.erf(0.3) # => 0.32862675945912734
175
+ def self.erf(x)
176
+ Math.erf(x)
177
+ end
178
+
179
+ ##
180
+ # Returns the erfc [Float] per Math.erfc
181
+ # For an input x and a normal distribution with mean 0 and variance 0.5, it is the probability a random variable will not be between -x and x
182
+ # == Arguments
183
+ # x:: [Float]
184
+ ## Digiproc::Prbobability.erfc(0.3) # => 0.6713732405408727
185
+ def self.erfc(x)
186
+ Math.erfc(x)
187
+ end
188
+
189
+ ##
190
+ # Returns [Float] probability that a random variable from a normal distribution of a certain mean and standard deviation will be less than a value x
191
+ # == Arguments
192
+ # x:: [Float] the value which the probability will be evaluated that a random variable will be below
193
+ # mean:: [Float] the mean of the normal distribution (defaults to 0)
194
+ # stddev:: [Float] the standard deviation of the normal distribution (defaults to 1)
195
+ ## Digiproc::Probability.normal_cdf(0.5) # => 0.691462461274013
196
+ ## Digiproc::Probability.normal_cdf(140, 100, 15) # => 0.9961696194324102
197
+ def self.normal_cdf(x, mean = 0, stddev = 1)
198
+ 1 - normal_q(x, mean, stddev)
199
+ end
200
+
201
+ ##
202
+ # Returns [Float] the probability that a random variable from a normal distribution of a certain mean and standard deviation will be greater than a value x
203
+ # == Arguments
204
+ # x:: [Float] the value which the probability will be evaluated that a random variable will be below
205
+ # mean:: [Float] the mean of the normal distribution (defaults to 0)
206
+ # stddev:: [Float] the standard deviation of the normal distribution (defaults to 1)
207
+ ## Digiproc::Probability.normal_q(0.5) # => 0.30853753872598694
208
+ ## Digiproc::Probability.normal_q(140, 100, 15) # => 0.0038303805675897395
209
+ def self.normal_q(x, mean = 0, stddev = 1)
210
+ xformed_x = (x - mean) / stddev.to_f
211
+ 0.5 * erfc(xformed_x / (2 ** 0.5))
212
+ end
213
+
214
+ ##
215
+ # Returns [Hash] the discrete probability distribution function of an inputted array of values
216
+ # == Arguments
217
+ # data:: [Array] discrete values in a dataset
218
+ ## dat = [3,3,4,5,4,5,6,5,6,5,7,6,5,4]
219
+ ## Digiproc::Probability.pdf(dat) # => {3=>2, 4=>3, 5=>5, 6=>3, 7=>1}
220
+ def self.pdf(data)
221
+ pdf = {}
222
+ data.each do |datapoint|
223
+ pt = datapoint.round(2)
224
+ if pdf[pt].nil?
225
+ pdf[pt] = 1
226
+ else
227
+ pdf[pt] += 1
228
+ end
229
+ end
230
+ pdf
231
+ end
232
+
233
+
234
+ end
@@ -0,0 +1,59 @@
1
+ ##
2
+ # Class to calculate probabilities based off of a gaussian distributed random variable.
3
+ class Digiproc::Probability::TheoreticalGaussianDistribution
4
+
5
+ attr_reader :mean, :stddev, :variance
6
+
7
+ def initialize(mean: , stddev: , generator_strategy: Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy)
8
+ @mean, @stddev = mean, stddev
9
+ @variance = stddev ** 2
10
+ @generator_strategy = generator_strategy.new(mean, stddev)
11
+ end
12
+
13
+ ##
14
+ # Return the cdf [Float] of an input x [Float]
15
+ ## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
16
+ ## gd.cdf(0.3) # => 0.6179114221889526
17
+ def cdf(x)
18
+ 1 - q(x)
19
+ end
20
+
21
+ ##
22
+ # Return the q value [Float] of an input x [Float]
23
+ ## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
24
+ ## gd.q(0.3) # => 0.3820885778110474
25
+ def q(x)
26
+ xform_x = xform_x_to_standard(x)
27
+ 0.5 * Math.erfc(xform_x / (2 ** 0.5))
28
+ end
29
+
30
+ ##
31
+ # Return the probability [Float] of the random variable being between an upper [Float] and lower [Float] value
32
+ ## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
33
+ ## gd.p_between(-1, 1) # => 0.6826894921370859
34
+ def p_between(lower, upper)
35
+ cdf(upper) - cdf(lower)
36
+ end
37
+
38
+ ##
39
+ # Return the probability [Float] of the random variable being outside an upper [Float] and lower [Float] value
40
+ ## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
41
+ ## gd.p_outside(-1, 1) # => 0.31731050786291415
42
+ def p_outside(lower, upper)
43
+ cdf(lower) + q(upper)
44
+ end
45
+
46
+ ##
47
+ # Outputs a single random number from the gaussian distribution
48
+ ## gd.rand # => 1.4173209026395848
49
+ def rand
50
+ @generator_strategy.rand
51
+ end
52
+
53
+
54
+ private
55
+
56
+ def xform_x_to_standard(x)
57
+ (x - self.mean) / self.stddev.to_f
58
+ end
59
+ end
data/lib/quick_plot.rb ADDED
@@ -0,0 +1,96 @@
1
+ ##
2
+ # Class for quickly plotting data
3
+ class Digiproc::QuickPlot
4
+
5
+ extend Digiproc::Plottable::ClassMethods
6
+
7
+
8
+
9
+ ##
10
+ # Quickly plot data. Either data OR x AND y are required. If just data is inputted, x values will be the index of the value in the data array
11
+ # == Input Args
12
+ # x:: Array[Numeric] (required if data is not included)
13
+ # y:: Array[Numeric] (required if data is not included)
14
+ # data:: Array[Numeric] (required if x and y are not included)
15
+ # title (Optional):: String
16
+ # x_label (Optional):: String
17
+ # y_label (Optional):: String
18
+ # labels (Optional):: Array[String] x value labels
19
+ # data_name (Optional):: name of data for legend
20
+ # label_map (Optional):: Lambda - mapping data index to an x label for that index
21
+ # dark (Optional):: Boolean - whether or not to use dark mode (defaults to false)
22
+ # path (Optional):: Path to save plot picture to. Defauilts to "./plots"
23
+ def self.plot(x: nil,y: nil, data: nil, title: nil, x_label: nil, y_label: nil, labels: nil, data_name: "data", label_map: nil, dark: false, path: "./plots/")
24
+ xyname, dataname = nil, nil
25
+ if not x.nil?
26
+ if evenly_spaced?(x)
27
+ label_map = Digiproc::Functions.map_to_eqn(0, x.length, x.min, x.max)
28
+ data = y
29
+ y, x = nil, nil
30
+ end
31
+ end
32
+ data.nil? ? (xyname = data_name) : (dataname = data_name)
33
+ qplot(x: x, y: y, data: data, data_name: dataname, xyname: xyname, label_map: label_map, filename: to_filename(title), path: path) do |g|
34
+ g.title = title if not title.nil?
35
+ g.x_axis_label = x_label if not x_label.nil?
36
+ g.y_axis_label = y_label if not y_label.nil?
37
+ g.labels = labels if not labels.nil?
38
+ g.theme = Digiproc::Plottable::Styles::MIDNIGHT if dark
39
+ end
40
+ end
41
+
42
+ def self.step()
43
+ end
44
+
45
+ ##
46
+ # Plots a pi plot
47
+ def self.pi_plot(label ="0.5")
48
+ z = Digiproc::Functions.zeros(26)
49
+ o = Digiproc::Functions.ones(50)
50
+ y = z + o + z
51
+ x = Digiproc::Functions.linspace(-2, 2, 102)
52
+ plot(x: x, y: y, title: "PI fn" ,labels: {-1 => "-#{label}", 0 => "0", 1 => label})
53
+ end
54
+
55
+
56
+ ##
57
+ # Plots a lambda plot
58
+ def self.lambda_plot(label = "1")
59
+ eqn = ->(t){ t.abs <= 1 ? (1 - t.abs) : 0}
60
+ x = Digiproc::Functions.linspace(-2, 2, 102)
61
+ plot(data: x.map{|t| eqn.call(t)},title: "Lambda fn" ,labels: {26 => "-#{label}", 51 => 0 ,76 => label})
62
+ end
63
+
64
+ ##
65
+ # Plots an input equation (lambda)
66
+ # == Input args
67
+ # eqn:: lambda, equation to pe plotted
68
+ # sample_times:: Array[Numeric] input values to lambda equation
69
+ # title (Optional):: String
70
+ # x_label (Optional):: String
71
+ # y_label (Optional):: String
72
+ def self.plot_eq(eqn: , sample_times: ,title: nil, x_label: nil, y_label: nil)
73
+ data = process(eqn, sample_times)
74
+ plot(data: data, title: title, x_label: x_label, y_label: y_label)
75
+ end
76
+
77
+ def process(eqn, locations)
78
+ locations.map{ |n| eqn.call(n) }
79
+ end
80
+
81
+ private
82
+
83
+ def self.evenly_spaced?(data)
84
+ interval = data[1] - data[0]
85
+ for i in 2...data.length do
86
+ new_int = data[i] - data[i-1]
87
+ return false if new_int.round(10) != interval.round(10)
88
+ end
89
+ true
90
+ end
91
+
92
+ def self.to_filename(title)
93
+ title.nil? ? "Quickplot Graph" : title.downcase.gsub(" ", "_")
94
+ end
95
+
96
+ end