SAS - PROC SQL - Case When arithmetic not making sense - sql

In SAS Proc SQL, I am using this Case statement to try to mark policies that have differences other than .015 per year:
CASE WHEN (MOD1 - MOD0) NOT = .015 THEN 2
WHEN (MOD2 - MOD1) NOT = .015 THEN 3
WHEN (MOD3 - MOD2) NOT = .015 THEN 4
WHEN (MOD4 - MOD3) NOT = .015 THEN 5
ELSE 0 END
The weird thing is it doesn't work when it feels like it should. ( e.g. mod1 = .955 and mod0 = .94 but the first statement believes its not .015) At first I believed it to be an issue with digits but that never worked.
Oddly enough this does work and I don't know why.
CASE WHEN (MOD1 - MOD0) NOT = (1- .985) THEN 2
WHEN (MOD2 - MOD1) NOT = (1- .985) THEN 3
WHEN (MOD3 - MOD2) NOT = (1- .985) THEN 4
WHEN (MOD4 - MOD3) NOT = (1- .985) THEN 5
ELSE 0 END
But this doesn't work
CASE WHEN (MOD1 - MOD0) NOT = (.2- .185) THEN 2
WHEN (MOD2 - MOD1) NOT = (.2- .185) THEN 3
WHEN (MOD3 - MOD2) NOT = (.2- .185) THEN 4
WHEN (MOD4 - MOD3) NOT = (.2- .185) THEN 5
ELSE 0 END
I have workarounds to get around any of these (including using data steps) so I'm not really looking for solutions to the issue but more answers as to what is going on here. I like to understand why things don't work rather than just knowing what I can and can't do.
Thanks!

The problem is probably floating point arithmetic. Exact comparisons to floats is troublesome. Try something like this:
CASE WHEN ABS((MOD1 - MOD0) - 0.015) > 0.00001 THEN 2
WHEN ABS((MOD2 - MOD1) - 0.015) > 0.00001 THEN 3
WHEN ABS((MOD3 - MOD2) - 0.015) > 0.00001 THEN 4
WHEN ABS((MOD4 - MOD3) - 0.015) > 0.00001 THEN 5
ELSE 0
END
The 0.00001 is arbitrary. It is just some small number.

You can use round function:
CASE WHEN round(MOD1 - MOD0,.0001) NOT = .015 THEN 2
WHEN round(MOD2- MOD1,.0001) NOT = .015 THEN 3
WHEN round(MOD3 - MOD2,.0001) NOT = .015 THEN 4
WHEN round(MOD4 - MOD3,.0001) NOT = .015 THEN 5
ELSE 0 END

Related

I am trying to recreate this R logic within a SQL query. Any ideas on how I should go about doing so? Appreciate any assistance at all

This is the R script that I am attempting to recreate using a CASE WHEN statement in SQL:
dat[ ,X_1_7_Spline := pmax(1,pmin(ifelse(is.na(X),1,X),7))]
It seems that this command is telling the parser to return the parallel maxima of a vector containing a conditional statement as long as the value of variable X lies between 1 and the parallel minima of some value and 7 (as long as the value is not null). It then seems to join the new column containing these values back to the original dataset (dat). I am having some troubles representing the "pmax(1,pmin(ifelse(is.na(X),1,X),7))" portion of the code in my SQL query and would appreciate any ideas on how I might be able to do this effectively.
I have something very remedial right now, which I know does not express this above statement properly:
CASE WHEN MAX(IF(ISNOTNULL(X) AND MIN(X)=1 AND MAX(X)=7) then 1 else X end as X_1_7_Spline
Any thoughts/feedback would be greatly appreciated as I am still trying to understand the R script. Thanks in advance for any insight on this issue.
ifelse(is.na(X),1,X) can be translated into SQL's COALESCE(X, 1); and
pmin and pmax logic can be placed in a CASE WHEN (as you've started)
Perhaps this?
CASE WHEN X < 1 THEN 1
WHEN X > 7 THEN 7
ELSE coalesce(X, 1) END as NewX
We don't need to worry about coalesceing the X < 1 or X > 7 because null < 1 does not resolve as true, so it does not accept that case.
Demo in R using sqldf:
library(data.table)
dat <- data.table(X = c(-1,5,9,NA))
dat[, X_1_7_Spline := pmax(1,pmin(ifelse(is.na(X),1,X),7)) ]
sqldf::sqldf("select *, (CASE WHEN X < 1 THEN 1 WHEN X > 7 THEN 7 ELSE coalesce(X,1) END) as NewX from dat")
# X X_1_7_Spline NewX
# 1 -1 1 1
# 2 5 5 5
# 3 9 7 7
# 4 NA 1 1

Get fraction between x.66 and x.99

I'm trying to make a query - (CASE - WHEN statement):
--all cols are numbers
case when round((O.POCET - O.P_DEL - O.P_DEL_DOD - O.P_FAK - O.P_VYD - O.P_OBJ)/SK.VELKE_BAL,3)
between 0.66 and 0.99 then 1
when round((O.POCET - O.P_DEL - O.P_DEL_DOD - O.P_FAK - O.P_VYD - O.P_OBJ)/SK.DOD_BAL,3)
between 0.66 and 0.99 then 2
else 0 end
It's working fine, but i need to get numbers between 1.66-1.99, 2.66-2.99 and so on...
IF i use LIKE, i can get the fraction, but not between theese numbers.
How can i solve this ?
If you just want it when the decimal part of the (positive) number is between a range then use the MOD function:
CASE
WHEN MOD(your_sum1, 1) BETWEEN 0.66 AND 0.99
THEN 1
WHEN MOD(your_sum2, 1) BETWEEN 0.66 AND 0.99
THEN 2
ELSE 0
END
db<>fiddle here

Alternative to "While > 127 subtract 128"

I'm writing an algorithm that generates 4 values between 0 and 127, based on an input number. It looks like this:
value = {}
input = number
mod = input * 2 - 1
value[1] = input - 1
value[2] = input - 1 + mod
value[3] = input - 1 + mod*2
value[4] = input - 1 + mod*3
To make sure the resulting numbers remain between 0 and 127 for larger numbers, I have this in place:
for i = 1, 4 do
while value[i] > 127 do
value[i] = value[i] - 128
end
end
This works as intended, but as the generated numbers grow larger, this method becomes extremely slow. For instance, if the input number is 400000, value[4] will become 2799996. Reducing that to a number below 127 using my method takes quite a while. Is there a better way to do this?
Any and all suggestions will be much appreciated!
Modulo solved my problem. I had no idea it was a thing; I better retake maths.
The while loop is out the window, new code looks like this:
for i = 1, 4 do
value[i] = value[i] % 128
end
It was that simple, thank you Sami Kuhmonen!

How to get the KY-022 Infrared Receiver module to work on NodeMCU in Lua?

I have a KY-022 IR module that I can't get to work on my NodeMCU. I've been searching for some code samples in Lua on the internet with no luck. Can anyone point me in the right direction? Any code samples would be greatly appreciate it.
At the moment I have the following code:
local pin = 4
gpio.mode(pin, gpio.OPENDRAIN, gpio.PULLUP)
gpio.trig(pin, "down", function (level, micro)
print(gpio.read(pin), level, micro)
end)
When I press a button on the remote, I get something like this:
0 0 571940709
0 0 571954086
0 0 571955257
1 0 571958694
1 0 571963275
1 0 571969917
0 0 571974347
0 0 571980989
1 0 571983203
1 0 571987709
0 0 571993359
1 0 572000078
0 0 572004508
0 0 572047513
0 0 572058674
So, how do I get from that to figuring out which key was pressed on the remote?
After a month or so i've reopened this project and played around with it some more. As piglet suggested, I started listening for both high and low signals. The data is still very inconsistent and can't get a stable reading.
(And by the way, thanks for the vote-down piglet, that was greatly appreciated. I wish you could have seen my search history before you decided that i'm ignorant)
I'm going to post my curent code maybe somebody can point out what I'm doing wrong here.
local pin = 4
local prevstate = false
local prevmicro = 0
local prevtime = 0
local count = 0
gpio.mode(pin, gpio.INT)
gpio.trig(pin, "both", function (level, micro)
--local state = gpio.read(pin)
local state = level
if (micro - prevmicro) > 90000 then
prevmicro = 0
prevstate = false
count = 0
print("\n#", "st", "lv", "microtime", "timing")
end
if prevstate ~= state then
time = math.floor((micro - prevmicro)/100)
prevstate = state
prevmicro = micro
if time > 3 and time < 1000 then
if prevtime > 80 and prevtime < 100 then
if time > 17 and time < 25 then
print('Repeat')
elseif time > 40 and time < 50 then
print('Start')
end
else
print(count, gpio.read(pin), level, micro, time)
count = count + 1
end
prevtime = time
end
end
end)
and here are some sample readouts from pushing the same button:
# st lv microtime timing
1 1 1 1504559531 16
2 1 0 1504566995 74
3 0 1 1504567523 5
4 1 0 1504573619 60
5 0 1 1504587422 138
6 1 0 1504588011 5
7 1 1 1504604250 162
8 1 0 1504605908 16
9 1 1 1504659929 540
10 1 0 1504662154 22
# st lv microtime timing
1 1 1 1505483535 16
2 1 0 1505491003 74
3 0 1 1505491558 5
4 1 0 1505497627 60
5 0 1 1505511409 137
6 1 0 1505512023 6
7 1 1 1505518186 61
8 1 0 1505527733 95
9 1 0 1505586167 22
10 1 1 1505586720 5
# st lv microtime timing
1 1 1 1507990937 16
2 1 0 1507998405 74
3 0 1 1507998934 5
4 1 0 1508005029 60
5 0 1 1508018811 137
6 1 0 1508019424 6
7 1 1 1508035641 162
8 1 0 1508037322 16
9 1 1 1508091345 540
10 1 0 1508093570 22
As it turns out, the Lua code required for this is actually quite simple.
Where the code above is falling over is actually the print statements. These are extremely expensive and basically, kill your sampling resolution until it's useless.
You are in essence, writing an interrupt service routine, you have a limited time budget before you have to read the next edge change and if it happens before you are done processing, tough luck! So you need to make the ISR as efficient as you can.
In the example below, we listen to the "both" edge event, when one occurs, we simply record an indication of which edge and what duration.
Periodically (using a timer) we print out the contents of the waveform.
This perfectly matches the waveform on my logic analyzer, you still have the challenge of decoding the signal. Though, there are lots of great protocol docs that explain how to take accurate waveform data and use it to determine the signal being sent. I found that a lot of cheap "brand x" remotes appear to be using the NEC protocol, so this might be a good place to start depending on your project.
IR transmission because of its nature is not completely error-free so you may get a spurious edge signal from time to time but the code below is pretty stable and runs quite well in isolation, I have yet to test it when the Microcontroller is under more load than just listening for IR.
It may turn out that using Lua for this purpose is not the best due to the fact that it is an interpreted language (each command issued is parsed and then executed at runtime, this is not at all efficient.) But I will see how far I can get before I decide to write a c module.
local irpin = 2
local lastTimestamp = 0
local waveform = {}
local i = 1
gpio.mode(irpin,gpio.INT)
gpio.trig(irpin, "both", function(level, ts)
onEdge(level, ts)
end)
function onEdge(level, ts)
waveform[i] = level
waveform[i+1] = ts - lastTimestamp
lastTimestamp = ts
i = i+2
end
-- Print out the waveform
function showWaveform ()
if table.getn(waveform) > 65 then
for k,v in pairs(waveform) do
print(k,v)
end
i = 1;
waveform = {}
end
end
tmr.alarm(0, 1000, 1, showWaveform)
print("Ready")
The following code works for my 17 key remote which came with my cheap KY-022 module. I just finished it and haven't had time to clean it up nor to optimize it, so bear with me.
local IR = 2
local lts, i, wave = 0, 0, {}
local keys = {}
keys['10100010000000100000100010101000'] = '1'
keys['10001010000000100010000010101000'] = '2'
keys['10101010000000100000000010101000'] = '3'
keys['10000010000000100010100010101000'] = '4'
keys['10000000000000100010101010101000'] = '5'
keys['10101000000000100000001010101000'] = '6'
keys['10101010000000000000000010101010'] = '7'
keys['10100010001000000000100010001010'] = '8'
keys['10100000100000000000101000101010'] = '9'
keys['10100000101000000000101000001010'] = '0'
keys['10001010001000000010000010001010'] = '*'
keys['10100010100000000000100000101010'] = '#'
keys['10000000101000000010101000001010'] = 'U'
keys['10000000100000000010101000101010'] = 'L'
keys['10001000101000100010001000001000'] = 'R'
keys['10001000001000100010001010001000'] = 'D'
keys['10000010101000000010100000001010'] = 'OK'
local function getKey()
local data = ''
local len = table.getn(wave)
if len >= 70 then
local pkey = 0
local started = false
for k, v in pairs(wave) do
v = math.floor(v/100)
if (pkey == 87 or pkey == 88 or pkey == 89) and (v > 40 and v < 50) then
started = true
end
pkey = v
if started then
if v > 300 then
started = false
end
--this is just to fix some random skipped edges
if (v > 20 and v < 25) or v == 11 then
if v > 20 and v < 25 then
d = 17
else
d = 6
end
v1 = v - d
data = data .. '' .. math.floor(v1/10)
v2 = v - (v - d)
data = data .. '' .. math.floor(v2/10)
else
if v < 40 then
data = data .. '' .. math.floor(v/10)
end
end
end
end
control = data:sub(0, 32)
if control == '00000000000000000101010101010101' then
data = data:sub(32, 63)
print(len, data, keys[data] or '?')
end
end
lts, i, wave = 0, 0, {}
end
local function onEdge(level, ts)
local time = ts - lts
wave[i] = time
i = i + 1
if time > 75000 then
tmr.alarm(0, 350, 0, getKey)
end
lts = ts
end
gpio.mode(IR,gpio.INT)
gpio.trig(IR, "both", onEdge)
I'm putting this asside and start working on some other parts of my project for the moment, but if anyone has any suggestions on how I could improve it, make it faster, smaller even, leave a comment.
PS: for those that are going to complain about not working for them, you need to adjust the if statement values for the started variable based on your remote timings. In my case it's always 88 or 89 followed by 44.
You have to get the sequence sent by the remote for each button.
Record the IR emitter's on-off sequence by logging the time stamps for high-low and low-high transitions.
Note the various patterns for each button you want to use or emulate.
Here's a in-depth tutorial http://www.instructables.com/id/How-To-Useemulate-remotes-with-Arduino-and-Raspber/
You can find this and similar resources using www.google.com

How to work with foldRight() in Kotlin?

I try this code
println(listOf(1, 2, 4).foldRight(0) { total, next ->
total - next
})
I thought it works like 0 + 4 - 2 - 1 = 1.
But it returns 3. Why?
Sorry for this silly question.
foldRight works by accumulating the result from right to left. In your case, it is going to do
(1 - (2 - (4 - 0))) = (1 - (2 - 4)) = 1 - (-2) = 3
Note that your operation has its parameters in the wrong order, foldRight will pass you the next element as the first parameter and the accumulator as the second parameter. See https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fold-right.html. If you swap them, you would have
(((0 - 4) - 2) - 1) = - 7
unless I am getting something wrong