3 Ways to Solve Series Objects Are Mutable and Cannot be Hashed (Python)

🏷️

This is about TypeError: Series objects are mutable and cannot be hashed in Python.

This error occurs when you use mutable objects as key for a dictionary.

You’ll learn:

  • The meaning of the error
  • How to solve the error (3 ways)
  • Lots more

So if you want to understand this error in Python and how to solve it, then you’re in the right place.

It’s time for the first step!

Understand TypeError: Series Objects Are Mutable and Cannot be Hashed in Python

To understand the causes of the TypeError: “Series objects are mutable and cannot be hashed” error, let’s first take a look at the pandas module, as it’s easier to explain the error by using this module.

The Pandas Module 101

If you are getting started with data analysis, you will surely come across the pandas library.

Pandas is a convenient tool for quickly loading, processing, and saving even large amounts of tabular data. This module is part of a group of projects sponsored by numFocus

NumFocus supports various projects related to scientific computing, such as

  • Numpy
  • Matplotlib
  • Scipy
  • Scikit-learn

The pandas library is well documented. It is widely used and supported all over the world. You can even get involved in development by fixing bugs on the project here.

You can connect the pandas library with the following command:

import pandas

Pandas uses its own specific data structures to work with data quickly and conveniently. These structures are:

  • Series
  • Data frame

Series is a labelled one-dimensional data structure. Think of it as a table with one row

You can work with a series as a regular array (refer to the index number), and as an associated array, when you can use a key to access data items. 

You can also think of a data frame as a collection of series

Data frame is a two-dimensional labeled structure. It is very similar to a regular table. 

Thinking of a data frame as a table will help you understand how to create data frames and work with their elements, rows, and columns.

First, let’s create a series object from a list:

ser1 = pd.Series(["a", "b", "c", "d", "e"])
print(ser1)
0    a
1    b
2    c
3    d
4    e
dtype: object

This object is quite simple, but in addition to data, it contains indexes

These indexes can be named so that it looks more like a table row. Plus, let’s put some data inside the series—data of an agent:

ser2 = pd.Series([192168, "John Smith", "agent", 120000], index=["id", "name", "job_title", "salary"])
print(ser2)
id               192168
name         John Smith
job_title         agent
salary           120000
dtype: object

The series object is one-dimensional—it can contain only one row. 

To store multiple lines, you need a data frame.

Mutable Objects and Immutable Objects in Python

Series and data frame are both mutable data types.

All data types in Python fall into one of two categories: mutable and immutable

Many of the predefined Python data types are immutable object types such as numeric data (int, float, complex), character strings, and tuples. 

Other types are defined as mutable, such as lists, sets, and dictionaries

Newly defined user types (classes) can be defined as immutable or mutable

The mutability of an object of a specific type is a fundamentally important characteristic that determines whether or not an object of this type can act as a key for dictionaries.

Dictionaries in Python

Dictionaries represent one of the built-in, complexly structured types (collections) of Python (along with lists, tuples, and sets).

However, dictionaries in Python have a unique, exclusive meaning, as the Python interpreter itself is based on dictionaries, and the current namespace at the point of execution is a dictionary whose keys are object names.

The keys of the dictionary elements can be numeric values, strings, tuples, and objects of their own classes, or even functions. What these data types have in common is that they are all immutable

Mutable data types cannot be dictionary keys, and using them as such will raise a TypeError with the message “unhashable type”

The exception will be thrown right at runtime because the interpreter cannot determine the hashability of the key type before actually executing it.

As it becomes clear, Python mutable data types are data types for which the value returned by the hash() function is undefined.

If you try to create a dictionary whose key is the series object from above, you get an error:

dic1 = {ser1: "Our series"}
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-cc98405a72a9> in <module>()
----> 1 dic1 = {ser1: "Our series"}

/usr/local/lib/python3.6/dist-packages/pandas/core/generic.py in __hash__(self)
   1667     def __hash__(self):
   1668         raise TypeError(
-> 1669             f"{repr(type(self).__name__)} objects are mutable, "
   1670             f"thus they cannot be hashed"
   1671         )

TypeError: 'Series' objects are mutable, thus they cannot be hashed

You can quickly check on whether a particular object can be used for a key in a dictionary by trying to calculate its hash with a special hash function:

hash(ser2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-22-4378016f27e1> in <module>()
----> 1 hash(ser2)

/usr/local/lib/python3.6/dist-packages/pandas/core/generic.py in __hash__(self)
   1667     def __hash__(self):
   1668         raise TypeError(
-> 1669             f"{repr(type(self).__name__)} objects are mutable, "
   1670             f"thus they cannot be hashed"
   1671         )

You can deal with this error in several ways:

#1 Option to Solve TypeError: Series objects are mutable and cannot be hashed

So let’s NOT use the series object as a key. Rather, use only one of its fields

This method is suitable if the field value is unique—for example, some ID number. 

The ser2 object has an identifier field. It’s possible to build a dictionary with keys—the values of the id field in the series object ser2

Let’s store the agent’s callsign in it. 

But first, check on whether it’s possible to calculate the hash of the id field of ser2:

hash(ser2["id"])
192168

As you can see, the hash was evaluated successfully, so it’s possible to create a dictionary with this field as a key:

dic1 = {ser2["id"]: "Tiger"}
print(dic1)

{192168: 'Tiger'}

The problem with the example above is that, according to the dictionary, it is difficult to say something about the original object. It does not store all the data, but only the identifier, which is hard to interpret. 

However, it may be even safer for the task of storing an agent’s callsign.

#2 Option to Solve TypeError: Series objects are mutable and cannot be hashed

The second option is to convert the series object into some immutable type, such as a tuple. 

Let’s do this and try to calculate the hash of ser2 as a tuple:

tup = tuple(ser2)
hash(tup)
-2100016149781129293

The hash computed successfully. Let’s create a new dictionary and print it:

dic2 = {tup: "Tiger"}
print(dic2)
{(192168, 'John Smith', 'agent', 120000): 'Tiger'}

Better, but index values are lost here—you can save them if you use the zip function to link the field names and their values:

tup2 = tuple(zip(ser2.index, ser2))
hash(tup2)
4742336251574250744

Okay, done. Now let’s make another dictionary:

dic3 = {tup2: "Tiger"}
print(dic3)
{(('id', 192168), ('name', 'John Smith'), ('job_title', 'agent'), ('salary', 120000)): 'Tiger'}

Looks friendly and readable, but you cannot refer to the field by its name. To get the name of the agent, you will need to write something like this:

list(dic3.keys())[0][1][1]
John Smith

Doesn’t look very nice or convenient. It would be helpful to use another dictionary as a key. 

The series object is somewhat similar to a dictionary, and you can convert it into a dictionary via the same zip:

dic4 = dict(tup2)
print(dic4)
{'id': 192168, 'name': 'John Smith', 'job_title': 'agent', 'salary': 120000}

Here, you can access values by keys, just like in the Series object:

dic4['name']
John Smith

However, the dictionary object is not hashable, like the series object:

hash(dic4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-9b57975f1da1> in <module>()
----> 1 hash(dic4)

TypeError: unhashable type: 'dict'

So, you cannot make a dictionary with other dictionaries as the keys. However, the collections module has a data structure that looks like a hashable, immutable dictionary called namedtuple:

from collections import namedtuple

employer = namedtuple('Employer', dic4)
em1 = employer(**dic4)

print(em1)
Employer(id=192168, name='John Smith', job_title='agent', salary=120000)

The namedtuple object is hashable, which means it can be a dictionary key:

hash(em1)
-2100016149781129293

Let’s make a dictionary based on it and display it:

dic5 = {em1: "Tiger"}
print(dic5)

{Employer(id=192168, name='John Smith', job_title='agent', salary=120000): 'Tiger'}

Now you can refer to the key fields by their names. For example, like this:

for key in dic5:
  print(key.name)
  print(dic5[key])
John Smith
Tiger

#3 Option to Solve TypeError: Series objects are mutable and cannot be hashed

Finally, the last method.

You can create a class that will inherit from the series class, but add the hashing function to it.

You can take the hash in any way that is convenient for you.

Keep in mind that if the hash suddenly changes for some reason, this object will stop working as a key:

class MySeries(pd.Series):
  def __hash__(self):
    return hash(tuple(self))

myser = MySeries([192168, "John Smith", "agent", 120000], index=["id", "name", "job_title", "salary"])
hash(myser)

dic = {myser: "Tiger"}
print(dic)
{id               192168
name         John Smith
job_title         agent
salary           120000
dtype: object: 'Tiger'}

As you can see, the dictionary key is now an object of our new class MySeries

You can access the values of the dictionary by key:

dic[myser]
Tiger

However, if you change the hash of your object (in the present case, you can change the id), then you will no longer be able to access the values of the dictionary:

myser['id'] = 111111
dic[myser]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-66-534b17545b32> in <module>()
      1 myser['id'] = 111111
----> 2 dic[myser]

KeyError: id               111111
name         John Smith
job_title         agent
salary           120000
dtype: object

When you try to access a dictionary value by a modified MySeries object, you get a KeyError

This error appears due to the storage of data in the dictionary, which is described above. 

When trying to access data by key, Python calculates the hash of the given key and compares it to the hash table of the dictionary

That is why it is forbidden to use mutable objects as keys. You’ve seen above how to get around this if you really need to, but use this method with extreme caution.

Here’s more Python support:

✨ More Similar Articles

Menu