43 const std::vector<std::size_t>& row_data,
44 const std::vector<std::size_t>& col_data)
46 if (row_data.size() != col_data.size()) {
47 throw std::invalid_argument(
"statcpp::contingency_table: data lengths must match");
49 if (row_data.empty()) {
50 throw std::invalid_argument(
"statcpp::contingency_table: empty data");
54 std::size_t max_row = *std::max_element(row_data.begin(), row_data.end());
55 std::size_t max_col = *std::max_element(col_data.begin(), col_data.end());
56 std::size_t n_rows = max_row + 1;
57 std::size_t n_cols = max_col + 1;
60 std::vector<std::vector<std::size_t>> table(n_rows, std::vector<std::size_t>(n_cols, 0));
62 for (std::size_t i = 0; i < row_data.size(); ++i) {
63 table[row_data[i]][col_data[i]]++;
67 std::vector<std::size_t> row_totals(n_rows, 0);
68 std::vector<std::size_t> col_totals(n_cols, 0);
69 std::size_t total = 0;
71 for (std::size_t i = 0; i < n_rows; ++i) {
72 for (std::size_t j = 0; j < n_cols; ++j) {
73 row_totals[i] += table[i][j];
74 col_totals[j] += table[i][j];
79 return {table, row_totals, col_totals, total, n_rows, n_cols};
117 if (table.size() != 2 || table[0].size() != 2 || table[1].size() != 2) {
118 throw std::invalid_argument(
"statcpp::odds_ratio: table must be 2x2");
121 double a =
static_cast<double>(table[0][0]);
122 double b =
static_cast<double>(table[0][1]);
123 double c =
static_cast<double>(table[1][0]);
124 double d =
static_cast<double>(table[1][1]);
126 if (a == 0 || b == 0 || c == 0 || d == 0) {
127 throw std::invalid_argument(
"statcpp::odds_ratio: zero cell count not allowed");
130 double or_val = (a * d) / (b * c);
131 double log_or = std::log(or_val);
132 double se_log_or = std::sqrt(1.0/a + 1.0/b + 1.0/c + 1.0/d);
136 double ci_lower = std::exp(log_or - z * se_log_or);
137 double ci_upper = std::exp(log_or + z * se_log_or);
139 return {or_val, log_or, se_log_or, ci_lower, ci_upper};
193 if (table.size() != 2 || table[0].size() != 2 || table[1].size() != 2) {
194 throw std::invalid_argument(
"statcpp::relative_risk: table must be 2x2");
197 double a =
static_cast<double>(table[0][0]);
198 double b =
static_cast<double>(table[0][1]);
199 double c =
static_cast<double>(table[1][0]);
200 double d =
static_cast<double>(table[1][1]);
205 if (n1 == 0 || n0 == 0) {
206 throw std::invalid_argument(
"statcpp::relative_risk: zero row total");
208 if (a == 0 || c == 0) {
209 throw std::invalid_argument(
"statcpp::relative_risk: zero risk in a group");
212 double risk1 = a / n1;
213 double risk0 = c / n0;
215 double rr = risk1 / risk0;
216 double log_rr = std::log(rr);
219 double se_log_rr = std::sqrt((1.0 - risk1)/(a) + (1.0 - risk0)/(c));
223 double ci_lower = std::exp(log_rr - z * se_log_rr);
224 double ci_upper = std::exp(log_rr + z * se_log_rr);
226 return {rr, log_rr, se_log_rr, ci_lower, ci_upper};
270 if (table.size() != 2 || table[0].size() != 2 || table[1].size() != 2) {
271 throw std::invalid_argument(
"statcpp::risk_difference: table must be 2x2");
274 double a =
static_cast<double>(table[0][0]);
275 double b =
static_cast<double>(table[0][1]);
276 double c =
static_cast<double>(table[1][0]);
277 double d =
static_cast<double>(table[1][1]);
282 if (n1 == 0 || n0 == 0) {
283 throw std::invalid_argument(
"statcpp::risk_difference: zero row total");
286 double risk1 = a / n1;
287 double risk0 = c / n0;
289 double rd = risk1 - risk0;
292 double se = std::sqrt(risk1 * (1.0 - risk1) / n1 + risk0 * (1.0 - risk0) / n0);
296 double ci_lower = rd - z * se;
297 double ci_upper = rd + z * se;
299 return {rd, se, ci_lower, ci_upper};