Làm thế nào để bạn thay thế các giá trị trùng lặp bằng nhiều chuỗi duy nhất trong Pandas?


8
import pandas as pd
import numpy as np
data = {'Name':['Tom', 'Tom', 'Jack', 'Terry'], 'Age':[20, 21, 19, 18]} 
df = pd.DataFrame(data)

Hãy nói rằng tôi có một khung dữ liệu trông như thế này. Tôi đang cố gắng tìm ra cách kiểm tra cột Tên cho giá trị 'Tom' và nếu tôi tìm thấy nó lần đầu tiên tôi thay thế nó bằng giá trị 'FirstTom' và lần thứ hai nó xuất hiện, tôi thay thế nó bằng giá trị 'SecondTom' . Làm thế nào để bạn thực hiện điều này? Tôi đã sử dụng phương pháp thay thế trước đây nhưng chỉ để thay thế tất cả Tom bằng một giá trị duy nhất. Tôi không muốn thêm 1 vào cuối giá trị, nhưng thay đổi hoàn toàn chuỗi thành thứ khác.

Biên tập:

Nếu df trông giống như dưới đây, làm thế nào chúng ta sẽ kiểm tra Tom trong cột đầu tiên và cột thứ hai và sau đó thay thế phiên bản đầu tiên bằng FirstTom và phiên bản thứ hai bằng SecondTom

data = {'Name':['Tom', 'Jerry', 'Jack', 'Terry'], 'OtherName':[Tom, John, Bob,Steve]}

Câu trả lời:


9

Chỉ cần thêm vào các giải pháp hiện có, bạn có thể sử dụng inflectđể tạo từ điển động

import inflect
p = inflect.engine()

df['Name'] += df.groupby('Name').cumcount().add(1).map(p.ordinal).radd('_')
print(df)

        Name  Age
0    Tom_1st   20
1    Tom_2nd   21
2   Jack_1st   19
3  Terry_1st   18

7

Chúng tôi có thể làm cumcount

df.Name=df.Name+df.groupby('Name').cumcount().astype(str)
df
     Name  Age
0    Tom0   20
1    Tom1   21
2   Jack0   19
3  Terry0   18

Cập nhật

suf = lambda n: "%d%s"%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th"))
g=df.groupby('Name')


df.Name=df.Name.radd(g.cumcount().add(1).map(suf).mask(g.Name.transform('count')==1,''))
df
     Name  Age
0  1stTom   20
1  2ndTom   21
2    Jack   19
3   Terry   18

Cập nhật 2 cho cột

suf = lambda n: "%d%s"%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th"))

g=s.groupby([s.index.get_level_values(0),s])
s=s.radd(g.cumcount().add(1).map(suf).mask(g.transform('count')==1,''))
s=s.unstack()
     Name OtherName
0  1stTom    2ndTom
1   Jerry      John
2    Jack       Bob
3   Terry     Steve

1
OP cầnI don't want to add a 1 on the end of the value
jezrael

Điều này là rất tốt, cảm ơn bạn. Bây giờ nếu có một cột tên thứ hai và thay vì kiểm tra các giá trị theo chiều dọc thì nó sẽ kiểm tra cùng tên theo chiều ngang?
Logan0015

1
@ Logan0015L bạn có thể làm df.groupby (['Name1', 'Name2']). Cumcount ()
YOBEN_S

@jezrael Theo cách hiểu của tôi, nếu chúng ta không thể xây dựng chuỗi thứ 1 thành .... nth, tôi nghĩ tốt hơn là giữ số trong tên
YOBEN_S

Điều này có thể được nhóm theo hàng thay vì cột?
Logan0015

7

EDIT: Đối với số lượng nhân đôi trên mỗi hàng sử dụng:

df = pd.DataFrame(data = {'Name':['Tom', 'Jerry', 'Jack', 'Terry'], 
                          'OtherName':['Tom', 'John', 'Bob','Steve'],
                          'Age':[20, 21, 19, 18]})

print (df)
    Name OtherName  Age
0    Tom       Tom   20
1  Jerry      John   21
2   Jack       Bob   19
3  Terry     Steve   18

import inflect
p = inflect.engine()

#map by function for dynamic counter
f = lambda i: p.number_to_words(p.ordinal(i))
#columns filled by names
cols = ['Name','OtherName']
#reshaped to MultiIndex Series
s = df[cols].stack()
#counter per groups
count = s.groupby([s.index.get_level_values(0),s]).cumcount().add(1)
#mask for filter duplicates
mask = s.reset_index().duplicated(['level_0',0], keep=False).values
#filter only duplicates and map, reshape back and add to original data
df[cols] = count[mask].map(f).unstack().add(df[cols], fill_value='')
print (df)
       Name  OtherName  Age
0  firstTom  secondTom   20
1     Jerry       John   21
2      Jack        Bob   19
3     Terry      Steve   18

Sử dụng GroupBy.cumcountvới Series.map, nhưng chỉ cho các giá trị trùng lặp bởi Series.duplicated:

data = {'Name':['Tom', 'Tom', 'Jack', 'Terry'], 'Age':[20, 21, 19, 18]} 
df = pd.DataFrame(data)

nth = {
0: "First",
1: "Second",
2: "Third",
3: "Fourth"
}

mask = df.Name.duplicated(keep=False)
df.loc[mask, 'Name'] = df[mask].groupby('Name').cumcount().map(nth) + df.loc[mask, 'Name']
print (df)
        Name  Age
0   FirstTom   20
1  SecondTom   21
2       Jack   19
3      Terry   18

Từ điển động nên giống như:

import inflect
p = inflect.engine()

mask = df.Name.duplicated(keep=False)
f = lambda i: p.number_to_words(p.ordinal(i))
df.loc[mask, 'Name'] = df[mask].groupby('Name').cumcount().add(1).map(f) + df.loc[mask, 'Name']
print (df)

        Name  Age
0   firstTom   20
1  secondTom   21
2       Jack   19
3      Terry   18

Đây là cách sử dụng rất trơn tru của bản đồ và cumcount, một cái tốt đẹp. có thể thêm một bước để hiển thị số lượng tích lũy có thể và xây dựng một từ điển động?
Datanovice

5

transform

nth = ['First', 'Second', 'Third', 'Fourth']

def prefix(d):
    n = len(d)
    if n > 1:
        return d.radd([nth[i] for i in range(n)])
    else:
        return d

df.assign(Name=df.groupby('Name').Name.transform(prefix))

          Name  Age
0     FirstTom   20
1    SecondTom   21
2         Jack   19
3        Terry   18
4   FirstSteve   17
5  SecondSteve   16
6   ThirdSteve   15
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.