digiproc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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