Last active
January 21, 2024 22:45
-
-
Save komasaru/3fd50ace9d989258b91f5f71e5dfb83c to your computer and use it in GitHub Desktop.
C++ source code to calculate a Kendall's Rank Correlation Coefficient.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; // 読み込み成功 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*********************************************************** | |
ケンドール順位相関係数の計算 | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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....