英文:
Calculating average of seconds in list of maps Elixir
问题
以下是翻译好的代码部分:
我有这个样本数据
[
%{check_in_time: 60, imaging_time: 180, lab_time: 120, vital_time: 60, pharmacy_time: 100},
%{check_in_time: 20, imaging_time: 380, lab_time: 10, vital_time: 60, pharmacy_time: 200}
]
其中所有这些实体都以秒为单位表示时间。我想要实现的目标是:
计算这些时间的平均值,每个条目表示一个患者的详细信息以及患者在医院中花费的时间,但现在我需要计算这些时间的总平均值,以显示考虑所有患者详细信息的患者的平均等待时间。
我所做的。
items
|> Enum.reduce(
%{
check_in_time: 0,
imaging_time: 0,
lab_time: 0,
pharmacy_time: 0,
vital_time: 0
},
fn %{
check_in_time: check_in_time,
imaging_time: imaging_time,
lab_time: lab_time,
pharmacy_time: pharmacy_time,
vital_time: vital_time
},
acc ->
%{
check_in_time: acc.check_in_time + check_in_time,
imaging_time: acc.imaging_time + imaging_time,
lab_time: acc.lab_time + lab_time,
pharmacy_time: acc.pharmacy_time + pharmacy_time,
vital_time: acc.vital_time + vital_time
}
end
)
|> sum_average()
end
defp sum_average(%{
check_in_time: check_in_time,
imaging_time: imaging_time,
lab_time: lab_time,
pharmacy_time: pharmacy_time,
vital_time: vital_time
}) do
(check_in_time + imaging_time + lab_time + pharmacy_time + vital_time) / 5
end
我希望这能帮助你计算秒钟时间的平均值。如果你有任何其他问题,请随时提出。
英文:
I have this sample data
[
%{ check_in_time: 60,
imaging_time: 180,
lab_time: 120,
vital_time: 60,
pharmacy_time: 100
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
vital_time: 60,
pharmacy_time: 200
}
]
where all these entities are presenting time in seconds, What I want to achieve here is:
calculate the average of these times such as each entry presents, a Patients details, and how much time a patient spent in the hospital, but now I need to calculate the grand average of these times, to show the average waiting time for a patient considering all patient's details.
what I did.
items
|> Enum.reduce(
%{
check_in_time: 0,
imaging_time: 0,
lab_time: 0,
pharmacy_time: 0,
vital_time: 0
},
fn %{
check_in_time: check_in_time,
imaging_time: imaging_time,
lab_time: lab_time,
pharmacy_time: pharmacy_time,
vital_time: vital_time
},
acc ->
%{
check_in_time: acc.check_in_time + check_in_time,
imaging_time: acc.imaging_time + imaging_time,
lab_time: acc.lab_time + lab_time,
pharmacy_time: acc.pharmacy_time + pharmacy_time,
vital_time: acc.vital_time + vital_time
}
end
)
|> sum_average()
end
defp sum_average(%{
check_in_time: check_in_time,
imaging_time: imaging_time,
lab_time: lab_time,
pharmacy_time: pharmacy_time,
vital_time: vital_time
}) do
(check_in_time + imaging_time + lab_time + pharmacy_time + vital_time) / 5
end
I did some R&D and learned this is not the right way to do it. Please guide me on the right way of calculating the average time value in seconds, with the above data. PS: values for those keys can be nil as well.
Any help would be wonderful thank you.
Update: the expected formula and result would be.
(60 + 20) / 2 (number of values) = 40
(180 + 380) / 2 = 280
(120 + 10) / 2 = 65
(60 + 60) / 2 = 60
(100 + 200) / 2 = 150
(40 + 280 + 65 + 60 + 150) / 5 (number of values) = 119
答案1
得分: 1
我认为你可以使用 Map.values/1
和 Enum.sum/1
很简单地完成这个任务,例如:
input = [
%{ check_in_time: 60,
imaging_time: 180,
lab_time: 120,
vital_time: 60,
pharmacy_time: 100
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
vital_time: 60,
pharmacy_time: 200
}
]
times = input |> Enum.map(fn x -> Map.values(x) end) |> List.flatten() |> Enum.reject(&is_nil/1)
cnt = length(times)
sum = Enum.sum(times)
average = sum / cnt
# 119.0
我认为没有必要维护一个复杂的累加器映射。如果你自己枚举列表一次,性能会稍微好一些,例如:
{sum, cnt} = input |> Enum.reduce({0, 0}, fn x, {sum, cnt} ->
vals = x |> Map.values() |> Enum.reject(&is_nil/1)
sum = sum + Enum.sum(vals)
cnt = cnt + length(vals)
{sum, cnt}
end)
average = sum / cnt
还有其他一些优化可能,但这两个选项都相当快:运行第一个选项 1000 万次只需 1033 毫秒,而第二个选项只需 773 毫秒。因此,我建议专注于具有最大清晰度和易维护性的解决方案,这意味着尽可能使用内置函数而不是自定义函数。
英文:
I think you can do this fairly simply using Map.values/1
and Enum.sum/1
, e.g.
input = [
%{ check_in_time: 60,
imaging_time: 180,
lab_time: 120,
vital_time: 60,
pharmacy_time: 100
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
vital_time: 60,
pharmacy_time: 200
}
]
times = input |> Enum.map(fn x -> Map.values(x) end) |> List.flatten() |> Enum.reject(&is_nil/1)
cnt = length(times)
sum = Enum.sum(times)
average = sum / cnt
# 119.0
I don't think there's any need to maintain a complicated map of accumulators. Performance is a bit better if you enumerate the list yourself once, e.g.
{sum, cnt} = input |> Enum.reduce({0, 0}, fn x, {sum, cnt} ->
vals = x |> Map.values() |> Enum.reject(&is_nil/1)
sum = sum + Enum.sum(vals)
cnt = cnt + length(vals)
{sum, cnt}
end)
average = sum / cnt
Other refinements are possible, but both options are reasonably fast: running the first option 10 million (!) times took 1033ms, while the second option took 773m. So I would focus on a solution with the greatest clarity and ease of maintenance, and I think that means using built-in functions over home-rolled ones whenever possible.
答案2
得分: 0
以下是代码部分的翻译:
defmodule My do
def items do
[
%{ check_in_time: 60,
imaging_time: 180,
lab_time: 120,
vital_time: 60,
pharmacy_time: 100
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
vital_time: 60,
pharmacy_time: 200
}
]
end
//公共接口:
def get_average(patients) do
get_average(patients, [0,0,0,0,0,0])
end
//私有辅助函数:
defp get_average([], [cit, it, lt, pt, vt, count]) do
patients_avg = (cit + it + lt + pt + vt)/count
_category_avg = patients_avg/5
end
defp get_average([%{
check_in_time: check_in_time,
imaging_time: imaging_time,
lab_time: lab_time,
vital_time: vital_time,
pharmacy_time: pharmacy_time} | patients],
[cit, it, lt, vt, pt, count] ) do
get_average(patients,
[
cit + if is_nil(check_in_time) do 0 else check_in_time end,
it + if is_nil(imaging_time) do 0 else imaging_time end,
lt + if is_nil(lab_time) do 0 else lab_time end,
vt + if is_nil(vital_time) do 0 else vital_time end,
pt + if is_nil(pharmacy_time) do 0 else pharmacy_time end,
count + 1
]
)
end
end
在iex中的输出:
iex(1)> items = My.items()
[
%{
check_in_time: 60,
imaging_time: 180,
lab_time: 120,
pharmacy_time: 100,
vital_time: 60
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
pharmacy_time: 200,
vital_time: 60
}
]
iex(2)> My.get_average(items)
119.0
if is_nil()
语句可以改为更短但更难理解的方式:
cit + (check_in_time || 0),
it + (imaging_time || 0),
lt + (lab_time || 0),
vt + (vital_time || 0),
pt + (pharmacy_time || 0),
但你可能需要考虑使用 is_number()
,因为如果 check_in_time
是 "a"
,那么 ||
将返回 "a"
而不是 0
- is_nil()
也会如此。
英文:
The following code just does reduce() by hand so that on the final iteration of the list, i.e. when the list is empty, the code can calculate the average:
defmodule My do
def items do
[
%{ check_in_time: 60,
imaging_time: 180,
lab_time: 120,
vital_time: 60,
pharmacy_time: 100
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
vital_time: 60,
pharmacy_time: 200
}
]
end
//Public interface:
def get_average(patients) do
get_average(patients, [0,0,0,0,0,0])
end
//Private helper function:
defp get_average([], [cit, it, lt, pt, vt, count]) do
patients_avg = (cit + it + lt + pt + vt)/count
_category_avg = patients_avg/5
end
defp get_average([%{
check_in_time: check_in_time,
imaging_time: imaging_time,
lab_time: lab_time,
vital_time: vital_time,
pharmacy_time: pharmacy_time} | patients],
[cit, it, lt, vt, pt, count] ) do
get_average(patients,
[
cit + if is_nil(check_in_time) do 0 else check_in_time end,
it + if is_nil(imaging_time) do 0 else imaging_time end,
lt + if is_nil(lab_time) do 0 else lab_time end,
vt + if is_nil(vital_time) do 0 else vital_time end,
pt + if is_nil(pharmacy_time) do 0 else pharmacy_time end,
count + 1
]
)
end
end
In iex:
/elixir_programs% iex a.ex
Erlang/OTP 24 [erts-12.3.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1]
Interactive Elixir (1.14.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> items = My.items()
[
%{
check_in_time: 60,
imaging_time: 180,
lab_time: 120,
pharmacy_time: 100,
vital_time: 60
},
%{
check_in_time: 20,
imaging_time: 380,
lab_time: 10,
pharmacy_time: 200,
vital_time: 60
}
]
iex(2)> My.get_average(items)
119.0
The if is_nil()
statements can be changed to the shorter but more cryptic:
cit + (check_in_time || 0),
it + (imaging_time || 0),
lt + (lab_time || 0),
vt + (vital_time || 0),
pt + (pharmacy_time || 0),
But, you might want to consider using is_number()
because if check_in_time
is "a"
, then ||
will return "a"
not 0
--as will is_nil()
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论