Pull to refresh

Находим аномалии в российской статистике COVID-19

Reading time10 min
Views54K

Несмотря на рост заболеваемости covid-19 и горячих споров насчет принимаемых мер, разговоры про достоверность статистики немного поутихли. Кто-то согласен с руководством страны и считает, что с официальными данными все хорошо и они объективно описывают текущую ситуацию. Другие считают, что статистика безбожно врет и показатели, скорее всего, очень сильно занижены.

Последние часто ссылаются на совместное расследование «Медузы», «Медиазоны» и «Холода», которое утверждает, что в реестре Минздрава в 5 раз больше зарегистрированных случаев коронавируса, чем сообщается официально. Само расследование базируется на исследовании Сергея Шпилькина, который ранее с помощью статистических методов доказал фальсификации на выборах. В чем проблема этого исследования?

Гипотеза Шпилькина

Шпилькин обнаружил, что порядковые номера сертификатов «Госуслуг» для переболевших прирастают в 5 раз быстрее, чем число заболевших. На основании этого и был сделан вывод, что официальное число заболевших занижено в те же 5 раз. И это очень спорно, особенно с учетом того, что никаких дополнительных проверок этой гипотезы не проводилось. Помимо этого, само расследование строится на некоторых допущениях:

  1. Вывод сделан на основе всего нескольких десятков сертификатов. Для уверенных выводов это слишком малая выборка.

  2. Сделано допущение, что ид в таблице — порядковый номер переболевшего коронавирусом, а сама нумерация ид — сплошная, без пропусков. При столь небольшой выборке сертификатов проверить это невозможно. В нумерации легко могут появляться пропуски, например при использовании алгоритма Hi/Lo. При этом, даже если в определенном диапазоне идет сплошная нумерация — нет гарантий, что пропуски в нумерации отсутствуют до и после, т. к. приложение, вносящее данные в бд, могло неоднократно модифицироваться.

  3. Главное же допущение состоит в том, что порядковый номер, который выглядит как ид в базе данных, используется в одной таблице, в которой находятся только записи с коронавирусом. Но по информации минздрава, в реестр вносят данные не только о коронавирусе, но и о пневмонии и ОРВИ. А это уже делает выводы в изначальной статье полностью несостоятельными.

Это же в итоге подтвердили и авторы расследования, сделав собственную оценку количества переболевших коронавирусом в регистре, которая совпала с официальными данными. Правда, внимание на этом не акцентировали, а сделали вывод, что сам регистр заполняется неправильно.

Но хотелось бы не гадать относительно степени достоверности статистики, а знать её наверняка.

Поиск зацепок

Те, кто учился на технических специальностях, должны были проводить измерения на лабораторных работах по физике, для подтверждения изучаемых физических законов. Обычно ставится серия экспериментов, и по собранной статистике высчитывается итоговый результат с учетом ошибок измерений. Не всегда студенты выполняют измерения прилежно, а иногда и сам эксперимент такой, что итоговые погрешности огромны и даже простое попадание в ожидаемый порядок вычисляемого значения можно считать успехом.

Но некоторые студенты хитрят, не ставят сам эксперимент, или не учитывают «неудачные» измерения. Вместо этого они «подгоняют» измерения под ожидаемый результат, решая обратную задачу. Однако такие манипуляции раскрываются очень быстро, без каких-либо усилий со стороны преподавателя. Достаточно просто взглянуть на данные, чтобы увидеть, что они слишком правильные, а ошибки измерения аномально низкие, по сравнению с работами других студентов.

Попробуем и мы найти такие аномалии и слишком хорошие результаты в отечественной статистике коронавируса.

В качестве источника данных будем использовать ресурс ourworldindata, который агрегирует официальную статистику, публикуемую локальными министерствами здравоохранения. На ресурсе есть как визуальное представление информации, так и сами данные, подготовленные для дальнейшего анализа, доступные на github.

Посмотрим статистику ежедневных новых подтвержденных случаев по России, США и Германии.

Число новых подтвержденных случаев COVID-19 в день на миллион человек в России, Германии и США
Число новых подтвержденных случаев COVID-19 в день на миллион человек в России, Германии и США

Сразу же бросается в глаза то, что наш график гораздо более монотонный, чем у США и Германии. Может ли быть такое, что именно в США и Германии количество новых случаев в день колеблется сильнее, чем в других странах, а у нас график не выбивается из статистики? Все может быть, поэтому надо сравнить данные по всем доступным странам, чтобы делать однозначные выводы.

Для этого введем новые метрики. Первая пришедшая в голову идея — считать количество идущих подряд дней, когда величина новых случаев в день менялась менее чем на N случаев или меньше, чем в n раз.

Для подсчета метрики будем использовать pandas. Гистограммы построим с помощью matplotlib.

Первым делом создадим датафрейм по данным с репозитория ourworldindata.

import pandas as pd

df = pd.read_json('https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/owid-covid-data.json')

Затем отфильтруем данные, оставив только информацию о странах, с населением больше 15 миллионов человек. Это необходимо для уменьшения количества стран на графиках и исключения микрогосударств, которые могут сильно искажать итоговую картину.

df_filtered = df.T[
    (df.T.continent.apply(lambda continent: isinstance(continent, str)))
    & (df.T.population > 15_000_000)
    ].T

Проитерируемся по списку стран. Статистика по каждой стране находится в поле data. Создадим по значению этого поля отдельный датафрейм:

for country in df_filtered:
    data = pd.DataFrame.from_dict(df_filtered[country].data)

Для того, чтобы посчитать метрики, добавим новые колонки в датафрейме.

column = 'new_cases'
minus = column + '_shift_minus'
plus = column + '_shift_plus'
data.loc[:, minus] = data[column].shift(-1)
data.loc[:, plus] = data[column].shift(1)

Колонки 'new_cases_shift_minus' и 'new_cases_shift_plus' смещены влево и вправо относительно 'new_cases'. Это необходимо для поиска похожих соседних дней в статистике.

Для получения количества похожих дней по разности в значениях, сделаем выборку из датафрейма с помощью свойства loc. Выберем все дни, когда разница между предыдущим или последующим днем была меньше трех.

При небольших значениях числа зараженных, выше вероятность, что значения в соседних днях будут близки друг к другу по величине. Поэтому дополнительно отсечем все дни из выборки, когда было менее 100 новых зараженных в день.

diff = 3
data = data.loc[(data['new_cases'].ge(100))
                & (((data[minus] - data[column]).abs() < diff) | ((data[plus] - data[column]).abs() < diff))]

Интересующее нас количество дней в выборке будет равно количеству строк в датафрейме

series_days_length = data.shape[0]

Аналогично получим количество похожих дней по разности. Будем учитывать все дни, когда количество новых случаев отличалось в соседнем дне не более, чем на 1%.

rate = 0.01
r1 = (data[minus] / data[column] - 1).abs()
r2 = (data[plus] / data[minus] - 1).abs()
data = data.loc[(data['new_cases'].ge(100)) & ((r1.notnull() & (r1 <= rate)) | (r2.notnull() & (r2 <= rate)))]
Весь код, вместе с постройкой гистрограмм
import matplotlib.pyplot as plt
import pandas as pd
from pandas import DataFrame

URL = 'https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/owid-covid-data.json'


def find_series(df: DataFrame, column: str, diff: int = None, rate: float = None) -> DataFrame:
    minus = column + '_shift_minus'
    plus = column + '_shift_plus'
    df.loc[:, minus] = df[column].shift(-1)
    df.loc[:, plus] = df[column].shift(1)
    if diff is not None:
        return df.loc[(df['new_cases'].ge(100))
                      & (((df[minus] - df[column]).abs() < diff) | ((df[plus] - df[column]).abs() < diff))]
    if rate is not None:
        r1 = (df[minus] / df[column] - 1).abs()
        r2 = (df[plus] / df[column] - 1).abs()
        return df.loc[(df['new_cases'].ge(100)) & ((r1.notnull() & (r1 <= rate)) | (r2.notnull() & (r2 <= rate)))]
    else:
        raise Exception


def find_series_df(df: DataFrame, column: str, diff: int = None, rate: float = None) -> DataFrame:
    df_filtered = df.T[
        (df.T.continent.apply(lambda continent: isinstance(continent, str)))
        & (df.T.population > 15_000_000)
        ].T
    computed = []
    for country in df_filtered:
        data = pd.DataFrame.from_dict(df_filtered[country].data)
        data = find_series(data, column, diff, rate)
        computed.append({
            'country': country,
            'location': df_filtered[country].location,
            'series_days_length': data.shape[0],
            'days': len(df_filtered[country].data)
        })
    series_df = pd.DataFrame.from_records(computed)
    return series_df


def plot_one_series(series_df: DataFrame, name):
    series_df.sort_values('series_days_length', inplace=True)
    series_df.reset_index(inplace=True)

    plt.figure(figsize=(10, 13), dpi=120)
    plt.hlines(y=series_df.index, xmin=0, xmax=series_df.series_days_length, lw=6)
    cutoff = series_df.series_days_length.max() / 2
    for x, y, series, days in zip(series_df.series_days_length, series_df.index, series_df.series_days_length,
                                  series_df.days):
        plt.text(x, y, f'{series}   ({days})', horizontalalignment='left',
                 verticalalignment='center', fontdict={'color': 'red' if x > cutoff else 'green', 'size': 12})

    plt.title(name)
    plt.yticks(series_df.index, series_df.location, fontsize=12)
    plt.grid(linestyle='--', alpha=0.5)
    plt.xlim(0, series_df.series_days_length.max())
    russia_label = plt.gca().get_yticklabels()[series_df.loc[series_df.country == 'RUS'].index.tolist()[0]]
    russia_label.set_color('red')
    russia_label.set_weight("bold")
    russia_label.set_fontsize(14)
    plt.tight_layout()
    plt.savefig(name + '.png')
    plt.show()


def plot_series():
    df = pd.read_json(URL)

    series_df = find_series_df(df, 'new_cases', diff=3)
    plot_one_series(series_df, 'new_cases_series_diff<3')
    series_df = find_series_df(df, 'new_cases', rate=0.01)
    plot_one_series(series_df, 'new_cases_series_rate<=0.01')


if __name__ == '__main__':
    plot_series()

Изучим полученные результаты:

Количество дней подряд, когда число новых случаев менялось менее чем на 3 случая.
Количество дней подряд, когда число новых случаев менялось менее чем на 3 случая.

Без скобок — количество «странных» дней, в скобках — общее количество дней в статистике. Лишь в Сирии и Египте число таких дней сильно выше, чем в остальных странах. В первой стране более 10 лет идет гражданская война, а во второй явно хотят вернуть туристов как можно быстрее, так что правдивой информации от этих двух стран ждать не стоит. Россия же на этой гистограмме имеет вполне среднее значение.

Посмотрим теперь вторую гистограмму:

Количество дней подряд, когда число новых случаев менялось не более чем на 1%
Количество дней подряд, когда число новых случаев менялось не более чем на 1%

И тут мы резко вырываемся вперед, оставляя позади всех остальных. На текущий момент ровно треть дней в нашей статистике имеют разницу с соседними днями менее чем на 1%, что в разы больше, чем у всех остальных, не считая Египта. Думаю, даже без дополнительных расчетов наглядно видно, что российская статистика сильно искажена и явно подверглась «подгонке» под нужные результаты, как у нерадивых студентов. Другой вопрос, как ее искажают?

Рисуем нужную статистику без регистрации и смс

Для того, чтобы ответить на этот вопрос, надо понять, что представляет из себя статистика новых случаев, которую мы только что исследовали. Подробно этот вопрос раскрыт на ourworldindata:

В эпидемиологии случаи заболевания, часто делят на три разных уровня.

Эти определения часто специфичны для конкретного заболевания, но обычно имеют некоторые четкие и частично совпадающие критерии.

Случаи COVID-19, как и другие заболевания, широко определяются в рамках трехуровневой системы:подозрение на болезнь, вероятные и подтвержденные случаи.

1. Подозрение на болезнь
Подозрением на болезнь считается тот случай, когда проявляются клинические признаки и симптомы COVID-19, но не было лабораторных исследований.

2. Вероятный случай
Подозрение на болезнь с эпидемиологической связью с подтвержденным случаем. Это означает, что у кого-то проявляются симптомы COVID-19 и он либо находился в тесном контакте с положительным пациентом, либо находится в зоне, особенно пострадавшей от COVID.

3. Подтвержденный случай
Подтвержденный случай - это «человек с лабораторным подтверждением инфекции COVID-19», как объясняет Всемирная организация здравоохранения (ВОЗ).

Как правило, для подтверждения случая у человека должен быть положительный результат лабораторных анализов. Это верно независимо от того, проявились ли у них симптомы COVID-19 или нет.

<...>

Чтобы понять масштаб вспышки COVID-19 и отреагировать соответствующим образом, мы хотели бы знать, сколько людей инфицировано COVID-19. Мы хотели бы знать фактическое количество случаев.

Однако фактическое количество случаев COVID-19 неизвестно. Когда СМИ заявляют, что сообщают «количество случаев», они не являются точными и не говорят, что это количество подтвержденных случаев, о которых они говорят.

Фактическое количество случаев неизвестно ни нам, ни каким-либо другим исследовательским, правительственным или отчетным учреждениям.

Еще раз уточним: когда говорят о «случаях COVID-19», речь идет о подтвержденных случаях. Подтверждается каждый случай только тестом. А теперь вопрос: как исказить статистику нужным образом, ничего не фальсифицируя и не скрывая якобы имеющиеся случаи в реестре? Просто не делать тесты!

А далее выяснилось, что тесты нам никто делать не разбежался, так как моей мамы нет в списке контактных. По первому требованию делают тесты только людям 65+, и ни моя мама, ни я не попадаем в эту категорию. Платный тест мы тоже не могли сдать, потому что, представьте себе, его нельзя сдавать с симптомами ОРВИ!

Саратов, июнь 2020 ( https://fn-volga.ru/news/view/id/146915)

В калининградских поликлиниках невозможно сдать тесты на коронавирус, больных с симптомами отправляют лечиться домой без полной диагностики. При этом официальная статистика говорит о снижении числа заболевших.
<…>
– Когда вышла статья, мне позвонили и стали спрашивать, как было дело. Я попросила, чтобы взяли тесты у моих родителей, хоть мы в разных местах живем, но у папы онкология. Взяли тест и у старшей дочери, ей 19 лет. А вот вторую дочь, ей 12, и десятилетнего сына не проверили по сей день
<…>
– В апреле делали тесты всем. А сейчас не дождешься. Снижение числа заболевших создано искусственным образом.

Калининград, август 2020 (https://www.severreal.org/a/30764532.html)

...В итоге он нам сказал, что нужно добавить второе противовирусное и на этом поставил диагноз – ОРЗ. Тест ПЦР никто у нас не взял и не предложил даже, хотя у дочери не так давно заболела одногруппница, у нее был подтвержден ковид
<...>
На вопрос о тесте ПЦР мы получили отрицательный ответ: «Его у всех подряд не берут»
<...>
На очередной вопрос про тесты ПЦР я снова получила отрицательный ответ. Врач сказала, что их делают только согласно постановлению минздрава

Новосибирск, ноябрь 2020 ( https://sibkray.ru/news/1/939215/ )

Исследовав новости, можно найти много свидетельств того, что в разных регионах врачи отказываются делать тесты. Причем часто не делают именно для определенных групп, например, для детей и подростков. А если делать тесты только у стариков и не протестировать ни одного ребенка, то и в подтвержденных случаях не будет ни одного случая болезни у детей!

По изученной информации можно сделать предположение, что для каждого региона существуют некоторая квота на количество тестов / случаев. Причем, скорее всего, квоты отдельные для разных возрастных групп. Квоты формируются централизованно и спускаются в регионы. В регионах делают тесты до исчерпания квот по тестам или по зарегистрированным случаям, после чего отправляют данные в оперштаб, где на всякий случай проверяют их и спускают на места уже проверенные цифры «статистики». Проверка информации от регионов в любом случае нужна, т. к. местные чиновники по тем или иным экономическим или политическим соображениям могут начать передавать «наверх» слишком неправдоподобную информацию, поэтому обязательно должен быть некий централизованный фильтр коррекции ошибок.

Вот пример такой «статистики» на местах. Явно есть запрет на регистрацию больше 100 случаев, но и 99 тоже не рисуют, т. к. слишком «красивое» число. А вот 98 уже можно.
Вот пример такой «статистики» на местах. Явно есть запрет на регистрацию больше 100 случаев, но и 99 тоже не рисуют, т. к. слишком «красивое» число. А вот 98 уже можно.

Все это, естественно, лишь гипотеза. Однако гипотеза с квотами на тесты, на мой взгляд, гораздо правдоподобней, чем простое сокрытие информации из реестра. Такой способ внесения информации позволяет вполне честно и без всяких дополнительных фальсификаций получать практически любой заранее нужный результат. Достаточно лишь корректировать правила сбора статистики, воздействуя на количество проводимых тестов в день, получая нужное количество случаев, которое, по-факту, является функцией от числа тестов.

В следующей статье мы попробуем проверить эту гипотезу. Заодно попробуем понять характер и масштаб искажений в статистике по COVID-19.

Tags:
Hubs:
+154
Comments394

Articles