Skip to content

Instantly share code, notes, and snippets.

@komasaru
Last active January 21, 2024 22:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save komasaru/3fd50ace9d989258b91f5f71e5dfb83c to your computer and use it in GitHub Desktop.
Save komasaru/3fd50ace9d989258b91f5f71e5dfb83c to your computer and use it in GitHub Desktop.
C++ source code to calculate a Kendall's Rank Correlation Coefficient.
#include "calc.hpp"
#include <algorithm> // for std::count
#include <cmath> // for std::sqrt
#include <iostream>
#include <unordered_map>
#include <vector>
Calc::Calc(std::vector<std::vector<double>>& data) {
try {
cnt = data.size();
for (auto d : data) {
data_x.push_back(d[0]);
data_y.push_back(d[1]);
}
} catch (...) {
throw;
}
}
/**
* @brief ケンドール順位相関係数の計算
*
* @return RCC(double)
*/
double Calc::kendall() {
std::vector<double> rank_x; // ランク(X)
std::vector<double> rank_y; // ランク(Y)
int p = 0; // P
int q = 0; // Q
std::unordered_map<double, unsigned int> tai_x; // 同順位の数(X)
std::unordered_map<double, unsigned int> tai_y; // 同順位の数(Y)
double t_x; // Tx の sum 部分
double t_y; // Ty の sum 部分
double nn; // (n * n - n) / 2.0
double rcc = 0.0; // 順位相関係数
try {
// ランク付け
if (!rank_dbl(data_x, rank_x)) {
std::cout << "[ERROR] Failed to rank!" << std::endl;
return rcc;
}
if (!rank_dbl(data_y, rank_y)) {
std::cout << "[ERROR] Failed to rank!" << std::endl;
return rcc;
}
// P, Q 計算
if (!calc_pq(rank_x, rank_y, p, q)) {
std::cout << "[ERROR] Failed to calculate P, Q!" << std::endl;
return rcc;
}
// 同順位の数(2個以上のみ)
if (!count_tai(rank_x, tai_x)) {
std::cout << "[ERROR] Failed to count tais!" << std::endl;
return rcc;
}
if (!count_tai(rank_y, tai_y)) {
std::cout << "[ERROR] Failed to count tais!" << std::endl;
return rcc;
}
// Tx, Ty の sum 部分
t_x = sum_t(tai_x);
t_y = sum_t(tai_y);
// 順位相関係数
nn = (cnt * cnt - cnt) / 2.0;
rcc = (p - q) / (std::sqrt(nn - t_x) * std::sqrt(nn - t_y));
} catch (...) {
throw;
}
return rcc; // 計算成功
}
/**
* @brief ランク付け
*
* @param[ref] 元配列 ns (dbl)
* @param[ref] ランク配列 rs (int)
* @return 真偽(bool)
* @retval true 成功
* @retval false 失敗
*/
bool Calc::rank_dbl(std::vector<double>& ns, std::vector<double>& rs) {
unsigned int i;
unsigned int j;
unsigned int c;
try {
for (i = 0; i < cnt; i++) {
c = 0;
for (j = 0; j < cnt; j++)
if (ns[i] < ns[j]) c++;
rs.push_back(c + 1);
}
} catch (...) {
return false;
}
return true;
}
/**
* @brief P(x_s と x_t, y_s と y_t の大小関係が一致する組の数)
* (x_s = x_t or y_s = y_t は除く)
*
* @param[ref] ランク配列 rank_x (dbl)
* @param[ref] ランク配列 rank_y (dbl)
* @param[ref] P (int)
* @param[ref] Q (int)
* @return 真偽(bool)
* @retval true 成功
* @retval false 失敗
*/
bool Calc::calc_pq(
std::vector<double>& rank_x, std::vector<double>& rank_y, int& p, int& q) {
unsigned int i;
unsigned int j;
double w;
try {
for (i = 0; i < cnt - 1; i++) {
for (j = i + 1; j < cnt; j++) {
w = (rank_x[i] - rank_x[j]) * (rank_y[i] - rank_y[j]);
if (w > 0) p++;
if (w < 0) q++;
}
}
} catch (...) {
return false;
}
return true;
}
/**
* @brief 同順位の数
* (数が 2 未満は除去)
*
* @param[ref] ランク配列 rank (double)
* @param[ref] 同順位の数の連想配列 tai (<double, unsigned int>)
* @return 真偽(bool)
* @retval true 成功
* @retval false 失敗
*/
bool Calc::count_tai(
std::vector<double>& rank, std::unordered_map<double, unsigned int>& tai) {
unsigned int i;
try {
for (i = 0; i < cnt; i++) {
if (tai.find(rank[i]) == tai.end()) {
tai[rank[i]] = 1;
continue;
}
tai[rank[i]]++;
}
// 個数が 2 未満のものは削除
for (auto it = tai.begin(); it != tai.end();) {
if (it->second < 2) {
it = tai.erase(it);
} else {
++it;
}
}
} catch (...) {
return false;
}
return true;
}
/**
* @brief Tx, Ty の sum 部分
*
* @param[in] 同順位の数の連想配列 tai (<double, unsigned int>)
* @return Tx, Ty の sum
*/
double Calc::sum_t(std::unordered_map<double, unsigned int> tai) {
double s = 0.0;
try {
for (const auto [k, v] : tai) {
s += (v * v * v - v) / 2.0;
}
} catch (...) {
return s;
}
return s;
}
#ifndef RCC_KENDALL_CALC_HPP_
#define RCC_KENDALL_CALC_HPP_
#include <unordered_map>
#include <vector>
class Calc {
std::vector<double> data_x; // 元データ(x)
std::vector<double> data_y; // 元データ(y)
unsigned cnt; // データ件数
bool rank_dbl(std::vector<double>&, std::vector<double>&); // ランク付け
bool calc_pq(std::vector<double>&, std::vector<double>&, int&, int&); // P, Q
bool count_tai(
std::vector<double>&, std::unordered_map<double, unsigned int>&); // 同順位の数
double sum_t(std::unordered_map<double, unsigned int>); // Tx, Ty の sum 部分
public:
Calc(std::vector<std::vector<double>>&);
double kendall(); // ケンドール順位相関係数の計算
};
#endif
#include "file.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
bool File::get_text(std::vector<std::vector<double>>& data) {
try {
// ファイル OPEN
std::ifstream ifs(f_data);
if (!ifs.is_open()) return false; // 読み込み失敗
// ファイル READ
std::string buf; // 1行分バッファ
while (getline(ifs, buf)) {
std::vector<double> rec; // 1行分ベクタ
std::istringstream iss(buf); // 文字列ストリーム
std::string field; // 1列分文字列
// 1行分文字列を1行分ベクタに追加
double s;
while (iss >> s)
rec.push_back(s);
// 1行分ベクタを data ベクタに追加
if (rec.size() != 0) data.push_back(rec);
}
} catch (...) {
std::cerr << "EXCEPTION!" << std::endl;
return false;
}
return true; // 読み込み成功
}
#ifndef RCC_KENDALL_FILE_HPP_
#define RCC_KENDALL_FILE_HPP_
#include <fstream>
#include <string>
#include <vector>
class File {
std::string f_data;
public:
File(std::string f_data) : f_data(f_data) {}
bool get_text(std::vector<std::vector<double>>&);
};
#endif
/***********************************************************
ケンドール順位相関係数の計算
DATE AUTHOR VERSION
2020.09.11 mk-mode.com 1.00 新規作成
Copyright(C) 2020 mk-mode.com All Rights Reserved.
***********************************************************/
#include "calc.hpp"
#include "file.hpp"
#include <cstdlib> // for EXIT_XXXX
#include <iomanip> // for setprecision
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char* argv[]) {
std::string f_data; // データファイル名
std::vector<std::vector<double>> data; // データ配列
std::size_t i; // loop インデックス
double rcc; // 順位相関係数
try {
// コマンドライン引数のチェック
if (argc != 2) {
std::cerr << "[ERROR] Number of arguments is wrong!\n"
<< "[USAGE] ./rcc_spearmn <file_name>"
<< std::endl;
return EXIT_FAILURE;
}
// ファイル名取得
f_data = argv[1];
// データ取得
File file(f_data);
if (!file.get_text(data)) {
std::cout << "[ERROR] Failed to read the file!" << std::endl;
return EXIT_FAILURE;
}
// データ一覧出力
std::cout << std::fixed << std::setprecision(4);
std::cout << " X Y" << std::endl;
for (i = 0; i < data.size(); i++)
std::cout << std::setw(10) << std::right << data[i][0]
<< " "
<< std::setw(10) << std::right << data[i][1]
<< std::endl;
// 計算
Calc calc(data);
rcc = calc.kendall();
// 結果出力
std::cout << std::fixed << std::setprecision(8);
std::cout << "---\n"
<< "Kendall's RCC = " << std::setw(16) << std::right << rcc
<< std::endl;
} catch (...) {
std::cerr << "EXCEPTION!" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
@Junesh9
Copy link

Junesh9 commented Jan 4, 2022

I have no idea what kendell rank correlation is, but I came across it just now, while reading a research paper on wearable robots for scoliosis and kyphosis patients. And I came to your coding because I had been learning C++. I hope to learn more from both of these sources. Thank you for uploading this....

@pachadotdev
Copy link

very well done!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment