初心者のためのデヌタサむ゚ンスでのCおよびC ++の䜿甚

C99およびC ++ 11の叀兞的なデヌタサむ゚ンスの問題を解決したしょう。



PythonやRなどの蚀語はデヌタサむ゚ンスでたすたす人気が高たっおいたすが、CおよびC ++はデヌタサむ゚ンスの問題を効率的に解決するための匷力な遞択肢になり埗たす。この蚘事では、C99ずC ++ 11を䜿甚しお、Anscombeカルテットで動䜜するプログラムを䜜成したす。これに぀いおは、次に説明したす。



私はPythonずGNU Octaveの蚘事で垞に蚀語を孊ぶ動機に぀いお曞いたので、読む䟡倀がありたす。すべおのプログラムはコマンドラむン甚であり、グラフィカルナヌザヌむンタヌフェむスGUIではありたせん。完党な䟋はpolyglot_fitリポゞトリにありたす。



プログラミングの課題



このシリヌズで䜜成するプログラム



  • CSVファむルからデヌタを読み取りたす
  • 盎線でデヌタを補間したす぀たり、fx= m⋅x + q。
  • 結果を画像ファむルに曞き蟌みたす


これは、倚くのデヌタサむ゚ンティストが盎面する共通の課題です。デヌタの䟋は、次の衚に瀺すAnscombeカルテットの最初のセットです。これは人工的に䜜成されたデヌタのセットであり、盎線に圓おはめるず同じ結果になりたすが、グラフは倧きく異なりたす。デヌタファむルは、列を区切るためのタブずヘッダヌを圢成する耇数の行を含むテキストファむルです。この問題は、最初のセット぀たり、最初の2列のみを䜿甚したす。



アンスコムカルテット



画像


Cの゜リュヌション



Cは、今日䜿甚されおいる最も人気のある蚀語の1぀である汎甚プログラミング蚀語ですTIOBEむンデックス、RedMonkプログラミング蚀語ランキング、プログラミング蚀語人気床むンデックス、およびGitHub調査によるず。これは叀い蚀語であり1973幎頃に䜜成されたした、倚くの成功したプログラムがその蚀語で蚘述されおいたすたずえば、LinuxカヌネルやGit。この蚀語は、盎接的なメモリ管理に䜿甚されるため、コンピュヌタの内郚動䜜にもできる限り近いものです。これはコンパむルされた蚀語であるため、゜ヌスコヌドはコンパむラによっおマシンコヌドに倉換される必芁がありたす。圌の暙準ラむブラリはサむズが小さく軜いため、䞍足しおいる機胜を提䟛するために他のラむブラリが開発されおいたす。



これは、䞻にそのパフォヌマンスのために、数倀の圧瞮に最もよく䜿甚する蚀語です。倚くのボむラヌプレヌトコヌドが必芁なため、䜿甚するのは非垞に退屈ですが、さたざたな環境で十分にサポヌトされおいたす。C99暙準は、いく぀かの気の利いた機胜を远加した最近のリビゞョンであり、コンパむラヌによっお十分にサポヌトされおいたす。



初心者ず経隓豊富なナヌザヌの䞡方がこれらの蚀語を䜿甚できるように、CおよびC ++でのプログラミングの前提条件に぀いお説明したす。



取り付け



C99開発にはコンパむラが必芁です。私は通垞Clangを䜿甚したすが、もう1぀の本栌的なオヌプン゜ヌスコンパむラであるGCCが䜿甚したす。デヌタに合わせるために、GNU科孊ラむブラリヌを䜿甚するこずにしたした。プロットのために、私は劥圓なラむブラリを芋぀けるこずができなかったため、このプログラムは倖郚プログラムGnuplotに䟝存しおいたす。この䟋では、動的デヌタ構造を䜿甚しおデヌタを栌玍したす。これは、Berkeley Software DistributionBSDで定矩されおいたす。Fedoraぞの



むンストヌルは非垞に簡単です



sudo dnf install clang gnuplot gsl gsl-devel


コヌドコメント



C99では、コメントは行の先頭に//を远加するこずでフォヌマットされ、行の残りの郚分はむンタヌプリタヌによっお砎棄されたす。/ *ず* /の間のすべおも砎棄されたす。



//    .
/*     */


必芁なラむブラリ



ラむブラリは2぀の郚分で構成されおいたす。





ヘッダヌファむルは゜ヌスコヌドに含たれおおり、ラむブラリの゜ヌスコヌドは実行可胜ファむルにリンクされおいたす。したがっお、この䟋ではヘッダヌファむルが必芁です。



//  -
#include <stdio.h>
//  
#include <stdlib.h>
//     
#include <string.h>
//   ""  BSD
#include <sys/queue.h>
//   GSL
#include <gsl/gsl_fit.h>
#include <gsl/gsl_statistics_double.h>


䞻な機胜



Cでは、プログラムはmainずいう特別な関数内になければなりたせん。



int main(void) {
    ...
}


ここでは、前回のチュヌトリアルで説明したPythonずの違いに気付くでしょう。Pythonの堎合、゜ヌスファむルで芋぀かったコヌドはすべお実行されるためです。



倉数の定矩



Cでは、倉数を䜿甚する前に宣蚀し、型に関連付ける必芁がありたす。倉数を䜿甚する堎合は垞に、倉数に栌玍するデヌタを決定する必芁がありたす。倉数を定数倀ずしお䜿甚するかどうかを指定するこずもできたすが、これは必須ではありたせんが、コンパむラヌはこの情報を利甚できたす。リポゞトリヌの継ぎ手_C99.cプログラムの䟋



const char *input_file_name = "anscombe.csv";
const char *delimiter = "\t";
const unsigned int skip_header = 3;
const unsigned int column_x = 0;
const unsigned int column_y = 1;
const char *output_file_name = "fit_C99.csv";
const unsigned int N = 100;


Cの配列は、長さを事前に぀たり、コンパむル前に決定する必芁があるずいう意味で動的ではありたせん。



int data_array[1024];


通垞、ファむル内のデヌタポむントの数がわからないため、単䞀リンクリストを䜿甚したす。これは、無期限に拡匵できる動的なデヌタ構造です。幞い、BSD は単䞀にリンクされたリストを提䟛したす。定矩䟋は次のずおりです。



struct data_point {
    double x;
    double y;

    SLIST_ENTRY(data_point) entries;
};

SLIST_HEAD(data_list, data_point) head = SLIST_HEAD_INITIALIZER(head);
SLIST_INIT(&head);


この䟋では、x倀ずy倀の䞡方を含む構造化された倀で構成されるリストdata_pointを定矩しおいたす。構文はかなり耇雑ですが、盎感的であり、詳现な説明は冗長すぎたす。



プリントアりト



端末に出力するには、Octaveのprintf関数最初の蚘事で説明のように機胜するprintf関数を䜿甚できたす。



printf("####      C99 ####\n");


printf関数は、印刷された行の終わりに自動的に改行を远加しないため、自分で远加する必芁がありたす。最初の匕数は文字列で、関数に枡すこずができる他の匕数の圢匏に関する情報を含めるこずができたす。次に䟋を瀺したす。



printf("Slope: %f\n", slope);


デヌタの読み取り



ここで泚意が必芁な郚分がありたす... CにはCSVファむルを解析するためのラむブラリがいく぀かありたすが、Fedoraパッケヌゞリポゞトリにあるほど十分に安定しおいるか人気のあるものはありたせん。このチュヌトリアルに䟝存関係を远加する代わりに、この郚分を自分で䜜成するこずにしたした。繰り返しになりたすが、詳现に説明するのは冗長すぎたす。そのため、䞀般的なアむデアに぀いおのみ説明したす。゜ヌスコヌドのいく぀かの行は簡朔にするために無芖されたすが、完党な䟋はリポゞトリで芋぀けるこずができたす。



最初に入力ファむルを開きたす。



FILE* input_file = fopen(input_file_name, "r");


次に、゚ラヌが発生するたで、たたはファむルが終了するたで、ファむルを1行ず぀読み取りたす。



while (!ferror(input_file) && !feof(input_file)) {
    size_t buffer_size = 0;
    char *buffer = NULL;
    
    getline(&buffer, &buffer_size, input_file);

    ...
}


getline 関数は、POSIX.1-2008暙準に最近远加された玠晎らしい機胜です。ファむルの行党䜓を読み取り、必芁なメモリを割り圓おるこずができたす。次に、各行はstrtok関数を䜿甚しおトヌクンに分割されたす。トヌクンを芋お、必芁な列を遞択したす。



char *token = strtok(buffer, delimiter);

while (token != NULL)
{
    double value;
    sscanf(token, "%lf", &value);

    if (column == column_x) {
        x = value;
    } else if (column == column_y) {
        y = value;
    }

    column += 1;
    token = strtok(NULL, delimiter);
}


最埌に、x倀ずy倀を遞択しお、新しいポむントをリストに远加したす。



struct data_point *datum = malloc(sizeof(struct data_point));
datum->x = x;
datum->y = y;

SLIST_INSERT_HEAD(&head, datum, entries);


malloc 関数は、新しいポむントに䞀定量の氞続メモリを動的に割り圓お予玄したす。



フィッティングデヌタ



GSLの 線圢補間関数gsl_fit_linearは、入力ずしお通垞の配列を受け入れたす。したがっお、䜜成された配列のサむズを事前に知るこずができないため、手動で配列にメモリを割り圓おる必芁がありたす。



const size_t entries_number = row - skip_header - 1;

double *x = malloc(sizeof(double) * entries_number);
double *y = malloc(sizeof(double) * entries_number);


次に、リストを調べお、関連するデヌタを配列に栌玍したす。



SLIST_FOREACH(datum, &head, entries) {
    const double current_x = datum->x;
    const double current_y = datum->y;

    x[i] = current_x;
    y[i] = current_y;

    i += 1;
}


リストが完成したので、泚文を敎理したす。メモリリヌクを防ぐために、手動で割り圓おたメモリは垞に解攟しおください。メモリリヌクは、ひどい、ひどい、たたひどいものです。蚘憶が解攟されないたびに、庭のノヌムは頭を倱いたす。



while (!SLIST_EMPTY(&head)) {
    struct data_point *datum = SLIST_FIRST(&head);

    SLIST_REMOVE_HEAD(&head, entries);

    free(datum);
}


最埌に、最埌に、デヌタを適合させるこずができたす。



gsl_fit_linear(x, 1, y, 1, entries_number,
               &intercept, &slope,
               &cov00, &cov01, &cov11, &chi_squared);
const double r_value = gsl_stats_correlation(x, 1, y, 1, entries_number);

printf("Slope: %f\n", slope);
printf("Intercept: %f\n", intercept);
printf("Correlation coefficient: %f\n", r_value);


グラフをプロットする



グラフをプロットするには、倖郚プログラムを䜿甚する必芁がありたす。したがっお、フィッティング関数を倖郚ファむルに保存したす。



const double step_x = ((max_x + 1) - (min_x - 1)) / N;

for (unsigned int i = 0; i < N; i += 1) {
    const double current_x = (min_x - 1) + step_x * i;
    const double current_y = intercept + slope * current_x;

    fprintf(output_file, "%f\t%f\n", current_x, current_y);
}


Gnuplotプロットコマンドは次のようになりたす。



plot 'fit_C99.csv' using 1:2 with lines title 'Fit', 'anscombe.csv' using 1:2 with points pointtype 7 title 'Data'


結果



プログラムを実行する前に、プログラムをコンパむルする必芁がありたす。



clang -std=c99 -I/usr/include/ fitting_C99.c -L/usr/lib/ -L/usr/lib64/ -lgsl -lgslcblas -o fitting_C99




このコマンドは、コンパむラヌにC99暙準を䜿甚し、fitting_C99.cファむルを読み取り、gslおよびgslcblasラむブラリヌをロヌドしお、結果をFitting_C99に保存するように指瀺したす。コマンドラむンの結果の出力



####      C99 ####
 : 0.500091
: 3.000091
 : 0.816421


画像



以䞋は、Gnuplotを䜿甚しお生成された結果の画像です。



C ++ 11゜リュヌション



C ++は、今日䜿甚されおいる最も人気のある蚀語の1぀でもある汎甚プログラミング蚀語です。オブゞェクト指向プログラミングOOPに重点を眮いお、C蚀語1983幎の埌継ずしお䜜成されたした。 C ++は䞀般にCのスヌパヌセットず芋なされるため、CプログラムはC ++コンパむラでコンパむルする必芁がありたす。動䜜が異なるいく぀かの゚ッゞケヌスがあるため、これは垞に圓おはたるわけではありたせん。私の経隓では、C ++はCよりボむラヌプレヌトコヌドを必芁ずしたせんが、オブゞェクトを蚭蚈する堎合、その構文はより耇雑です。 C ++ 11暙準は最近のリビゞョンであり、コンパむラヌによっおサポヌトされる倚かれ少なかれいく぀かの気の利いた機胜を远加しおいたす。



C ++はCずほが互換性があるため、この2぀の違いにのみ焊点を圓おたす。この郚分でセクションを説明しない堎合、それはCず同じであるこずを意味したす。



取り付け



C ++の䟝存関係は、䟋Cず同じです。Fedoraでは、次のコマンドを実行したす。



sudo dnf install clang gnuplot gsl gsl-devel


必芁なラむブラリ



ラむブラリはCず同じように機胜したすが、includeディレクティブは少し異なりたす。



#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>

extern "C" {
#include <gsl/gsl_fit.h>
#include <gsl/gsl_statistics_double.h>
}


GSLラむブラリはCで蚘述されおいるため、この機胜に぀いおコンパむラに通知する必芁がありたす。



倉数の定矩



C ++は、Cよりも倚くのデヌタ型クラスをサポヌトしたす。たずえば、文字列型は、察応するCよりも倚くの機胜を備えおいたす。それに応じお倉数定矩を曎新したす。



const std::string input_file_name("anscombe.csv");


文字列などの構造化オブゞェクトの堎合、=蚘号を䜿甚せずに倉数を定矩できたす。



プリントアりト



printf 関数を䜿甚できたすが、coutを䜿甚する方が䞀般的です。<<挔算子を䜿甚しお、coutで印刷する文字列たたはオブゞェクトを指定したす。



std::cout << "####      C++11 ####" << std::endl;
...
std::cout << " : " << slope << std::endl;
std::cout << ": " << intercept << std::endl;
std::cout << " : " << r_value << std::endl;




デヌタの読み取り



回路は以前ず同じです。ファむルが開かれ、1行ず぀読み蟌たれたすが、構文は異なりたす。



std::ifstream input_file(input_file_name);

while (input_file.good()) {
    std::string line;

    getline(input_file, line);

    ...
}




文字列トヌクンは、C99の䟋ず同じ関数によっお取埗されたす。暙準のC配列の代わりに2぀のベクトルを䜿甚したす。ベクトルは、C ++ 暙準ラむブラリのC配列の拡匵で、mallocを呌び出さずにメモリを動的に管理したす。



std::vector<double> x;
std::vector<double> y;

//    x  y
x.emplace_back(value);
y.emplace_back(value);


フィッティングデヌタ



ベクトルはシヌケンシャルメモリを持っおいるこずが保蚌されおいるので、C ++でデヌタを収めるために、リストに぀いお心配する必芁はありたせん。ベクトルバッファヌぞのポむンタヌをフィッティング関数に盎接枡すこずができたす。



gsl_fit_linear(x.data(), 1, y.data(), 1, entries_number,
               &intercept, &slope,
               &cov00, &cov01, &cov11, &chi_squared);
const double r_value = gsl_stats_correlation(x.data(), 1, y.data(), 1, entries_number);

std::cout << " : " << slope << std::endl;
std::cout << ": " << intercept << std::endl;
std::cout << " : " << r_value << std::endl;


グラフをプロットする



プロットは以前ず同じ方法で行われたす。ファむルに曞き蟌む



const double step_x = ((max_x + 1) - (min_x - 1)) / N;

for (unsigned int i = 0; i < N; i += 1) {
    const double current_x = (min_x - 1) + step_x * i;
    const double current_y = intercept + slope * current_x;

    output_file << current_x << "\t" << current_y << std::endl;
}

output_file.close();


次に、Gnuplotを䜿甚しおグラフをプロットしたす。



結果



プログラムを実行する前に、同様のコマンドでコンパむルする必芁がありたす。



clang++ -std=c++11 -I/usr/include/ fitting_Cpp11.cpp -L/usr/lib/ -L/usr/lib64/ -lgsl -lgslcblas -o fitting_Cpp11


コマンドラむンでの結果の出力



####      C++11 ####
 : 0.500091
: 3.00009
 : 0.816421


そしお、これがGnuplotで生成された結果の画像です。



画像



結論



この蚘事では、C99およびC ++ 11でのデヌタのフィッティングずプロットの䟋を瀺したす。 C ++はCずほが互換性があるため、この蚘事では類䌌点を䜿甚しお2番目の䟋を蚘述したす。䞀郚の偎面では、C ++は明瀺的なメモリ管理の負担を郚分的に軜枛するため、䜿いやすくなっおいたすが、OOPのクラスを䜜成する機胜を導入しおいるため、構文はより耇雑です。ただし、OOPはプログラミングスタむルであるため、OOPテクニックを䜿甚しおCで蚘述するこずもできたす。これは、どの蚀語でも䜿甚できたす。GObjectやJanssonラむブラリなど、CでのOOPの優れた䟋がいく぀かありたす。



数倀の凊理には、構文が単玔でサポヌト範囲が広いため、C99を䜿甚するこずを奜みたす。最近たで、C ++ 11は広くサポヌトされおいなかったため、以前のバヌゞョンでは荒削りな郚分を避けようずしたした。より耇雑な゜フトりェアの堎合は、C ++を遞択するこずをお勧めしたす。



デヌタサむ゚ンスにCたたはC ++を䜿甚しおいたすかコメントであなたの経隓を共有しおください。



画像



SkillFactoryの有料オンラむンコヌスを受講しお、スキルず絊䞎の泚目の職業をれロから取埗する方法の詳现をご芧ください。






続きを読む






All Articles