Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: TypeError: unhashable type: 'slice'

Tags:

python

csv

I am reading CSV using python, here is the code.

train_csv = open('train.csv')
test_csv = open('test.csv')
train_data_reader = csv.DictReader(train_csv)
test_data_reader = csv.DictReader(test_csv)

row=[]
for row in train_data_reader:
    X.append([int(item) for item in row[4:]]) 
    char = row[1]
    Y.append(charIntConversion(char))
    train_id.append(row[0])
    prediction.append(row[1])
for row in test_data_reader:
    test_id.append(row[0])
    test_X.append([int(item) for item in row[4:]]

when I tried to run the code, it shows that TypeError: unhashable type: 'slice' for

X.append([int(item) for item in row[4:]])
test_X.append([int(item) for item in row[4:]] 

X and test_X should contain the value from column 4 to last column of the CSV.

May I know what's wrong with my code and how can I fix this issue?

like image 977
BOSubuntu Avatar asked Feb 05 '23 20:02

BOSubuntu


1 Answers

I can see you have resolved your issue, but I thought I'd leave an answer here for any future readers. Who might get caught by this (I just was).

The issue is that the object is a dictionary and you are trying to pass it a slice, which is not hashable and hence cant be used as a dict key.

Simple example

>>> d = {0: 5, 1: 6, 2: 7, 3: 8, 4: 9}
>>> d[:5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'slice'

What you are trying to do is take n items from the collection. So the way to achieve this is to first convert the dict to a list (which is sliceable). To do this use dict.items, dict.keys or dict.values depending on your use case.

A dict (recent python 3.6 development notwithstanding) is not ordered and so what you will get back from dict.items (or friends) may not be in an order useful to you. So you can sort it before making the slice.

>>> sorted(d.items())[:5]
[(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]

The above code is slightly ugly, but works.

You could avoid the indexing by using islice from itertools

>>> from itertools import islice
>>> list(islice(sorted(d.items()), 5))
[(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]

You'd provide a key to sort if the default sorting order was not what you wanted. This way is inefficient as it sorts the entire collection before slicing, not sure of a way around that though.

Anyone arriving at this page and being puzzled by the above error, will not have realised they are attempting a slice on a dictionary. Performing a slice on a dictionary, doesn't really make a lot of sense and most experienced developers would realise that if you do that you are asking for an unpredictable result.

If you genuinely do want to take the first n elements from a dictionary, say a row from a csv file where the columns are in some prescribed order. It would be much better to format the required keys into a tuple and just take those elements from the dict.

e.g.

To take only the first two columns from the dict

people = [{'name': 'paul', 'job': 'programmer', 'age': 'old'}, 
          {'name': 'chris', 'job': 'student', 'age': 'young'}]

>>> for p in people:
...     res = [p[key] for key in ('name', 'job')]
...     print(res)
['paul', 'programmer']
['chris', 'student']

hth

like image 135
Paul Rooney Avatar answered Feb 07 '23 09:02

Paul Rooney