【问题标题】:Using apply method on pandas series getting TypeError 'Series' objects are mutable, thus they cannot be hashed在获得 TypeError 'Series' 对象的 pandas 系列上使用 apply 方法是可变的,因此它们不能被散列
【发布时间】:2017-01-30 12:49:28
【问题描述】:

我有两个数据框 D1D2。我想要实现的是对于D1D2 中的非整数和非浮点类型的任何列对,我想使用公式计算距离度量

 |A intersect B|/ |A union B|

我先定义了一个函数

def jaccard_d(series1, series2):
    if (series1.dtype is not (pd.np.dtype(int) or pd.np.dtype(float))) and     (series2.dtype is not (pd.np.dtype(int) or pd.np.dtype(float))):
        series1 = series1.drop_duplicates()
        series2 = series2.drop_duplicates()
        return len(set(series1).intersection(set(series2)))     /len(set(series1).union(set(series2)))
    else:
        return np.nan

然后我所做的是首先循环遍历D1 中的所有列,然后对于D1 中的每个固定列,我在jaccard_d 函数上使用apply。我尽量避免编写 2 层循环。可能有更好的方法而不写任何循环?

DC = dict.fromkeys(list(D1.columns))
INN = list(D2.columns)
for col in D1:
    DC[col] = dict(zip(INN, D2.apply(jaccard_d,D1[col])))

首先,我不确定我是否正确使用了 apply 函数,即我的 jaccard_d 函数需要 2 个系列作为输入,但是对于每次迭代,我将 D1[col] 作为一个系列,我想要使用applyD1[col] 应用于D2 的所有列

其次,我收到此错误“'Series' 对象是可变的,因此它们不能被散列”,我不太明白。任何 cmets 都表示赞赏。

我尝试只编写一个 2 层循环并使用我的函数 jaccard_d 来做到这一点。有用。但我想写更高效的代码。

【问题讨论】:

  • 您正在尝试使用 pandas series 作为字典键。
  • @hpaulj 你能详细说明一下吗?以及如何解决这个问题?为什么如果我只写一个 2 层循环这不会有问题?
  • 这可能是一个小问题,但如果您已经将系列放入set,是否需要drop_dupicatesset 无论如何都会收集唯一值。
  • 问题在于如何将数据系列传递给apply

标签: python pandas numpy


【解决方案1】:

因此,在四处寻找错误发生的确切位置并检查apply 文档之后,我推断您需要调用apply,因此:

 D2.apply(jaccard_d, args=(D1[col],))

你使用的是

 D2.apply(jaccard_d, axis=D1[col])

===================

我可以用一个简单的数据框重现您的错误消息:

In [589]: df=pd.DataFrame(np.arange(12).reshape(6,2))
In [590]: df
Out[590]: 
    0   1
0   0   1
1   2   3
2   4   5
3   6   7
4   8   9
5  10  11

将系列放入set 是可行的,就像我们将列表放入set 一样:

In [591]: set(df[0]).union(set(df[1]))
Out[591]: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}

但是,如果我尝试将包含系列的列表放入集合中,我会收到您的错误。

In [592]: set([df[0]])
....
TypeError: 'Series' objects are mutable, thus they cannot be hashed

如果问题不在于set 表达式,那么它会出现在dict() 表达式中。

您没有指定错误发生的位置,也没有给出 MVCe。

(但事实证明这是一个死胡同)

==========================

好的,模拟你的代码:

In [606]: DC=dict.fromkeys(list(df.columns))
In [607]: DC
Out[607]: {0: None, 1: None}
In [608]: INN=list(df.columns)
In [609]: INN
Out[609]: [0, 1]
In [610]: for col in df:
     ...:     dict(zip(INN, df.apply(jaccard_d, df[col])))
    ....
----> 2     dict(zip(INN, df.apply(jaccard_d, df[col])))


/usr/local/lib/python3.5/dist-packages/pandas/core/frame.py in apply(self, func, axis, broadcast, raw, reduce, args, **kwds)
   ...
-> 4125         axis = self._get_axis_number(axis)

/usr/local/lib/python3.5/dist-packages/pandas/core/generic.py in _get_axis_number(self, axis)
    326 
    327     def _get_axis_number(self, axis):
--> 328         axis = self._AXIS_ALIASES.get(axis, axis)
    ....        

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

所以问题出在

df.apply(jaccard_d, df[0])

问题与jaccard_d无关。如果我用简单替换它就会发生

def foo(series1, series2):
    print(series1)
    print(series2)
    return 1

=======================

但请查看apply 的文档

df.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)

如果不是关键字,第二个参数是轴号。所以我们一直在尝试使用Series作为轴号!难怪它反对!如果我更仔细地阅读错误跟踪,那应该很明显。

保留默认的axis=0,让我们将其他系列传递为args

In [632]: df.apply(jaccard_d,args=(df[1],))
Out[632]: 
0    0.0
1    1.0
dtype: float64

或者在你的循环中:

In [643]: for col in df:
     ...:     DC[col] = dict(zip(INN, df.apply(jaccard_d,args=(df[col],))))  
In [644]: DC
Out[644]: {0: {0: 1.0, 1: 0.0}, 1: {0: 0.0, 1: 1.0}}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多