user4987992
user4987992

Reputation: 25

python csv to nested lists, counter

I'm trying to do something that is probably not so difficult in Python (2.7.6), but I'm nowhere near solution. This is for an fMRI experiment (in expyriment, specifically). I have a csv that looks something like this:

level,string,key
1,string11,1
1,string12,0
1,string13,1
2,string21,1
2,string22,1
2,string23,0

There are 20 levels (1:20), the csv has roughly 5000 records. What I'd like to do is present a string from the 2nd column from a particular level (the starting level is determined in advance). So for example we've tested a subject and determined that they will start at level 5. A random string is presented from level 5 (e.g., "a cow is an animal"). The subject presses a button for true/false (answer in the "key" column - 1/0). The tricky bit is that every 4 consecutive correct answers the level increases by 1, an incorrect answer decreases the level by 1.

Timing/efficiency is somewhat important, so I was thinking the csv could be parsed in advance, then the part working during the experiment is just accessing the data stored by level. I've looked at nested dictionaries, doesn't seem right. Perhaps nested lists? I'm a bit out of my depth. I wrote a working example in bash, in case that is more clear than my explanation.

#!/bin/bash

csv="file.csv"
level="5"
ilevel="0"
counter="1"

while [ "$counter" -le 10 ]; do
    stim=$(awk -F',' -v l="$level" '{if($1==l) print $2","$3}' "$csv"|sort -R|head -n1)
    stim1=$(echo "$stim"|cut -d',' -f1)
    stim2=$(echo "$stim"|cut -d',' -f2)
    read -p "$stim1 (t/f): " ANS 
    if [[ "$stim2" == "1" && "$ANS" == "t" || "$stim2" == "0" && "$ANS" == "f" ]]; then #if correct
        ilevel=$(echo "scale=2;$ilevel+.25"|bc) #add .25 to ilevel counter
        if [[ "$ilevel" == "1.00" ]]; then
            if [[ "$level" < "20" ]]; then
                level=$(echo "$level+1"|bc) #increase level
            fi
            ilevel=0
        fi
    else
        if [[ "$level" > "1" ]]; then #if incorrect
            level=$(echo "$level-1"|bc) #decrease level
        fi
        ilevel=0
    fi
    let "counter += 1"
done

The bash script is just for the purpose of a working example - the counter, printing to screen, user feedback.. is all handled differently in reality and is already done. I just need to figure out the sorting of the csv and manipulating the level. Thanks in advance for any suggestions.

Upvotes: 2

Views: 118

Answers (1)

Padraic Cunningham
Padraic Cunningham

Reputation: 180441

To group the strings use a dict and use random.choice to pick a random string :

 from collections import defaultdict
import csv
from random import choice
d = defaultdict(list)


with open("in.csv") as f:
    next(f)
    r = csv.reader(f)
    for row in r:
        d[int(row[0])].append(row[1])


level = 2

ch = choice(d[level])
print(ch)
string21

If you need the answer/ column 2:

from collections import defaultdict

d = defaultdict(defaultdict)

with open("in.csv") as f:
    next(f)
    r = csv.reader(f)
    for row in r:
        d[int(row[0])][row[1]] = row[2]

from pprint import pprint as pp
pp(dict(d))
level = 2
ch = choice(list(d[level]))
ans = d[level][ch]
print(ch,ans)

Output:

 # main dict keys are levels, nested dict contain key/value pairing
 # question and answer
{1: defaultdict(None, {'string11': '1', 'string12': '0', 'string13': '1'}),
 2: defaultdict(None, {'string21': '1', 'string23': '0', 'string22': '1'})}

The main dict keys are the levels, the nested dicts hold all the level strings which are the keys of the nested dicts and the values are column 3 the answers.

If you are using a gui which it seems you are your logic will be something like the following but obviously using your gui methods:

level = 2
streak = 0
# current level questions
level_choice = list(d[level])

while True:
    # pick random string/question
    ch = choice(level_choice)
    # correct answer
    ans = d[level][ch]
    inp = raw_input("ask questions using ch")
    # if answer  is correct increment streak
    if inp == ans:
        streak += 1
    else:
        # else it is wrong, drop a level and update variables
        level -= 1
        streak = 0
        level_choice = list(d[level])
    # if we have four in a row, up level ad again update variables
    if streak == 4:
        level += 1
        streak = 0
        level_choice = list(d[level])

Obviously there must come a point where numerous answers that are wrong will mean the level hits 0 so that may be the time to break, that logic you will have to decide. If you don't want repeated questions then you will have to remove them from the dict.

Upvotes: 1

Related Questions