質問: sorted(count.items(), key=lambda x:x[1], reverse=True) for i in xrange(100): print str(i + 1) + " & " + sorted_count[i][0] + " & " + str(sorted_count[i][1]) + " \\ " f2.write(str(i + 1) + " & " + sorted_count[i][0] + " & " + str(sorted_count[i][1]) + " \n ") f.close() ``` (ファイルの読み込みは省略しています) このコードは、nameに関わらずカウントしてしまいます。(右の値の出現回数だけをひたすら計算) エラーは出ずに、実行できます。 ここで、私が行いたいのはnameごとにカウントするということです。 例えば、1行目のりんごに関して右の値の出現回数を調べる。 それが終わったら、みかんに関して右の値の出現回数を調べる(この時、りんごの右の値は無視) 次は、ぶどうに関して右の値の出現回数を調べる(この時、りんご、みかんの右の値は無視) のように、1回調べたものは次の思考では無視するということです。 その上で上のコードのように数を数えたいです。 最終的には、nameごとの右の値の出現数を合計していきたいと考えています。 わかる方いらっしゃいましたら教えていただきたいです。 よろしくお願い致します。 コメント欄に記載されているjun68yktさんの丁寧な解説のおかげでデータ数が少ない場合の処理は行うことができました(コードが多いので省略させていただきます) しかし、データ数が増えた場合に処理が行えない(結果が出なくなってしまう)ので、データ数が多い場合でも実行できるコードの書き方があれば教えていただきたいです。 よろしくお願いいたします。
回答: こんにちは。 `defaultdict` を使うと、与えられたキーに対応する値がない場合の初期値を指定した dict を作ることができます。これを使うことで、makaronさんのご質問にある要件は、 ``` count = defaultdict(lambda: defaultdict(int)) ``` というdictに処理結果を保存することで、実現できると思います。 以下、上記の考えをもとに、標準入力からデータを読み込んで処理し、結果を標準出力にリストするpythonスクリプト **q149275.py** を作成し、これにデータファイル **input.txt** を読み込ませたログです。( $はシェルプロンプトです) > > $ **python -V** > > Python 2.7.10 > > $ **cat q149275.py** > > > ``` # coding: utf-8 import sys from collections import defaultdict count = defaultdict(lambda: defaultdict(int)) for line in sys.stdin: inputs = line.split("\t") if len(inputs) != 2: continue count[inputs[0]][int(inputs[1])] += 1 for name in count.keys(): print(name) for k, v in sorted(count[name].items(), key=lambda x: x[1], reverse=True): print('\t%d: %d 個' % (k, v)) ``` > > $ **cat input.txt | python q149275.py** > > > ``` レンコン 1: 6 個 8: 6 個 11: 6 個 3: 4 個 7: 4 個 10: 4 個 19: 4 個 4: 3 個 9: 3 個 13: 3 個 5: 2 個 12: 2 個 14: 2 個 15: 2 個 16: 2 個 17: 2 個 18: 2 個 2: 1 個 6: 1 個 りんご 14: 6 個 7: 5 個 16: 5 個 17: 5 個 1: 4 個 2: 4 個 6: 4 個 15: 4 個 3: 3 個 11: 3 個 19: 3 個 4: 2 個 5: 2 個 8: 2 個 9: 2 個 10: 2 個 12: 2 個 13: 2 個 18: 1 個 20: 1 個 みかん 13: 7 個 16: 7 個 2: 6 個 6: 6 個 19: 6 個 12: 4 個 17: 4 個 1: 3 個 3: 3 個 4: 3 個 18: 3 個 5: 2 個 7: 2 個 10: 2 個 11: 2 個 20: 2 個 9: 1 個 14: 1 個 15: 1 個 ぶどう 20: 11 個 2: 5 個 15: 5 個 3: 4 個 5: 4 個 11: 4 個 18: 4 個 7: 3 個 10: 3 個 13: 3 個 14: 3 個 1: 2 個 4: 2 個 6: 2 個 9: 2 個 17: 2 個 12: 1 個 16: 1 個 19: 1 個 やさい 4: 5 個 8: 4 個 9: 4 個 10: 4 個 13: 4 個 18: 4 個 1: 3 個 3: 3 個 7: 3 個 16: 3 個 17: 3 個 5: 2 個 6: 2 個 11: 2 個 15: 2 個 20: 2 個 2: 1 個 12: 1 個 ``` > > $ > > > なお、上記の入力で使用した input.txt は以下です。(データが300行あります) ``` name レンコン 1 りんご 17 ぶどう 11 ぶどう 2 ぶどう 16 やさい 1 りんご 17 みかん 4 みかん 2 りんご 4 ぶどう 20 レンコン 12 (・・・以下略) ``` 上記の **q149275.py** の入力元、および結果の出力先、出力形式などは、質問者様の都合にあわせて修正いただければと思います。 以上、参考になれば幸いです。 追記(2018/10/5) ============== こんにちは。コメントから頂きました要件から、以下の ~~4~~ 3点のPythonスクリプト * count.py * data_gen.py * make_map.py * ~~mod.py~~ ( にて削除 ) を作成しました。これらは以下のレポジトリより取得できますので、git clone するかダウンロードしてください。 以下、これらのスクリプトで行うことを順を追って説明します。 ### 1. 準備 * `data_gen.py`, `make_map.py`, `count.py` には、 `chmod +x` で実行権限を与えます。 * また、これら3つのソースの shebang は、`#!/usr/bin/env python3` となっており、 python3 を使う前提になっていますが、これは質問者様の環境に合わせて適宜、修正をお願い致します。 * これら3つの Pythonファイルのうち、実質的な数え上げを行っているのは、`count.py`に含まれる関数 `count`です。 * また、以下の説明では環境変数 `PATH` にカレントディレクトリ `.` が含まれていることを前提にしています。 ### 2. テストデータの作成 以下のコマンドで、`data.txt` にテストデータのファイルが作成されます。 ``` $ data_gen.py > data.txt ``` `data.txt` は以下のようなものになります。 ``` # names: ['A', 'B', 'C', 'D', 'E'] # num_codes: 10 # num_data: 20 D X-07 B X-05 B X-01 B X-03 B X-06 C X-02 B X-08 C X-04 B X-04 C X-08 A X-08 D X-08 C X-03 D X-09 B X-02 D X-10 C X-07 E X-04 D X-03 C X-09 ``` 上記テストデータの補足説明です。 * `names` はデータ行の1カラム目に出現する文字列の配列です。 * データ行の2カラム目の文字列を便宜上、コード(code)と呼ぶことにしています。 * `data_gen.py` で生成されるコードは `X-[0-9]{2}`という形式になります。 * `num_codes: 10` は、コードの数字部分が `01`から `10` を取り得ることを表します。 * `num_data` は生成されるデータ行の数です。 `names`の数と `num_codes` は、それぞれ、起動時の第1、第2引数で指定できます。たとえば ``` $ data_gen.py 9 6 > data.txt ``` とすると、`data.txt`の冒頭は以下のようになります。 ``` # names: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] # num_codes: 6 # num_data: 21 A X-03 A X-06 H X-01 (...以下略) ``` なお、 `num_data` は、 ``` num_data = int(len(names) * num_codes / 2.5) ``` としています。 ### 3. マップファイルの生成 上記 2. で作成したデータファイル `data.txt` を読み込んで、シンボルテーブルと、各シンボルの出現した行インデクスを含むファイルを生成します。生成されるファイルを説明の便宜上、マップファイルといいます。マップファイルの形式は json になります。 以下のようにパイプとリダイレクトを使って、マップファイルを生成します。 ``` $ cat data.txt | make_map.py > data.map.json ``` `data.map.json` は、以下のようなものになります。 ``` { "symbols": ["A", "X-03", "X-06", "H", "X-01", "I", "X-04", "E", "F", "X-05", "C", "G", "X-02", "D", "B"], "data": [ [0, 1], [0, 2], [3, 4], [3, 2], [5, 2], [3, 6], [7, 1], [8, 9], [10, 9], [8, 1], [11, 12], [8, 6], [3, 12], [13, 1], [0, 12], [11, 1], [8, 12], [10, 4], [10, 2], [14, 2], [8, 2] ], "indexes": [ [0, 1, 14], [0, 6, 9, 13, 15], [1, 3, 4, 18, 19, 20], [2, 3, 5, 12], [2, 17], [4], [5, 11], [6], [7, 9, 11, 16, 20], [7, 8], [8, 17, 18], [10, 15], [10, 12, 14, 16], [13], [19] ] } ``` 上記のマップファイルの説明です。 * `"symbols"` は、 入力された `data.txt`のデータ行に出現する、すべての name と code の文字列を出現順に並べた配列です。 * `"data"` は、入力された `data.txt`のデータ行を構成する name と code を、 [*name_symbol_index*, *code_symbol_index*] という、長さ2の配列に置き換え、この長さ2の配列を要素とする配列です。 * `"indexes"` は、 `"symbols"` の各要素の文字列が出現するデータ行のインデクスを昇順に並べた配列を要素とする配列です。 * たとえば上記の例では、`"indexes"` の先頭要素は `[0, 1, 14]`ですが、これは、 `symbols[0]`である文字列 `"A"`が、 `"data"` 配列のインデクス 0, 1, 14 すなわち、元のデータファイルのデータブロックの中の1,2,15 行目に出現することを表しています。 ### 4. 数えあげ 上記 3. で作成した `data.map.json` を読み込んで、各nameについての数えあげを行った結果を得るには以下のようにします。 ``` $ cat data.map.json | count.py ``` 上記により、以下のように、各 name を数え上げた個数が標準出力に表示されます。 > > A: 13 > > B: 2 > > C: 5 > > D: 2 > > E: 4 > > F: 8 > > G: 5 > > H: 12 > > I: 4 > > > 使い方の説明は以上です。 テストデータのファイル `data.txt` を `data_gen.py` で作成せずに、以下 ``` りんご 12 みかん 34 ぶどう 56 りんご 34 やさい 34 豆 12 ``` のように作成すると、これの数え上げの結果は以下となります。 ``` $ cat data.txt | make_map.py | count.py ぶどう: 1 みかん: 3 やさい: 1 りんご: 4 豆: 1 ``` #### 追記 こんにちは。 その後、考えてみましたが、約2000万という個数のデータを扱うのであれば、データの元にするはテキストファイルでよいとしても、数え上げのような分析をするために、いったんデータベースに入れるのがよいと思います。 例えば仮にデータベースをMySQLだとして、以下のようなテーブルを作ります。扱う数も大きいので、DECIMALを使って最大桁数の65を指定しています。 | カラム | 型 | 内容 | 主キー | | --- | --- | --- | --- | | id | INT | 行インデクス | ○ | | name | DECIMAL(65,0) | 各行の左側の数 | | | code | DECIMAL(65,0) | 各行の右側の数 | | データファイルの10行目(行インデクス=9) が以下 ``` 114760239 781870726160588800 ``` であったとき、このテーブルは以下のようなレコードを持ちます。 | id | name | code | | --- | --- | --- | | 9 | 114760239 | 781870726160588800 | このようにデータファイルの内容を、データベースのテーブルとして移すことができれば、数え上げを行うロジックにSQLを使うことができます。