C++11中随机打乱矩阵行列并保留行列名称的实现问题
解决方案:同步打乱矩阵行列与对应名称
你的核心问题有两个:
- 原代码中每行单独随机打乱列元素,导致各行的列顺序不一致,根本无法对应统一的列名称;
- 没有将行列名称与矩阵元素的打乱操作同步执行。
下面是修正后的完整代码,实现统一打乱列顺序并同步保留列名称,同时保留行名称与行数据的对应关系:
#include <iostream> #include <fstream> #include <vector> #include <string> #include <random> #include <algorithm> #include <numeric> // 用于std::iota生成连续索引 #include "util/base.hpp" // 写入带行列名称的矩阵(符合生物信息学常见格式) void writeMatrixWithNames(const std::vector<std::vector<double>>& matrix, const std::vector<std::string>& rowNames, const std::vector<std::string>& colNames, const std::string& out_file_name) { std::ofstream file(out_file_name); // 写入第一行列名称(首列留空对应行名称列) file << "\t"; for (size_t i = 0; i < colNames.size(); ++i) { file << colNames[i]; if (i != colNames.size() - 1) file << "\t"; } file << "\n"; // 逐行写入行名称+对应数据 for (size_t rowIdx = 0; rowIdx < matrix.size(); ++rowIdx) { file << rowNames[rowIdx] << "\t"; const auto& row = matrix[rowIdx]; for (size_t i = 0; i < row.size(); ++i) { file << row[i]; if (i != row.size() - 1) file << "\t"; } file << "\n"; } file.close(); } // 同步打乱矩阵列顺序与列名称 void permuteMatrixColumns(std::vector<std::vector<double>>& matrix, std::vector<std::string>& colNames, std::mt19937& g) { // 生成列索引序列并打乱 std::vector<size_t> colIndices(colNames.size()); std::iota(colIndices.begin(), colIndices.end(), 0); std::shuffle(colIndices.begin(), colIndices.end(), g); // 重新排列列名称 std::vector<std::string> newColNames(colNames.size()); for (size_t i = 0; i < colIndices.size(); ++i) { newColNames[i] = colNames[colIndices[i]]; } colNames.swap(newColNames); // 按同一索引排列所有行的元素,保证列对应关系一致 for (auto& row : matrix) { std::vector<double> newRow(row.size()); for (size_t i = 0; i < colIndices.size(); ++i) { newRow[i] = row[colIndices[i]]; } row.swap(newRow); } } // 可选:同步打乱矩阵行顺序与行名称 void permuteMatrixRows(std::vector<std::vector<double>>& matrix, std::vector<std::string>& rowNames, std::mt19937& g) { std::vector<size_t> rowIndices(rowNames.size()); std::iota(rowIndices.begin(), rowIndices.end(), 0); std::shuffle(rowIndices.begin(), rowIndices.end(), g); // 重新排列行名称 std::vector<std::string> newRowNames(rowNames.size()); for (size_t i = 0; i < rowIndices.size(); ++i) { newRowNames[i] = rowNames[rowIndices[i]]; } rowNames.swap(newRowNames); // 重新排列矩阵行 std::vector<std::vector<double>> newMatrix(matrix.size()); for (size_t i = 0; i < rowIndices.size(); ++i) { newMatrix[i] = std::move(matrix[rowIndices[i]]); } matrix.swap(newMatrix); } int main(int argc, char* argv[]) { if (argc < 5) { std::cerr << "Usage: " << argv[0] << " <input file> <output file base> <number of permutations> <seed>\n"; return 1; } std::string inputFile = argv[1]; std::string outputFileBase = argv[2]; int numPermutations = std::stoi(argv[3]); int seed = std::stoi(argv[4]); std::vector<std::string> colAnnotations; // 列名称/注释 std::vector<std::string> geneNames; // 行名称(基因名) std::vector<std::vector<double>> matrix; load(inputFile, colAnnotations, geneNames, matrix); std::mt19937 g(seed); for (int i = 0; i < numPermutations; ++i) { auto permutedMatrix = matrix; auto permutedColNames = colAnnotations; auto permutedRowNames = geneNames; // 若要打乱行,需复制行名称 // 执行列打乱(核心需求) permuteMatrixColumns(permutedMatrix, permutedColNames, g); // 若需要同时打乱行,取消下面注释 // permuteMatrixRows(permutedMatrix, permutedRowNames, g); std::string out_file_name = outputFileBase + "_permutation" + std::to_string(i + 1) + ".txt"; writeMatrixWithNames(permutedMatrix, permutedRowNames, permutedColNames, out_file_name); } return 0; }
关键说明
- 统一列打乱:生成一次列索引的随机排列,所有行都按此排列调整元素,确保列名称与列数据的对应关系始终一致;
- 行列名称同步:在打乱矩阵元素的同时,对名称向量执行完全相同的索引重排;
- 可选行打乱:如果需要同时打乱行顺序,取消
permuteMatrixRows的调用注释即可,逻辑与列打乱完全一致; - 标准化输出:新的写入函数生成带表头和行名的矩阵格式,适配后续Pearson/Spearman相关性分析的输入要求。
内容的提问来源于stack exchange,提问作者zerberus
相关产品推荐
相关产品推荐

