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)

このようなデータフレームがあるとしましょう。Name列の値「Tom」を確認する方法を見つけようとしています。最初に見つかった場合は、値「FirstTom」に置き換え、2回目に表示された場合は、値「SecondTom」に置き換えます。 。これをどのように達成しますか?以前、replaceメソッドを使用しましたが、すべてのTomを単一の値に置き換えるためにのみ使用しました。値の最後に1を追加したくありませんが、文字列を完全に別のものに変更します。

編集:

dfが以下のようになっている場合、最初の列と2番目の列でトムを確認し、最初のインスタンスをFirstTomに、2番目のインスタンスをSecondTomに置き換えるにはどうすればよいでしょうか。

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

回答:


9

既存のソリューションに追加するだけで、inflect動的辞書の作成に使用できます

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

我々はできる 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

更新

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

列の更新2

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の必要性I don't want to add a 1 on the end of the value
jezrael

ありがとうございます。次に、名前の2番目の列があり、値を垂直方向にチェックする代わりに、同じ名前を水平方向にチェックする場合はどうでしょうか。
Logan0015

1
@ Logan0015L df.groupby(['Name1'、 'Name2'])。cumcount()を実行できます
YOBEN_S

@jezrael私の理解では、文字列1st to .... nthを構築できない場合は、名前に数値を保持する方がよいと思います
YOBEN_S

これを列ではなく行でグループ化できますか?
Logan0015

7

編集:行ごとに重複するカウントを使用するには:

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

GroupBy.cumcountと一緒Series.mapに使用しますが、以下による重複値にのみ使用します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

動的辞書は次のようになります。

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

これは非常に洗練されたマップとcumcountの使い方です。可能性のある累積カウントの数を表示し、動的に辞書を構築するためのステップを追加できますか?
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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.