Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

QML Pricing

pricing_qml.livemd

QML Pricing

Mix.install([
  {:vega_lite, "~> 0.1.9"},
  {:kino_vega_lite, "~> 0.1.13"}
])

alias VegaLite, as: Vl

Model

Il modello si aspetta una pricelist, del tipo:

  prices = [
    %{upto: 10, price: 3.0},
    %{upto: 50, price: 2.5},
    %{upto: 100, price: 2.0}
  ]

Da cui calcola un Pricemodel, del tipo:

[
  %{from: 0,  upto: 10, max_amt: 30.0, offset: 0, price: 3.0},
  %{from: 10, upto: 50, max_amt: 100.0, offset: 30.0, price: 2.5},
  %{from: 50, upto: 100,  max_amt: 100.0, offset: 130.0, price: 2.0}
]

Che poi viene usato nel calcolo.

prices = [
  %{upto: 10, price: 3.0},
  %{upto: 50, price: 2.5},
  %{upto: 100, price: 2.0}
]

defmodule Pricelist do
  def pricemodel(pricelist) do
    sorted_list =
      pricelist
      |> Enum.sort_by(fn %{upto: u} -> u end)

    scaglioni =
      Enum.zip([%{upto: 0, price: 0} | sorted_list], sorted_list)
      |> Enum.map(fn {%{upto: from}, %{upto: to, price: p}} ->
        %{from: from, upto: to, price: p, offset: 0, max_amt: (to - from) * p}
      end)
      |> Enum.sort_by(fn %{upto: u} -> u end)

    scaglioni
    |> Enum.map(fn %{from: from, upto: _to} = scaglione ->
      somma_precedenti =
        scaglioni
        |> Enum.filter(fn %{from: v_from} -> from > v_from end)
        |> Enum.reduce(0, fn %{max_amt: max_amt}, acc -> acc + max_amt end)

      %{scaglione | offset: somma_precedenti}
    end)
  end

  def price(0, _), do: 0

  def price(n_agents, pricemodel) do
    pricemodel
    |> Enum.filter(fn %{from: from, upto: to, price: _p, offset: _o} ->
      n_agents > from &amp;&amp; n_agents <= to
    end)
    |> Enum.map(fn %{from: from, upto: _to, price: p, offset: o} -> o + p * (n_agents - from) end)
    |> List.first()
  end

  def price_info_at(agentcount, pricemodel) do
    p = price(agentcount, pricemodel)
    avg = p / agentcount
    %{n_agents: agentcount, price: p, avg_price: avg}
  end

  @doc """
  Una lista di valori da 5 a n_agents per le simulazioni.
  """
  def items(n_agents, pricemodel) do
    for n <- 5..n_agents do
      price_info_at(n, pricemodel)
    end
  end

  def graph(n_agents, pricemodel) do
    data = items(n_agents, pricemodel)

    Vl.new(width: 400, height: 300)
    |> Vl.data_from_values(data)
    |> Vl.concat(
      [
        Vl.new()
        |> Vl.mark(:line)
        |> Vl.encode_field(:x, "n_agents", type: :quantitative)
        |> Vl.encode_field(:y, "price", type: :quantitative),
        Vl.new()
        |> Vl.mark(:line)
        |> Vl.encode_field(:x, "n_agents", type: :quantitative)
        |> Vl.encode_field(:y, "avg_price", type: :quantitative)
      ],
      :vertical
    )
  end

  def defined_points_table(pricemodel) do
    data =
      [10, 20, 50]
      |> Enum.map(fn n -> price_info_at(n, pricemodel) end)

    Kino.DataTable.new(data,
      name: "Price evaluation",
      keys: [:n_agents, :price, :avg_price]
    )
  end

  def render(pricelist) do
    pm = pricemodel(pricelist)
    Pricelist.graph(1000, pm) |> Kino.render()
    Pricelist.defined_points_table(pm) |> Kino.render()
    0
  end

  def log(v, lbl) do
    IO.puts("#{lbl}: #{inspect(v)}")
    v
  end
end

# Pricelist.items(100, prices)

# Pricelist.graph(100, prices)

Pricelist.pricemodel(prices)

# Pricelist.price(60, Pricelist.pricemodel(prices))

# nil

Prezzi

MOdello uno

Pricelist.render([
  %{upto: 10, price: 3},
  %{upto: 50, price: 2.5},
  %{upto: 200, price: 2},
  %{upto: 9999, price: 1}
])

Modello due

Pricelist.render([
  %{upto: 10, price: 3},
  %{upto: 50, price: 2.5},
  %{upto: 200, price: 2},
  %{upto: 9999, price: 1}
])