データの値をグループ分けする方法

このQ&Aのポイント
  • データの値の近いものをグループ分けするアルゴリズムを考えています。
  • 差が±1以内のデータをグループ分けし、値の近い方を優先します。
  • 同じ差の場合は番号の若い方のグループとします。
回答を見る
  • ベストアンサー

データの値の近いものをグルーピングする方法について

以下アルゴリズムについて考えております。 複数のデータの内、差が±1以内のものをグループ分けする。 ただし、複数の候補がある場合は値の近い方をグループとする。 また、差がまったく同じであった場合は番号の若いほうのグループとする。 例1 ・入力 data1=1 data2=1.5 data3=2 data4=5 data5=6 ・出力 グループ1:1,2,3 グループ2:4,5 例2 ・入力 data1=1 data2=1.3 data3=2.3 data4=5 data5=6 ・出力 グループ1:1,2 グループ3:3 グループ2:4,5 例3 ・入力 data1=1 data2=2 data3=3 data4=4 data5=6 ・出力 グループ1:1,2 グループ2:3,4 グループ3:5 このような結果が得られるプログラムを作成中です。 簡単なグループ分けならできるのですが、候補が複数でてきてしまう場合におかしく なってしまいます。 以下に書いてあるものが初めに思いついた方法の概要です。 このプログラムでは適切なグループ分けが出来ませんでした。 現在はデータの差が±1以内ならばフラグを立てて後でグループ分けをするといった 方法でやっておりますがどうも上手くいきません。 そのため、今回のプログラムについて教えて頂きたく質問をさせて頂きました。 よろしくお願いします。 以下、初めに思いついた方法の概要です。 double in_data[] //データの値 double data[][] //データの差 group[]//グループ番号    //例えばgroup[3]=2だったら、データ3はグループ2に入る //data1-data2 //data1-data3 //・・・ //と計算をしていくforループ for(n=0; n<=i; n++){   for(m=n+1; m<i ;m++){     data[n][m]=fabs(in_data[n]-in_data[m]);     if(data[n][m]<=1){       group[n]= num;  //グループ番号振り分け       group[m]= num;       num++;     }   } }

質問者が選んだベストアンサー

  • ベストアンサー
  • yuu_x
  • ベストアンサー率52% (106/202)
回答No.4

#include "stdio.h" #include "stdlib.h" #include "math.h" typedef struct { unsigned int no; double value; } Data; int cmp (const void* a, const void* b) { return (int)ceil(((Data*)a)->value - ((Data*)b)->value); } int main(int argc, char* argv[]) { Data data[] = {{1, 1},{2, 1.3},{3, 2.3},{4, 5},{5, 6}}; int n = sizeof(data) / sizeof(*data); Data *group[n * 2]; int i, j; double min; memset(group, NULL, n * 2 * sizeof(Data*)); qsort(data, n, sizeof(*data), cmp); group[0] = &data[0]; min = data[0].value + 1; for (i=1, j=1; i<n; i++, j++) { if (data[i].value > min) { min = data[i].value + 1; j ++; } group[j] = &data[i]; } for (i=0; i<n*2; i++) { if (group[i] == NULL) { printf("\n"); if (group[i+1] == NULL) break; } else { printf("%d, ", group[i]->no); } } return 0; }

araiku-ma
質問者

お礼

#include "string.h" を追加したところ問題なく動きました。 ありがとうございました。

その他の回答 (4)

  • yuu_x
  • ベストアンサー率52% (106/202)
回答No.5

int n = sizeof(data) / sizeof(*data); → const int n ...

回答No.3

>セグメンテーション違反 なんとなくそんな気がした。俺、識者に怒られるんだろうなあ。 >data1 = 1 >data2 = 2 >data3 = 3 >グループ1:data1,data2 >グループ2:data3 data1とdata2が同じグループ data1 data2とdata3が同じグループ よって data1とdata2とdata3が同じグループ …にならないってことは、そういう仮定をおかないといけないんだろうなぁ、って思って敢えてそのへん聞かずに放置して回答してたんだ。

araiku-ma
質問者

補足

度々すみません。 セグメンテーション違反は私のミスでした、申し訳ありません。 補足を書いてから分かったことなので編集できませんでした。 このように書き換えたところ正常に動きました。ありがとうございました。 現在はソートされていない状態でも動くように考えております。 何かよい方法があったら御教授ください。 よろしくお願いします。 double in_data[5]; in_data[0] = 0; in_data[1] = 1.2; in_data[2] = 1.5; in_data[3] = 3; in_data[4] = 4; vector< double > data; for(int i=0;i<5;i++){ data.push_back(in_data[i]); }

noname#137556
noname#137556
回答No.2

> 複数のデータの内、差が±1以内のものをグループ分けする。 > ただし、複数の候補がある場合は値の近い方をグループとする。 > また、差がまったく同じであった場合は番号の若いほうのグループとする。 「値が近い」とか,「差が全く同じ」というのは,  隣接する要素についてなのか,  グループ内の最大値と最小値についてなのか, どちらでしょうか?

araiku-ma
質問者

補足

例えば data1=2,data2=3,data3=4 とあった場合に data2から見て±1以内であるものはdata1,3があるということです。 その場合は グループ1:data1,data2 グループ2:data3 となる結果が得たいです。 よろしくお願いします。

回答No.1

質問文が難しい条件で述べられているように見えるだけ。 数値はソートされているものとする。 「低い方を見たときに条件に一致している場合は、その低い方を基準としたグループに既に含まれている」から 「高い方と低い方の両方を見る必要はない」 http://ideone.com/vG2y6 #元々C#で書いたものを慣れていないC++に移植したが、問題あるかも。(初期化子が書けるかどうかとか詳しいことは知らん) #表示されている数値が1ずつ少ないのは、添字が0から始まっているから

araiku-ma
質問者

補足

ありがとうございます。 動かして見たところセグメンテーション違反が出ました。 もう少し考えてみます。 また、数値がソートされているとは限りませんので、そのソート 方法についても考えてみたいと思います。

関連するQ&A

  • 値によって組み分けを作成するプログラムについて

    こんにちは。 プログラミングでつまってしまったため質問させて頂きます。 プログラミング言語はC++です(visual c++ではありません)。 ■作成したいプログラム A,B,C,D,Eがある。 AとBの類似度は91% 以下同様に AとCは89% AとDは79% AとEは77% BとCは93% BとDは97% ・・・のように続いていく.. (*ABが似ている、BCが似ている。だからと言ってACが似ているとは言えない。) この中で、類似度が閾値以上である場合は同じ組とする。 ただし、複数のパターンが考えられる場合は組の数が一番すくなくなるように作る。 ■プログラムの動作イメージ 例1: 閾値=0.90 //入力データ 01 = 0.91; OK 02 = 0.90; OK 03 = 0.78; NG 04 = 0.83; NG 12 = 0.94; OK 13 = 0.78; NG 14 = 0.77; NG 23 = 0.78; NG 24 = 0.69; NG 34 = 0.94; OK OKなのは,01,02,12,34 つまり以下のような出力が得られる >>0,1,2 >>3,4 --------------------------------------- 例2: 閾値=0.90 //入力データ 01 = 0.91; OK 02 = 0.90; OK 03 = 0.94; OK 04 = 0.83; NG 12 = 0.95; OK 13 = 0.78; NG 14 = 0.77; NG 23 = 0.78; NG 24 = 0.69; NG 34 = 0.94; OK OKなのは,01,02,03,12,34 つまり 0,1,2 3,4 or 0,3 1,2 4 となった場合は、組数の少ない上を選択する。 従って以下のような出力が得られる。 >>0,1,2 >>3,4 --------------------------------------- 例3: 閾値=0.90 //入力データ 01 = 0.91; OK 02 = 0.90; OK 03 = 0.78; NG 04 = 0.83; NG 12 = 0.77; NG 13 = 0.78; NG 14 = 0.77; NG 23 = 0.78; NG 24 = 0.69; NG 34 = 0.94; OK OKなのは,01,02,34 この場合は、01と02は閾値を超えているが、12は閾値を超えていないため、 0,1,2を同じ組とはできない。 従って以下の2パターンが考えられる。 0,1 2 3,4 or 0,2 1 3,4 この場合は組の数は同じなのでどちらが出力されてもOK。 番号の若いものから出力されるとすると以下のような出力が得られる。 >>0,1 >>2 >>3,4 ------------------------------------------------------ 以上のようなプログラムを作成したいです。 自分で考えてみたのプログラムが以下のものです。 01,02,12,34は閾値を超えているということまではできたのですが、その後のグループの作り方のところで詰まってしまいました。 そこで、上記のような動作をするプログラムについて教えていただきたいです。 よろしくお願い致します。 int main(){ //閾値 double THRESHOLD = 0.9; //dataを格納する配列 double in_data[5][5] = {0}; //group_flagを格納する配列 bool group_flag[5][5] = {0}; in_data[0][1] = 0.91; in_data[0][2] = 0.90; in_data[0][3] = 0.78; in_data[0][4] = 0.83; in_data[1][2] = 0.94; in_data[1][3] = 0.78; in_data[1][4] = 0.77; in_data[2][3] = 0.78; in_data[2][4] = 0.69; in_data[3][4] = 0.94; //組の番号を格納する配列    //group[3]=2だったら、データ3はグループ2に入る int group[5]; int i=5; //データ数 int num=0; for(int n=0; n<=i; n++){ for(int m=n+1; m<i ;m++){ cout << "in_data[" << n << "][" << m << "]=" << in_data[n][m] << endl; if(in_data[n][m] >= THRESHOLD){ cout << "OK" << endl; group_flag[n][m] = 1; } } } for(int n=0; n<=i; n++){ for(int m=n+1; m<i ;m++){ if(group_flag[n][m] == 1){ cout << n << m <<endl; } } } }

  • 同じデータファイルなのに、linuxとMacで値が違ってしまうのはなぜ?

    職場でlinux(初心者です), 自宅でMacを使っています。 同じCプログラムを使って、同じバイナリデータファイルから数値を読み出したところ、職場のlinuxでやった場合と自宅のMacでやった場合では、違う値が出てきてしまいます。読み込むデータは1byteのバイナリなので、エンディアンは関係ないと思うのですが、ほかに原因があるのでしょうか?そばに詳しい人がおらず、困っています。どうしてこういうことが起きるのか、また、どうやったら解決できるのか、どなたか詳しい方、ぜひアドバイスをお願いします。 以下、プログラムの説明とプログラムの一部です。 バイナリデータファイルから、dposition[i]番目の1byteのデータをそれぞれ読み込み、それぞれ別の出力ファイルfo[i]に書き込みます。dposition[i]はPOSITION_NUM個あり、従って、出力ファイルもPOSITION_NUM個用意しています。dposition[i]で指定した場所はとびとびの不連続な場所です。データを読み込むときは、0番目から、一番大きいdposition[i]番目(=maxnum番目)のデータまで、すべてをarray[datanum]に読み込み、書き出すときにdposition[i]番目のデータのみを書き出すよう指定しています。読み込むバイナリファイルはFILE_NUM個あり、すべて同じ形式です。順次開いて読み込み出力を繰り返します。以下プログラムの一部です。 for (j = 0; j < FILE_NUM; j++) { if(NULL == (fi = fopen(fn_in[j],"rb"))) { printf("Cannot open input BISE File\n"); exit(1); } for (datanum = 0; datanum < maxnum+1; datanum++) { array[datanum] = fgetc(fi); } for (i = 0; i < POSITION_NUM; i++) { fprintf(fo[i],"%d\n",array[dposition[i]]); } fclose (fi); } 以下、linuxでやった場合の出力を左、Maxでやった場合の出力を右に並べて書きます。 104 104 100 100 74  22 83  83 60  60 0   0 106 106 79  33 167 167 182 182 180 0 188 188 ... ... 3,8,11番目が違う数字が出力されます。繰り返しますが、プログラムと入力ファイルは同じものです。 linuxでもMacでもターミナルでコンパイル、実行しています。 コンパイラのバージョンは、gccで検索したらなんだかたくさん出てきてしまって、どれが使っているコンパイラのバージョンなのかよくわかりませんでした。 ちなみに、MacはIntel系の方です。 ほか、アドバイスいただくにあたって、何か必要な情報があればご指摘ください。 ぜひよろしくおねがいします。

  • アスタリスクでダイヤ型を作る

    アスタリスクでダイヤ型の形を出力するプログラムを作ったのですが 実行例と比較して形が変になってしまいます。また、関数をif文で作ったのですがそれをfor文while文で実行できる形にするにはどのようにしたらいいのでしょうか? 実行例と私が作ったプログラムの実行データを画像に添付しました。 #include <stdio.h> int daiya( int i, int j, int Num ){ if( j<= i+Num && j<=-i+Num && j>= i-Num && j>=-i-Num ){ return 1; } } int main(){ int i, j, n; do{ puts("数字を入力:"); scanf( "%d", &n ); if(n<2||n>9){ printf("入力エラーです。もう一度入力して下さい\n"); } }while(n<2||n>9); for( j=-n/2; j<=n/2; j++ ){ for( i=-n/2; i<=n/2; i++ ){ if( daiya( i, j, n/2 ) == 1 ){ putchar('*'); } else { putchar(' '); } } putchar('\n'); } return 0; }

  • 設定した値が意図せぬ値に

    POJ 3176の問題です。 http://poj.org/problem?id=3176 #include <iostream> #include <algorithm> #define MAX 100 using namespace std; int main() { int n; cin >> n; int line[n-1][MAX]; int num[n-1][MAX]; cin >> line[0][0]; if(n==0) { cout << 0 <<endl; return 0; } else if(n==1) { cout << line[0][0] << endl; return 0; } for (int i =1;i < n;i++) { for (int j=0;j < i+1;j++) { int x; cin >> x; line[i][j]= x; } } for (int k= 0 ; k<n;k++) { num[n-1][k]= line [n-1][k]; } for (int k= n-2; k > 0 ; k--) { for (int l=0 ; l<k+1; l++) { num[k][l] = max (num[k+1][l],num[k+1][l+1]) + line[k][l]; } } num[0][0] = max(num[1][0],num[1][1]) + line[0][0]; cout << line[0][0] <<" "<<num[0][0]<<endl; return 0; } 入力 4 3 1 3 1 2 3 1 3 4 5 出力 1 12 最後に出力でline[0][0]をするようにしているのはバグチェックのためです。 ここで僕がわからないのはどうしてline[0][0]が3で宣言し、ほかでいじっていないにも関わらず、最後に1になっているのかということです。 どなたかわかる方がいらっしゃったらよろしくお願いします。

  • 入れ子で検索

    初心者です。 $time,$number,$data(実験回数、データ番号、データ) という3列のデータを、実験回数毎に同じデータ番号のデータを出力するプログラムを組もうとしています。データ数は回数、データ番号毎に同数あり、データ番号は実験毎に1から振られています。 出力結果としては↓の様にしたいと思っています。 実験回数、データ番号、データ、データ、データ しかし、データ数が多すぎて、データをpushして配列にしすぎたり、データ番号をハッシュにするとPCがメモリ不足になるため、forの入れ子にして、毎回データを読む様にしました。 for(my $time_n = 1; $time_n <= $input; ++$time_n){ #$inputは最大実験回数 for(my $n = $data_num * ($time_n - 1) + 1 ; $nn <= ($data_num * $time_n) ; ++$nn){ #$data_numは実験1回分のデータ番号数、 @array = (); while ($line = <FILE>) { chomp $line; ($time,$number,$data) = split /,/, $line; if($number == $n){ push @array, $data; } } print OUT3 "$time_n,$array[0],$array[1],$array[2]\n"; } } これでやってみると、実験回数1回目しかうまく出ません。なぜでしょうか? どなたか教えて下さい。 宜しくお願いします。

    • ベストアンサー
    • Perl
  • うまく実行ができない(相関図)

    C++で二次元配列を勉強しています。そこで二次元配列を使用して相関図をつくるプログラムの問題をやっているのですが、自分なりにプログラムをつくったところ、コンパイルはできるのですが実行すると無限ループがおこり、うまく実行できません。いくら考えてもなにがいけないのかわからないので質問させていただきました。 下のは読み込むデータファイル例とその実行例です http://kansai2channeler.hp.infoseek.co.jp/cgi-bin/joyful/img/5254.txt 以下はつくってみた間違いプログラムです #include<iostream> #include<string> #include<iomanip> #include<fstream> using namespace std; int main() { int data; string filename,Madata,Endata; int i=0,j=0,x=0,y=0,m=0,n=0,num=100; char* graph[21][21]; ifstream fin; cout << "This program's spec:"<< endl; cout << "point range=(0-100) div=5 leftspace=4 divspace=3" << endl; cout << "Input data File Name!:"; cin >> filename; fin.open(filename.c_str()); if(!fin) { cerr << "指定されたファイルを開けません" << endl; return 1; } while(fin >> data) { Madata[i]=data; fin >> data; Endata[i]=data; i++; } for(m=0; m<21; m++) { for(n=0; n<21; n++) { graph[m][n]=" "; } } for(m=0; m<i; m++) { x=Madata[m]/5; y=Endata[m]/5; graph[x][y]="*"; } for(x=0; x<21; x++) { cout << setw(3) << num << "|"; for(y=0; y<21; y++) { cout << setw(3) << graph[x][y]; } cout << endl; num=num-5; } cout << "----------------------------------------------------------------------" << endl; cout << setw(3) << "|"; for(num=0; num<=100; num+5) { cout << setw(3) << num; } cout << endl; return 0; } 上記のプログラムだと0の無限ループになります。 長々となりましたが、時間のある方教えてください、よろしくおねがいします

  • C言語のプログラミング (基礎)配列の問題ですが。

    次のような問題があって、プログラミングしてみたのですが、 コンパイルすると永久ループになってしまいます。 また、入力されるはずの整数が表示されません。 どうすればよいか教えていただけると助かります。 よろしくお願いします。 整数を順番に入力し、負の数が入力されたら、それまでに入力された非負の整数を、 入力された順番とは逆に全角空白で区切って出力するプログラムを作成しなさい。 自動評価の都合上、上記以外は表示しないこと。なお、入力は最大10回とする。 【0 1 2 3 4 -1 が入力された場合の表示例】 4 3 2 1 0       ************************************** #include <stdio.h> int main(void) { int num[10]; int n; for(n=1;n<11;n++){ printf("整数入力:¥n"); scanf ("%d", &num[n]); if(num[n]<0){ for(n=10;n>0;n--){ printf("%d ",num[n]); } } } }

  • ファイル入力方法について

    下記の入力ファイルinput.datの内容を出力するプログラムを作っています。 ・1001,1002,・・・はデータ番号 ・データ番号の下は、行数1~20,列数4のデータ ・「DATAEND」で入力停止 1001 aa bb 01 02 cc dd 03 04 1002 ee ff 05 06 gg hh 07 08 ii jj 09 10 1003 kk ll 11 12 mm nn 13 14 DATAEND データ番号なしの入力データを出力するソースが下記のものです。データ番号が入った入力データではうまくファイル入力できません。 大変お手数ですが、教えてください。よろしくお願いします。 #include <stdio.h> #include <stdlib.h> int main(){ int i,j,n=0,N=0,num[20],c[10][20],d[10][20]; char str[20][100],a[10][20][5],b[10][20][5]; FILE *fpi; if((fpi=fopen("input.dat","r"))==NULL){ fprintf(stderr,"Cannot open file input.dat\n"); exit(1); } for(i=0;i<10;i++){ for(j=0;j<20;j++){ fgets(str[i],sizeof(str[i]),fpi); if(sscanf(str[i],"%s %s %d %d",a[i][j],b[i][j],&c[i][j],&d[i][j]) != 4){ break; } n++; } num[i]=n; n=0; if(num[i]==0){ break; } N++; } for(i=0;i<N;i++){ for(j=0;j<num[i];j++){ printf("%s %s %d %d\n",a[i][j],b[i][j],c[i][j],d[i][j]); } printf("\n"); } fclose(fpi); return 0; }

  • perl ファイルのデータを編集したい

    初めまして、perl をやり初めたばかりです。作業は Linux 上で行ってます。 あるテキストファイル data.txt があります。 data.txt の中は、以下のようになっているとします。 100 200 300 400 500 600 これを読み込んで、例えば、 1 2 3 4 5 6 以上の様に各数字を百分の一にして出力したいと思ってます。 一応、色々と調べながらプログラムを書いてはみましたが 思う様に出力されません。以下そのプログラム。 #!/usr/bin/perl open(IN, "data.txt") or die ; @x = <IN>; close (IN); $ref_x = \@x; $n_data = @x; for ($i = 0; $i< $n_data; ++$i) { $$ref_x[$i] /= 100 ; print $x[$i], " "; } print"\n"; どなたか、perl にお詳しい方教えていただけないでしょうか? 宜しくお願い申し上げます。

  • エクセル 2つのシートのデータ集計

    あるシステムからエクスポートしたエクセルデータ(同一内のファイルにある)で複数あるシートデータを結合したいのですが、よくわかりません。(VLOOKUP関数でトライしたがエラー表示されてしまいま一つのシートデータAは概要データで、一つのシートデータBは明細データで、共通するデータは伝票番号になります。(例N..) Aシート N1・N2・N3 Bシート N11・N12・N21・N22・N23・N31 結合させたいシート N1N11・N1N12・N2N21・N2N22・N2N23・N3N31 どのような方法で結合させたら良いか教えてください。

専門家に質問してみよう