pandas 索引名重名 怎么办
本文中索引名包括行名和列名,也就是
index
和column
。
也许很少有人注意到,pandas中索引名是可以重名的。
试一下。
import pandas as pdimport numpy as npdata = pd.DataFrame(np.random.randint(1,10,(5,5)),columns=list('aaabb'),index = list('11122'))
out:
a a a b bc 1 8 6 6 2c 8 9 4 9 7c 7 6 7 6 5d 2 7 8 1 6d 2 3 1 5 5
由此出现的问题,是我们在取值时取出的是dataframe
而不是series
。
data['a']
out:
a a ac 1 8 6c 8 9 4c 7 6 7d 2 7 8d 2 3 1
data.loc['c',:]
out:
a a a b bc 1 8 6 6 2c 8 9 4 9 7c 7 6 7 6 5
data.loc[:,'a']
out:
a a ac 1 8 6c 8 9 4c 7 6 7d 2 7 8d 2 3 1
这可能就直接导致了后面的bug出现。
很少有人希望索引名重名,但是问题总是不经意间来临。
data1 = pd.DataFrame(np.random.randint(1,10,(2,3)),columns=list('abc'),index = list('cd'))data2 = pd.DataFrame(np.random.randint(1,10,(2,3)),columns=list('bcd'),index = list('cd'))
pd.concat([data1,data2],axis=0)
out:
a b c dc 1.0 9 8 NaNd 5.0 6 6 NaNc NaN 8 3 7.0d NaN 9 3 5.0
行名重复。
pd.concat([data1,data2],axis=1)
out:
a b c b c dc 1 9 8 8 3 7d 5 6 6 9 3 5
列名重复。
有效的避免途径是concat
时使用ignore_index
参数。
pd.concat([data1,data2],axis=0,ignore_index=True)
out:
a b c d0 1.0 9 8 NaN1 5.0 6 6 NaN2 NaN 8 3 7.03 NaN 9 3 5.0
或者将verify_integrity
参数设为True
,concat
便会在出现重名时报错。
如果感觉到可能有重名,可以用has_duplicates
检验一下。
data.columns.has_duplicates
out:
True
去除重名
当遇到重名时,就需要有效的去除重名。
下面均以列名为例。
最简单的方法是删除重名。(废话)
data = pd.DataFrame(np.random.randint(1,10,(3,10)), columns=list('aabbccdefg'))data.loc[:,~data.columns.duplicated()]
out:
a b c d e f g0 5 3 8 5 8 9 11 5 1 9 4 1 6 32 2 4 3 2 2 2 8
这里必须使用loc
或iloc
,直接[]
会出现value_error
。因为在[]
中使用布尔列表取值会被认为是按行取值。
但是常见情况是必须保留并且将索引名唯一化。
重名 重命名
首先想到的方法是使用rename
。
data.rename(columns={'a':'a_1'},inplace=True)data
out:
a_1 a_1 b b c c d e f g0 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
但是rename
将所有a
都改为了a_1
。
在rename
失败后,唯一的办法就是自己重新构造columns
并赋值了。
简单的做法是在所有重名的索引名后面加一个东西。
data.columns = list('aabbccdefg')data.columns = [j + '_1' if data.columns.duplicated()[i] else j for i,j in enumerate(data.columns)]
out:
a a_1 b b_1 c c_1 d e f g0 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
但是这个只能对付重复次数小于等于2的情况。
可以用下面的代码做一个先行核查。
data.columns = list('aabbccdefg')np.all(data.columns.value_counts() <= 2)
out:
True
在遇到多重复(>2)时,可以用一下循环,不断的往重名列后面加东西。
data.columns=list('aabbbccccc')while data.columns.has_duplicates: data.columns = [j + '_' if data.columns.duplicated()[i] else j for i,j in enumerate(data.columns)]data
out:
a a_ b b_ b__ c c_ c__ c___ c____0 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
也可以稍改一下前面的代码,变为后面添加列的整数位置。
data.columns=list('aabbbccccc')data.columns = [j + f'_{i}' if data.columns.duplicated()[i] else j for i,j in enumerate(data.columns)]data
out:
a a_1 b b_3 b_4 c c_6 c_7 c_8 c_90 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
但是总归不是很完美,我们始终不能知道,这一列是该列名第几个重复。
如果需要将第几个这种参数,也加上去,就不能单纯的使用一行列表生成式解决了。
改变一下思路,只有逐个处理的时候我们才能顺利的知道重复总个数,进而判断这是第几个重复。
data.columns=list('aabbbccccc')for index in data.columns.value_counts()[data.columns.value_counts()>=2].index: duplicate = (data.columns==index) & (data.columns.duplicated()) data.columns = [j + f'_{np.sum(duplicate[:i+1])}' if duplicate[i] else j for i,j in enumerate(data.columns)]data
out:
a a_1 b b_1 b_2 c c_1 c_2 c_3 c_40 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
Perfect!
这里,首先通过value_counts
获得需要处理的列名。然后最重要的是通过生成duplicate
(结合列名和是否重复)确定需要的处理的是哪几列,然后一个sum
就能很轻松的确定这是第几个了。
这样的处理在需要更改时也很简单,比如希望所有重复的列名从第一个开始就加上后缀。
data.columns=list('aabbbccccc')for index in data.columns.value_counts()[data.columns.value_counts()>=2].index: duplicate = data.columns==index data.columns = [j + f'_{np.sum(duplicate[:i+1])}' if duplicate[i] else j for i,j in enumerate(data.columns)]data
out:
a_1 a_2 b_1 b_2 b_3 c_1 c_2 c_3 c_4 c_50 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
或者希望将后缀从0开始。
data.columns=list('aabbbccccc')for index in data.columns.value_counts()[data.columns.value_counts()>=2].index: duplicate = data.columns==index data.columns = [j + f'_{np.sum(duplicate[:i])}' if duplicate[i] else j for i,j in enumerate(data.columns)]pythondata
out:
a_0 a_1 b_0 b_1 b_2 c_0 c_1 c_2 c_3 c_40 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
甚至列名无脑按个数重复。
data.columns=list('aabbbccccc')for index in data.columns.value_counts()[data.columns.value_counts()>=2].index: duplicate = data.columns==index data.columns = [j + f'{index}' * np.sum(duplicate[:i]) if duplicate[i] else j for i,j in enumerate(data.columns)]data
out:
a aa b bb bbb c cc ccc cccc ccccc0 5 8 3 9 8 1 5 8 9 11 5 4 1 8 9 6 4 1 6 32 2 4 4 4 3 1 2 2 2 8
都可以轻松满足需求。
行名处理也是一样的,你,学会了吗?
我
我是 SSSimon Yang,关注我,用code解读世界