How to calculate the average of timestamps - lua

I have a .log file that looks like this :
--hh:mm:ss
10:15:46
10:09:33
10:27:26
10:09:49
09:54:21
10:10:25
And what I need to do is to calculate the average on those timestamps, I wrote a function to calculate the average :
function calculate_average_working_hours(working_day_diff)
local current_hour, current_minutes, current_secondes = 0, 0, 0
local total_hour, total_minutes, total_secondes = 0, 0, 0
local working_days_counter = #working_day_diff
for key, value in pairs(working_day_diff) do
current_hour, current_minutes, current_secondes = extract_timestamp_value(value) --covert the values to numbers
total_hour = total_hour + current_hour
total_minutes = total_minutes + current_minutes
total_secondes = total_secondes + current_secondes
end
total_hour = math.ceil(total_hour / working_days_counter)
total_minutes = math.ceil(total_minutes / working_days_counter)
total_secondes = math.ceil(total_secondes / working_days_counter)
return ("%02d:%02d"):format(total_hour, total_minutes)
end
So basically what I'm doing is to sum the seconds, minutes, and hours and divide the results by the number of logs,
for example if i have 10:15:46 and 10:09:33 i will add the seconds, minutes and hours and divide it by 2
is this the correct way to calculate the average of timestamps values?

I would be something like this solved the problem:
--- example 1
local log = [[--hh:mm:ss
10:15:46
10:09:33
10:27:26
10:09:49
09:54:21
10:10:25
]]
local function seconds(v)
local h,m,s = v:match("(%d%d):(%d%d):(%d%d)")
if h and m and s then return h*3600+m*60+s else return nil end
end
local i,sum = 0, 0
for line in log:gmatch('(.-)[\r\n]') do
local r = seconds(line)
if r then i=i+1; sum = sum+r end
end
print(sum,i, os.date("!%X",sum/i) )

Related

A better way on improving my roman numeral decoder

Quick explanation, I have recently started using codewars to further improve my programming skills and my first challenge was to make a roman numeral decoder, I went through many versions because I wasnt satisfied with what I had, So I am asking if there is an easier way of handling all the patterns that roman numerals have, for example I is 1 but if I is next to another number it takes it away for example V = 5 but IV = 4.
here is my CODE:
function Roman_Numerals_Decoder (roman)
local Dict = {I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000}
local number = 0
local i = 1
while i < #roman + 1 do
local letter = roman:sub(i,i) -- Gets the current character in the string roman
if roman:sub(i,i) == "I" and roman:sub(i + 1,i + 1) ~= "I" and roman:sub(i + 1,i + 1) ~= "" then -- Checks for the I pattern when I exists and next isnt I
number = number + (Dict[roman:sub(i +1,i + 1)] - Dict[roman:sub(i,i)]) -- Taking one away from the next number
i = i + 2 -- Increase the counter
else
number = number + Dict[letter] -- Adds the numbers together if no pattern is found, currently checking only I
i = i + 1
end
end
return number
end
print(Roman_Numerals_Decoder("MXLIX")) -- 1049 = MXLIX , 2008 = MMVIII
at the moment I am trying to get 1049 (MXLIX) to work but I am getting 1069, obviously I am not following a rule and I feel like its more wrong then it should be because usually if its not correct its 1 or 2 numbers wrong.
The algorithm is slightly different: you need to consider subtraction when the previous character has less weight than the next one.
function Roman_Numerals_Decoder (roman)
local Dict = {I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000}
local num = 0
local i = 1
for i=1, #roman-1 do
local letter = roman:sub(i,i) -- Gets the current character in the string roman
local letter_p = roman:sub(i+1,i+1)
if (Dict[letter] < Dict[letter_p]) then
num = num - Dict[letter] -- Taking one away from the next number
print("-",Dict[letter],num)
else
num = num + Dict[letter] -- Adds the numbers together if no pattern is found, currently checking only I
print("+",Dict[letter],num)
end
end
num = num + Dict[roman:sub(-1)];
print("+",Dict[roman:sub(-1)], num)
return num
end
print(Roman_Numerals_Decoder("MXLIX")) -- 1049 = MXLIX , 2008 = MMVIII

Lua - Analysing values in a text file

I’m writing power (watts) values to a text file in order to extract information I can use.
4.7
4.7
4.8
5.2
5.1
4.6
4.6
4.6
Currently I have the following code to give me the average, but I’d like to add to it so it tells me more such as, what was the highest value, the lowest value, the most frequent value, and if at all possible if there are any ‘0’ values recorded (* - if possible with that last one it would be good to ignore them..)
local ctr = 0
local sum = 0
for _ in io.lines"/www/EnergyPlug-b.txt" do
ctr = ctr + 1
end
print(ctr)
for line in io.lines"/www/EnergyPlug-b.txt" do
sum = sum + line
end
print(sum)
average = sum / ctr
print(average)
I did explore creating a Lua table via table.insert() off of the first io.lines section, like the following, but I’m not sure how good it is?
local rows = {}
-- go over the file line by line
for line in io.lines("/www/EnergyPlug-b.txt") do
-- if line matches ?.? then insert into table
local value = line:match("%d%p%d") -- 1.5
--print(value)
table.insert(rows, value)
end
local arraymax = math.max(unpack(rows))
local arraymin = math.min(unpack(rows))
print (arraymax) -- ?.?
print (arraymin) -- ?.?
If the above is suitable, how best should I go about identifying the items/values i mentioned at the very start ?
In the first snippet there is no reason to have a separate loop for ctr and sum. You can do it in one loop.
Your second snipped is ok. unpack is limited so this won't work for many thousand values.
You have to traverse the table anyway to get the other values so you can determin min and max in that loop too without that size limit.
local value = line:match("%d%p%d") if there are only those numbers in that file you can skip the pattern matching here.
The calculations are pretty straigt forward. I'm not sure what you're struggling with here.
local min = math.huge -- use a value that is definitely bigger than any value
local max = -math.huge -- use a value that is definitely smaller than any value
local counts = {} -- an emtpy table we'll use to count how often each value occurs
local numIngored = 0 -- how many 0 values have we ignored?
for line in io.lines(yourFileName) do
-- convert string to a number
local value = tonumber(line)
-- if we take the value into account
if value ~= 0.0 then
-- update min and max values
min = value < min and value or min
max = value > max and value or max
-- update counts
counts[value] = counts[value] and counts[value] + 1 or 1
else
-- count ignored values
numIgnored = numIgnored + 1
end
end
I'll leave it up to you to get the most frequent values from counts
Thanks to #piglet and using other SO posts, here is what I ended up with..
local min = math.huge -- use a value that is definitely bigger than any value
local max = -math.huge -- use a value that is definitely smaller than any value
local ctr = 0
local valtotal = 0
local counts = {} -- an emtpy table we'll use to count how often each value occurs
local numIngored = 0 -- how many 0 values have we ignored?
for line in io.lines("/www/EnergyPlug-b.txt") do
-- convert string to a number
local value = tonumber(line)
-- if we take the value into account
if value ~= 0.0 then
ctr = ctr + 1
valtotal = valtotal + value
-- update min and max values
min = value < min and value or min
max = value > max and value or max
-- update counts
counts[value] = counts[value] and counts[value] + 1 or 1
else
-- count ignored values
numIgnored = numIgnored + 1
end
end
print("----Table print out -----")
for k, v in pairs(counts) do
print(k,v)
end
print("---------------------------")
print("Lowest value recorded = " ..min)
print("Highest value recorded = " ..max)
print("Average value recorded = " ..round2(valtotal / ctr, 1))
print("Number of values recorded = " ..ctr)
print("---------------------------")
local max_val, key1 = -math.huge
for k, v in pairs(counts) do
if v > max_val then
max_val, key1 = v, k
end
end
local min_val, key2 = math.huge
for k1, v1 in pairs(counts) do
if v1 < min_val or v1 == min_val then
min_val, key2 = v1, k1
end
end
local min_qty, max_qty = 0, 0
local min_str, max_str = "", ""
for k2, v2 in pairs(counts) do
if v2 == min_val then
min_qty = min_qty + 1
min_str = min_str .. " " .. k2
elseif v2 == max_val then
max_qty = max_qty + 1
max_str = max_str .. " " .. k2
end
end
if min_qty == 1 then
print("Least recorded value was = " ..key2.. " recorded "..min_val.." times")
else
print("Least recorded values were = " ..min_str.. " each recorded "..min_val.." time")
end
if max_qty == 1 then
print("Most recorded value was = " ..key1.. " recorded "..max_val.." times")
else
print("Least recorded values were = " ..max_str.. " each recorded "..max_val.." time")
end
print("---------------------------")

bmp aspect ratio issue

I've been trying to understand how bmp files work so I can render some Mandelbrot set pictures and output them as bmp files since that seems to be one of the easiest methods but for some reason when I use an aspect ratio that isn't 1:1 even though its something to the power of 4 (so no padding is needed) I get weird artifacts like these 200:100 48:100 what I'm trying to do is turning an array of pixels that has white for even numbers and black for odd numbers into a bmp, this (100:100) is what it looks like with 1:1 aspect ratio.
I've tried reading through the wikipedia article to see if I can figure out what I'm doing wrong but I still don't get what I'm missing.
This is the script I've written in Lua so far:
ResolutionX = 100
ResolutionY = 100
local cos, atan, sin, atan2, sqrt, floor = math.cos, math.atan, math.sin, math.atan2, math.sqrt, math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep
io.output("Test.bmp")
function Basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return rep("0",32-#t)..concat(t,"")
end
FileSize = Basen(ResolutionY*ResolutionX*3 + 54,2)
FileSize4 = tonumber(sub(FileSize,1,8),2) or 0
FileSize3 = tonumber(sub(FileSize,9,16),2) or 0
FileSize2 = tonumber(sub(FileSize,17,24),2) or 0
FileSize1 = tonumber(sub(FileSize,25,32),2) or 0
Width = Basen(ResolutionX,2)
print("Width: ",Width)
Width4 = tonumber(sub(Width,1,8),2) or 0
Width3 = tonumber(sub(Width,9,16),2) or 0
Width2 = tonumber(sub(Width,17,24),2) or 0
Width1 = tonumber(sub(Width,25,32),2) or 0
Height = Basen(ResolutionY,2)
print("Height: ",Height)
Height4 = tonumber(sub(Height,1,8),2) or 0
Height3 = tonumber(sub(Height,9,16),2) or 0
Height2 = tonumber(sub(Height,17,24),2) or 0
Height1 = tonumber(sub(Height,25,32),2) or 0
BMPSize = Basen(ResolutionY*ResolutionX*3,2)
BMPSize4 = tonumber(sub(BMPSize,1,8),2) or 0
BMPSize3 = tonumber(sub(BMPSize,9,16),2) or 0
BMPSize2 = tonumber(sub(BMPSize,17,24),2) or 0
BMPSize1 = tonumber(sub(BMPSize,25,32),2) or 0
print("TotalSize: ",FileSize1,FileSize2,FileSize3,FileSize4,"\nWidth: ",Width1,Width2,Width3,Width4,"\nHeight: ",Height1,Height2,Height3,Height4,"\nImage data: ",BMPSize1,BMPSize2,BMPSize3,BMPSize4)
Result = {"BM"..char( --File type
FileSize1,FileSize2,FileSize3,FileSize4,--File size
0,0,0,0, --Reserved
54,0,0,0, --Where the pixel data starts
40,0,0,0, --DIB header
Width1,Width2,Width3,Width4, --Width
Height1,Height2,Height3,Height4, --Height
1,0, --Color planes
24,00, --Bit depth
0,0,0,0, --Compression
BMPSize1,BMPSize2,BMPSize3,BMPSize4, --The amount of bytes pixel data will consume
Width1,Width2,Width3,Width4,
Height1,Height2,Height3,Height4,
0,0,0,0, --Number of colors in palatte
0,0,0,0
)}
for X = 0, ResolutionX - 1 do
for Y = 0, ResolutionY - 1 do
insert(Result,rep(char(255 * ((X + 1) % 2) * ((Y + 1) % 2)),3))
end
end
io.write(table.concat(Result))
Ok, here's a BMP version. I've put things in a module so it might be easier to use.
local writeBMP = {}
local floor = math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep
local function Basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return rep("0",32-#t)..concat(t,"")
end
local function nextMul4(x)
if ( x % 4 == 0 ) then
return x
else
return x+4-(x%4)
end
end
local function clamp(x)
local y = x
if ( x > 255 ) then
y = 255
elseif ( x < 0 ) then
y = 0
end
return floor(y)
end
-- Accepts array of type pixelsXYC[X][Y][C] of numbers 0-255
-- C=1,2,3 are the red, green and blue channels respectively
-- X increases left to right, and Y increases top to bottom
function writeBMP.data(pixelsXYC, resolutionX, resolutionY)
local Pixels = pixelsXYC
local ResolutionX = resolutionX
local ResolutionY = resolutionY
assert(#Pixels == ResolutionX, "Table size and X resolution mismatch")
assert(#Pixels[1] == ResolutionY, "Table size and Y resolution mismatch")
local FileSize = Basen(ResolutionY*nextMul4(3*ResolutionX) + 54,2)
local FileSize4 = tonumber(sub(FileSize,1,8),2) or 0
local FileSize3 = tonumber(sub(FileSize,9,16),2) or 0
local FileSize2 = tonumber(sub(FileSize,17,24),2) or 0
local FileSize1 = tonumber(sub(FileSize,25,32),2) or 0
local Width = Basen(ResolutionX,2)
local Width4 = tonumber(sub(Width,1,8),2) or 0
local Width3 = tonumber(sub(Width,9,16),2) or 0
local Width2 = tonumber(sub(Width,17,24),2) or 0
local Width1 = tonumber(sub(Width,25,32),2) or 0
local Height = Basen(ResolutionY,2)
local Height4 = tonumber(sub(Height,1,8),2) or 0
local Height3 = tonumber(sub(Height,9,16),2) or 0
local Height2 = tonumber(sub(Height,17,24),2) or 0
local Height1 = tonumber(sub(Height,25,32),2) or 0
local BMPSize = Basen(ResolutionY*nextMul4(3*ResolutionX),2)
local BMPSize4 = tonumber(sub(BMPSize,1,8),2) or 0
local BMPSize3 = tonumber(sub(BMPSize,9,16),2) or 0
local BMPSize2 = tonumber(sub(BMPSize,17,24),2) or 0
local BMPSize1 = tonumber(sub(BMPSize,25,32),2) or 0
local Result = {}
Result[1] = "BM" .. char( --File type
FileSize1,FileSize2,FileSize3,FileSize4,--File size
0,0, --Reserved
0,0, --Reserved
54,0,0,0, --Where the pixel data starts
40,0,0,0, --DIB header
Width1,Width2,Width3,Width4, --Width
Height1,Height2,Height3,Height4, --Height
1,0, --Color planes
24,0, --Bit depth
0,0,0,0, --Compression
BMPSize1,BMPSize2,BMPSize3,BMPSize4, --The amount of bytes pixel data will consume
37,22,0,0, --Pixels per meter horizontal
37,22,0,0, --Pixels per meter vertical
0,0,0,0, --Number of colors in palatte
0,0,0,0
)
local Y = ResolutionY
while( Y >= 1 ) do
for X = 1, ResolutionX do
local r = clamp( Pixels[X][Y][1] )
local g = clamp( Pixels[X][Y][2] )
local b = clamp( Pixels[X][Y][3] )
Result[#Result+1] = char(b)
Result[#Result+1] = char(g)
Result[#Result+1] = char(r)
end
-- byte alignment
if ( ( (3*ResolutionX) % 4 ) ~= 0 ) then
local Padding = 4 - ((3*ResolutionX) % 4)
Result[#Result+1] = rep(char(0),Padding)
end
Y = Y - 1
end
return table.concat(Result)
end
function writeBMP.write(pixelsXYC, resolutionX, resolutionY, filename)
local file = io.open(filename,"wb")
local data = writeBMP.data(pixelsXYC, resolutionX, resolutionY)
file:write(data)
end
return writeBMP
A simple test:
-- writeBMP example
local writeBMP = require "writeBMP"
local resolutionX = 100
local resolutionY = 100
-- Pixel data
local pixels = {}
for x=1,resolutionX do
pixels[x] = {}
for y=1, resolutionY do
pixels[x][y] = {}
local red = 255*(resolutionX-x+resolutionY-y)/(resolutionX+resolutionY)
local green = 255*y/resolutionY
local blue = 255*x/resolutionX
pixels[x][y][1] = red
pixels[x][y][2] = green
pixels[x][y][3] = blue
end
end
writeBMP.write(pixels,resolutionX,resolutionY,"testwritebmp.bmp")
return
Note: In BMP, the Y axis starts on the bottom. I am more used to Y axis going from top down in computer graphics (so I wrote it that way).
Thanks HAX for the code.
Welcome to Stack Exchange :)
I suggest having a look at PPM files, they are easy. They can be converted with other tools to png or bmp with other tools.
Wikipedia PPM Specification
Here is a PPM solution:
ResolutionX = 48
ResolutionY = 100
local cos, atan, sin, atan2, sqrt, floor = math.cos, math.atan, math.sin, math.atan2, math.sqrt, math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep
local file = io.open("Test.ppm","w")
-- PPM File headers
local Output = { }
Output[1] = "P3"
Output[2] = tostring(ResolutionX) .. " " .. tostring(ResolutionY)
Output[3] = "255"
-- Image body
for Y = 0, ResolutionY - 1 do
for X = 0, ResolutionX - 1 do
local value = 255 * ((X + 1) % 2) * ((Y + 1) % 2)
Output[#Output+1] = rep(tostring(floor(value)),3," ")
end
end
-- Join lines together
local Result = table.concat(Output,"\n")
file:write(Result)
Notes: I could not get your code to write to file (see my usage of file). The inner loop must be X (columns) and the outer loop must be Y (rows) if the write order is English reading order (left-right down-up).

I am trying to do arithmetic on table values and keep getting an error. here is my code

I am trying to perform arithmetic on table values and keep getting an error. Here is my total code. I am basically trying to generate simplex noise. I have created a multidimensional array (table) and am trying to perform operations on the values but i keep getting an error that says I cannot perform arithmetic on a table value. I don't know If I have to convert it to something or what. Please Help.
totalNoiseMap = {}
function simplex_noise(width, height)
simplexnoise = {}
for i = 1,512 do
simplexnoise[i] = {}
for j = 1, 512 do
simplexnoise[i][j] = 0
end
end
frequency = 5.0 / width
for x = 1, width do
for y = 1, height do
simplexnoise[x][y] = noise(x * frequency,y * frequency)
simplexnoise[x][y] = (simplexnoise[x][y] + 1) / 2
end
end
return simplexnoise
end
function noise(x, y, frequency)
return simplex_noise(x / frequency, y / frequency)
end
function generateOctavedSimplexNoise(width,height,octaves,roughness,scale)
totalnoise = {}
for i = 1,512 do
totalnoise[i] = {}
for j = 1, 512 do
totalnoise[i][j] = 0
end
end
layerFrequency = scale
layerWeight = 1
weightSum = 0
for octave = 1, octaves do
for x = 1, width do
for y = 1, height do
totalnoise[x][y] = (totalnoise[x][y] + noise(x * layerFrequency,y * layerFrequency, 2) * layerWeight)
end
end
--Increase variables with each incrementing octave
layerFrequency = layerFrequency * 2
weightSum = weightSum + layerWeight
layerWeight = layerWeight * roughness
end
return totalnoise
end
totalNoiseMap = generateOctavedSimplexNoise(512, 512, 3, 0.4, 0.005)
totalnoise[x][y] + noise(x * layerFrequency,y * layerFrequency, 2) * layerWeight
Here you get table noise(x * layerFrequency,y * layerFrequency, 2), mult it by scalar layerWeight and than add it to scalar totalnoise[x][y].
I can think of how to multiply table by scalar - it should be something like
for i = 1,512 do
for j = 1,512 do
a[i][j] = t[i][j] * scalar
end
end
But I'm unable to get what you're trying to do by adding. Suppose it should be addition of two tables
for i = 1,512 do
for j = 1,512 do
a[i][j] = b[i][j] + c[i][j]
end
end
But it works only with same-sized tables

Table value doesn't change

I have a 2 dim array and all it's cells filled with zeros.
What i'm trying to do is to take some randomly chosen cells and fill it with 4 or 5
but what i get is either empty gird with all value equal to zero or i get just one value that has changed to 4 or 5 and that's my code below:
local grid = {}
for i=1,10 do
grid[i] = {}
for j=1,10 do
grid[i][j] = 0
end
end
local empty={}
for i=1,10 do
for j=1,10 do
if grid[i][j]==0 then
table.insert(empty,i ..'-'.. j)
end
end
end
local fp=math.floor(table.maxn(empty)/3)
local fx,fy
for i=1,fp do
math.randomseed(os.time())
math.random(0,1)
local fo=math.random(0,1)
math.random(table.maxn(empty))
local temp= empty[math.random(table.maxn(empty))]
local dashindex=string.find(temp,'-')
fx=tonumber(string.sub(temp,1,dashindex-1))
fy=tonumber(string.sub(temp,dashindex+1,string.len(temp)))
if fo==0 then
grid[fx][fy]=4
elseif fo==1 then
grid[fx][fy]=5
end
end
for i=1,10 do
for j=1,10 do
print(grid[i][j])
end
print('\n')
end
I'm not sure what the for i=1,fp loop is doing with temp and fo, for example the seed should only be set once, and also, the return value on line after local fo is ignored, seems very messy. But based on your post, if you really just want to randomly select N cells from your 2D array and set those to either 4 or 5 (randomly), this should work:
-- maybe N = fp
local N = 5
math.randomseed(os.time())
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] == 0 then
grid[fx][fy] = math.random(4,5)
i = i + 1
end
until i > N
Note however that the closer N is to number of items in array (100 in your example), the longer it will take for the loop to complete. If this is a concern, then for large N values, you could do the opposite: initialize each cell to 4 or 5 randomly, and then randomly set size - N of them to 0.
math.randomseed(os.time())
local rows = 10
local columns = 10
local grid = {}
if N > rows*columns/2 then
for i=1,rows do
grid[i] = {}
for j=1,columns do
grid[i][j] = math.random(4,5)
end
end
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] ~= 0 then
grid[fx][fy] = 0
i = i + 1
end
until i > N
else
for i=1,rows do
grid[i] = {}
for j=1,columns do
grid[i][j] = 0
end
end
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] == 0 then
grid[fx][fy] = math.random(4,5)
i = i + 1
end
until i > N
end

Resources