Модуль:CategoryStatistics

Материал из Поле цифровой дидактики

Для документации этого модуля может быть создана страница Модуль:CategoryStatistics/doc

local p = {}

local function getContent(titleText)
    local titleObj = mw.title.new(titleText)
    if not titleObj then
        return nil
    end
    return titleObj:getContent()
end

local function countWords(text)
    local n = 0
    for _ in mw.ustring.gmatch(text, '%S+') do
        n = n + 1
    end
    return n
end

local function getTitlesFromCategory(category, limit)
    if not mw.smw then
        return nil, 'Ошибка: mw.smw недоступен'
    end

    local results = mw.smw.ask({
        string.format('[[Category:%s]]', category),
        '?#-=title',
        'limit=' .. tostring(limit or 50)
    })

    if not results or #results == 0 then
        return nil, 'Нет страниц в категории: ' .. category
    end

    local titles = {}
    for _, item in ipairs(results) do
        if type(item) == 'table' then
            local title = item.title or item[1]
            if title and title ~= '' then
                table.insert(titles, title)
            end
        elseif type(item) == 'string' and item ~= '' then
            table.insert(titles, item)
        end
    end

    return titles, nil
end

local function summarize(data)
    local n = #data
    if n == 0 then
        return nil
    end

    local sum = 0
    local minv = data[1]
    local maxv = data[1]

    for _, v in ipairs(data) do
        sum = sum + v
        if v < minv then minv = v end
        if v > maxv then maxv = v end
    end

    local mean = sum / n

    table.sort(data)

    local median
    if n % 2 == 0 then
        median = (data[n / 2] + data[n / 2 + 1]) / 2
    else
        median = data[math.ceil(n / 2)]
    end

    local variance = 0
    if n > 1 then
        for _, v in ipairs(data) do
            variance = variance + (v - mean) ^ 2
        end
        variance = variance / (n - 1)
    else
        variance = 0
    end

    local stddev = math.sqrt(variance)

    return {
        count = n,
        sum = sum,
        mean = mean,
        median = median,
        variance = variance,
        stddev = stddev,
        min = minv,
        max = maxv
    }
end

function p.words(frame)
    local category = frame.args.category or frame.args[1] or 'Book'
    local limit = tonumber(frame.args.limit) or 50

    local titles, err = getTitlesFromCategory(category, limit)
    if not titles then
        return err
    end

    local values = {}
    for _, title in ipairs(titles) do
        local text = getContent(title)
        if text then
            table.insert(values, countWords(text))
        end
    end

    if #values == 0 then
        return 'Нет доступных страниц для анализа'
    end

    local s = summarize(values)

    local out = '{| class="wikitable"\n'
    out = out .. '! Показатель !! Значение\n'
    out = out .. '|-\n| Категория || ' .. category .. '\n'
    out = out .. '|-\n| Страниц в расчёте || ' .. s.count .. '\n'
    out = out .. '|-\n| Среднее число слов || ' .. string.format('%.2f', s.mean) .. '\n'
    out = out .. '|-\n| Медиана || ' .. string.format('%.2f', s.median) .. '\n'
    out = out .. '|-\n| Дисперсия || ' .. string.format('%.2f', s.variance) .. '\n'
    out = out .. '|-\n| Стандартное отклонение || ' .. string.format('%.2f', s.stddev) .. '\n'
    out = out .. '|-\n| Минимум || ' .. string.format('%.2f', s.min) .. '\n'
    out = out .. '|-\n| Максимум || ' .. string.format('%.2f', s.max) .. '\n'
    out = out .. '|}\n'

    return out
end

return p