from __future__ import division
"""This module is used to analyse the results of an ABX discrimination task
It collapses the result and give the mean score for each block of triplets
sharing the same on, across and by labels. It output a tab separated csv file
which columns are the relevant labels, the average score and the number of
triplets in the block. See `Files format <FilesFormat.html>`_ for a more
in-depth explanation.
It requires a score file and a task file.
Usage
-----
Form the command line:
.. code-block:: bash
python analyze.py data.score data.abx data.csv
In python:
.. code-block:: python
import ABXpy.analyze
# Prerequisite: calculate a task data.abx, and a score data.score
ABXpy.analyze.analyze(data.score, data.abx, data.csv)
"""
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 14 12:28:22 2013
@author: Thomas Schatz
"""
import h5py
import numpy as np
# import pandas
# import ast
import argparse
import os.path as path
import ABXpy.misc.type_fitting as type_fitting
# FIXME by_columns should be stored as attributes into the task file
# def analyze(task_file, score_file, analyze_file, by_columns=None):
# """Analyse the results of a task
# Parameters
# ----------
# task_file : string, hdf5 file
# the file containing the triplets and pairs of the task
# score_file : string, hdf5 file
# the file containing the score of a task
# analyse_file: string, csv file
# the file that will contain the analysis
# """
# # FIXME memory issues ?
# bys = []
# by_scores = []
# with h5py.File(score_file) as s:
# for by in s['scores']:
# scores = s['scores'][by][...]
# scores = np.float64(np.reshape(scores, scores.shape[0]))
# score = np.mean((scores + 1) / 2.)
# bys.append(by)
# by_scores.append(score)
# df = pandas.DataFrame({'by level': bys, 'average ABX score': by_scores})
# if not(by_columns is None): # FIXME ugly fix
# by_levels = np.array(map(ast.literal_eval, df['by level']))
# d = dict(zip(by_columns, zip(*by_levels)))
# for key in d:
# df[key] = d[key]
# del df['by level']
# df.to_csv(analyze_file, sep='\t')
[docs]def npdecode(keys, max_ind):
"""Vectorized implementation of the decoding of the labels:
i = (a1*n2 + a2)*n3 + a3 ...
"""
res = np.empty((len(keys), len(max_ind)))
aux = keys
k = len(max_ind)
for i in range(k - 1):
res[:, k - 1 - i] = np.mod(aux, max_ind[k - 1 - i])
aux = np.divide(aux - res[:, k - 1 - i], max_ind[k - 1 - i])
res[:, 0] = aux
return res
[docs]def unique_rows(arr):
"""Numpy unique applied to the row only.
"""
return (np.unique(np.ascontiguousarray(arr)
.view(np.dtype((np.void,
arr.dtype.itemsize * arr.shape[1]))))
.view(arr.dtype).reshape(-1, arr.shape[1]))
[docs]def collapse(scorefile, taskfile, fid):
"""Collapses the results for each triplets sharing the same on, across and
by labels.
"""
# wf_tmp = open('tmp_pandas.txt', 'wb')
scorefid = h5py.File(scorefile)
taskfid = h5py.File(taskfile)
nkeys = len(scorefid['scores'].keys())
# results = []
for key_idx, key in enumerate(scorefid['scores'].keys()):
print 'collapsing {0}/{1}'.format(key_idx + 1, nkeys)
context = key
tfrk = taskfid['regressors'][key]
tmp = tfrk[u'indexed_data']
indices = np.array(tmp)
if indices.size == 0:
continue
tmp = scorefid['scores'][key]
scores_arr = np.array(tmp)
tmp = np.ascontiguousarray(indices).view(
np.dtype((np.void, indices.dtype.itemsize * indices.shape[1])))
n_indices = np.max(indices, 0) + 1
if np.prod(n_indices) > 18446744073709551615:
print "type not big enough"
ind_type = type_fitting.fit_integer_type(np.prod(n_indices),
is_signed=False)
# encoding the indices of a triplet to a unique index
new_index = indices[:, 0].astype(ind_type)
for i in range(1, len(n_indices)):
new_index = indices[:, i] + n_indices[i] * new_index
permut = np.argsort(new_index)
i_unique = 0
# collapsing the score
key_reg = new_index[permut[0]]
mean = np.empty((len(permut), 3))
mean[0] = [key_reg, scores_arr[permut[0]], 0]
i_start = 0
for i, p in enumerate(permut[1:]):
i += 1
if new_index[p] != key_reg:
mean[i_unique, 1] = (np.mean(scores_arr[permut[i_start:i]])
+ 1) / 2
mean[i_unique, 2] = i - i_start
i_start = i
i_unique += 1
key_reg = new_index[p]
mean[i_unique] = [key_reg, 0, 0]
mean[i_unique] = [key_reg, (np.mean(scores_arr[permut[i_start:i + 1]])
+ 1) / 2, i - i_start + 1]
mean = np.resize(mean, (i_unique + 1, 3))
# retrieving the triplet indices from the unique index.
tmp = npdecode(mean[:, 0], n_indices)
regs = tfrk['indexed_datasets']
indexes = []
for reg in regs:
indexes.append(tfrk['indexes'][reg][:])
nregs = len(regs)
for i, key in enumerate(tmp):
aux = list()
for j in range(nregs):
aux.append(indexes[j][key[j]])
# aux.append((indexes[regs[j]])[key[j]])
score = mean[i, 1]
n = mean[i, 2]
result = aux + [context, score, int(n)]
fid.write('\t'.join(map(str, result)) + '\n')
# results.append(aux + [context, score, n])
# wf_tmp.write('\t'.join(map(str, results[-1])) + '\n')
# wf_tmp.close()
# return results
[docs]def analyze(scorefile, taskfile, outfile):
"""Analyse the results of a task
Parameters
----------
task_file : string, hdf5 file
the file containing the triplets and pairs of the task
score_file : string, hdf5 file
the file containing the score of a task
analyse_file: string, csv file
the file that will contain the analysis
"""
with open(outfile, 'w+') as fid:
taskfid = h5py.File(taskfile)
aux = taskfid['regressors']
tfrk = aux[aux.keys()[0]]
regs = tfrk['indexed_datasets']
string = ""
for reg in regs:
string += reg + "\t"
string += "by\tscore\tn\n"
fid.write(string)
collapse(scorefile, taskfile, fid)
# for r in results:
# fid.write('\t'.join(map(str, r)) + '\n')
[docs]def parse_args():
parser = argparse.ArgumentParser(
prog='collapse_results.py',
formatter_class=argparse.RawDescriptionHelpFormatter,
description='Collapse results of ABX on by conditions.',
epilog="""Example usage:
$ ./collapse_results.py abx.score abx.task abx_collapsed.txt
collapses the scores in abx.score by the conditions in abx.task and outputs
to plain text format in abx_collapsed.txt.""")
parser.add_argument('scorefile', metavar='SCORE',
nargs=1,
help='score file in hdf5 format')
parser.add_argument('taskfile', metavar='TASK',
nargs=1,
help='task file in hdf5 format')
parser.add_argument('output', metavar='OUTPUT',
nargs=1,
help='plain text output file')
return vars(parser.parse_args())
if __name__ == '__main__':
args = parse_args()
scorefile = args['scorefile'][0]
if not path.exists(scorefile):
print 'No such file:', scorefile
exit()
taskfile = args['taskfile'][0]
if not path.exists(taskfile):
print 'No such file:', taskfile
exit()
outfile = args['output'][0]
# if not path.exists(outfile):
# print 'No such file:', outfile
# exit()
analyze(scorefile, taskfile, outfile)