digiproc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +37 -0
- data/TODO.md +50 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/environment.rb +118 -0
- data/console_tests.rb +44 -0
- data/digiproc.gemspec +49 -0
- data/examples/analog_signals/analog_to_digital.rb +31 -0
- data/examples/analog_signals/companded-signals.png +0 -0
- data/examples/analog_signals/companding.rb +68 -0
- data/examples/analog_signals/fft-plot.png +0 -0
- data/examples/analog_signals/plot_Digiproc::FFT.png +0 -0
- data/examples/analog_signals/plot_Dsp::FFT.png +0 -0
- data/examples/analog_signals/quantization-outputs.png +0 -0
- data/examples/analog_signals/quantize_compand.rb +69 -0
- data/examples/binomial_distribution/bit_error.rb +14 -0
- data/examples/binomial_distribution/dice.rb +35 -0
- data/examples/digital_signals/_coded_frequency_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/_coded_frequency_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/coded_power_spectral_density,__ts_=_1_s.png +0 -0
- data/examples/digital_signals/coded_power_spectral_density,__ts_=_2_s.png +0 -0
- data/examples/digital_signals/coded_time_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/coded_time_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/frequency_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/frequency_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/modulate_square_pulses.rb +9 -0
- data/examples/digital_signals/modulated_sq._pulses.png +0 -0
- data/examples/digital_signals/modulated_sq._pulses_alt.png +0 -0
- data/examples/digital_signals/power_spectral_density,__ts_=_1_s.png +0 -0
- data/examples/digital_signals/power_spectral_density,__ts_=_2_s.png +0 -0
- data/examples/digital_signals/square_signals.rb +90 -0
- data/examples/digital_signals/time_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/time_signal,_ts_=_2_s.png +0 -0
- data/examples/encoding/gray_code.rb +22 -0
- data/examples/encoding/psk.rb +91 -0
- data/examples/encoding/system_2_phase.png +0 -0
- data/examples/encoding/system_2_xmit_signal.png +0 -0
- data/examples/encoding/system_3_phase.png +0 -0
- data/examples/encoding/system_3_xmit_signal.png +0 -0
- data/examples/encoding/system_4_xmit_signal.png +0 -0
- data/examples/encoding/xor-dpsk-phase-signal-(sys1).png +0 -0
- data/examples/encoding/xor-dpsk-xmit-signal-(sys-1).png +0 -0
- data/examples/factories/Quickplot Graph.png +0 -0
- data/examples/factories/bandpass.rb +6 -0
- data/examples/fft/plot_Dsp::FFT.png +0 -0
- data/examples/fft/recieved_data_(time_domain).png +0 -0
- data/examples/fft/simple_fft_example.rb +47 -0
- data/examples/fft/unprocessed_fft.png +0 -0
- data/examples/filters/bandpass_filter.png +0 -0
- data/examples/filters/filter_a_signal.rb +38 -0
- data/examples/filters/white_noise_db_out_of_bp_filter.png +0 -0
- data/examples/filters/white_noise_mag_out_of_bp_filter.png +0 -0
- data/examples/filters/white_noise_spectra.png +0 -0
- data/examples/functions/compute_probability.rb +29 -0
- data/examples/functions/gram_schmidt.rb +10 -0
- data/examples/functions/minimize_energy.rb +29 -0
- data/examples/functions/orthoganalize.rb +18 -0
- data/examples/functions/simple_functions.rb +81 -0
- data/examples/linear_algebra/diverging_sys.rb +13 -0
- data/examples/linear_algebra/iterative_sys_of_eqns_methods.rb +27 -0
- data/examples/modulation_schemes/dpsk_2.png +0 -0
- data/examples/modulation_schemes/dpsk_256.png +0 -0
- data/examples/modulation_schemes/dpsk_freq_domain.rb +119 -0
- data/examples/modulation_schemes/psk.rb +36 -0
- data/examples/modulation_schemes/psk_2.png +0 -0
- data/examples/modulation_schemes/psk_256.png +0 -0
- data/examples/modulation_schemes/psksystem_1_xmit_signal.png +0 -0
- data/examples/modulation_schemes/psksystem_2_xmit_signal.png +0 -0
- data/examples/modulation_schemes/psksystem_3_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_1_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_2_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_3_xmit_signal.png +0 -0
- data/examples/quickplot/PlottableClass_plot.png +0 -0
- data/examples/quickplot/decorators.rb +13 -0
- data/examples/quickplot/direct_gruff.png +0 -0
- data/examples/quickplot/plot_PlottableClass.png +0 -0
- data/examples/quickplot/quickplot_vs_others.rb +85 -0
- data/examples/quickplot/random_data_quickplot,_dark.png +0 -0
- data/examples/quickplot/random_data_quickplot.png +0 -0
- data/examples/realized_gaussian/norm_dist_plot.png +0 -0
- data/examples/realized_gaussian/norm_dist_spectrum.png +0 -0
- data/examples/realized_gaussian/realized_gaussian_example.rb +23 -0
- data/lib/concerns/convolvable.rb +144 -0
- data/lib/concerns/data_properties.rb +223 -0
- data/lib/concerns/fourier_transformable.rb +178 -0
- data/lib/concerns/initializable.rb +43 -0
- data/lib/concerns/multipliable.rb +22 -0
- data/lib/concerns/os.rb +36 -0
- data/lib/concerns/plottable.rb +248 -0
- data/lib/concerns/requires_data.rb +8 -0
- data/lib/digiproc/version.rb +8 -0
- data/lib/digiproc.rb +2 -0
- data/lib/extensions/array_extension.rb +23 -0
- data/lib/extensions/core_extensions.rb +117 -0
- data/lib/factories/factories.rb +3 -0
- data/lib/factories/filter_factory.rb +83 -0
- data/lib/factories/window_factory.rb +22 -0
- data/lib/fft.rb +255 -0
- data/lib/filters/bandpass_filter.rb +43 -0
- data/lib/filters/bandstop_filter.rb +44 -0
- data/lib/filters/digital_filter.rb +59 -0
- data/lib/filters/highpass_filter.rb +27 -0
- data/lib/filters/lowpass_filter.rb +27 -0
- data/lib/functions.rb +221 -0
- data/lib/probability/binomial_distribution.rb +84 -0
- data/lib/probability/bit_generator.rb +94 -0
- data/lib/probability/gaussian_distribution.rb +29 -0
- data/lib/probability/probability.rb +234 -0
- data/lib/probability/theoretical_gaussian_distribution.rb +59 -0
- data/lib/quick_plot.rb +96 -0
- data/lib/rbplot.rb +219 -0
- data/lib/signals/analog_signal.rb +143 -0
- data/lib/signals/digital_signal.rb +181 -0
- data/lib/strategies/code/differential_encoding_strategy.rb +69 -0
- data/lib/strategies/code/gray_code.rb +75 -0
- data/lib/strategies/code/xor_differential_encoding_strategy.rb +100 -0
- data/lib/strategies/code/xor_differential_encoding_zero_angle_strategy.rb +103 -0
- data/lib/strategies/companding/custom_companding_strategy.rb +29 -0
- data/lib/strategies/convolution/bf_conv.rb +57 -0
- data/lib/strategies/fft/brute_force_dft_strategy.rb +31 -0
- data/lib/strategies/fft/inverse_fft_conjugate_strategy.rb +44 -0
- data/lib/strategies/fft/radix2_strategy.rb +84 -0
- data/lib/strategies/gaussian/gaussian_generator.rb +49 -0
- data/lib/strategies/linear_algebra/gauss_seidel_strategy.rb +90 -0
- data/lib/strategies/linear_algebra/jacobi_strategy.rb +81 -0
- data/lib/strategies/linear_algebra/sor2_strategy.rb +98 -0
- data/lib/strategies/linear_algebra/sor_strategy.rb +108 -0
- data/lib/strategies/modulation/phase_shift_keying_strategy.rb +96 -0
- data/lib/strategies/orthogonalize/gram_schmidt.rb +50 -0
- data/lib/strategies/strategies.rb +3 -0
- data/lib/strategies/window/blackman_window.rb +32 -0
- data/lib/strategies/window/hamming_window.rb +31 -0
- data/lib/strategies/window/hanning_window.rb +31 -0
- data/lib/strategies/window/kaiser_window.rb +27 -0
- data/lib/strategies/window/rectangular_window.rb +22 -0
- data/lib/strategies/window/window.rb +42 -0
- data/lib/systems/custom_system.rb +13 -0
- data/lib/systems/hilbert_transform.rb +6 -0
- data/lib/systems/matched_filter.rb +21 -0
- data/lib/systems/raised_cosine_filter.rb +11 -0
- data/lib/systems/system.rb +19 -0
- data/lib/systems/systems.rb +3 -0
- data/playground.rb +323 -0
- data/plots/_coded_frequency_signal,_ts_=_1_s.png +0 -0
- data/plots/_coded_frequency_signal,_ts_=_2_s.png +0 -0
- data/plots/coded_freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/plots/coded_freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/plots/coded_power_spectral_density,__ts_=_1_s.png +0 -0
- data/plots/coded_power_spectral_density,__ts_=_2_s.png +0 -0
- data/plots/coded_time_signal,_ts_=_1_s.png +0 -0
- data/plots/coded_time_signal,_ts_=_2_s.png +0 -0
- data/plots/dpsk_2.png +0 -0
- data/plots/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/plots/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/plots/frequency_signal,_ts_=_1_s.png +0 -0
- data/plots/frequency_signal,_ts_=_2_s.png +0 -0
- data/plots/power_spectral_density,__ts_=_1_s.png +0 -0
- data/plots/power_spectral_density,__ts_=_2_s.png +0 -0
- data/plots/psk_2.png +0 -0
- data/plots/time_signal,_ts_=_1_s.png +0 -0
- data/plots/time_signal,_ts_=_2_s.png +0 -0
- data/test-title-dark.png +0 -0
- data/test-title.png +0 -0
- 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
|