如何在Pandas DataFrame列中查找连续的值并替换该值

3

我有一个以下的数据框:

Col1    Col2    Col3
A       ABC     100
B       BCD     200
C       CDA     300
D       CDA     400
E       CDA     500
F       EFG     600
G       XYZ     700
H       XYZ     800
I       PQR     900

我们可以看到在Col2中,CDA重复了3次,而XYZ重复了2次。
现在我需要创建新列 NewCol4 并检查连续值,如果存在连续值,就在最后加下划线并加上数字,如果不是连续值,则保持原样。
我需要以下格式的输出。
Col1    Col2    Col3   NewCol4
A       ABC     100    ABC
B       BCD     200    BCD
C       CDA     300    CDA_1
D       CDA     400    CDA_2
E       CDA     500    CDA_3
F       EFG     600    EFG
G       XYZ     700    XYZ_1
H       XYZ     800    XYZ_2
I       PQR     900    PQR

我对Pandas非常陌生,有没有办法实现上述输出,这将非常有帮助。

5个回答

2
这里是另一种“一行代码”的版本:
# Create NewCol4 by adding increments to col2 for those groups that have more than one element
df2 = (df.assign(NewCol4 = df['Col2'] + '_' + (df.groupby('Col2').cumcount()+1).astype(str))
         .groupby('Col2').filter(lambda g:len(g)>1)
)

# Join to the original. This will leave NaNs in NewCol4 for single-element groups
df3 = df.join(df2['NewCol4'])

# Fill NaNa from Col2
df3['NewCol4'] = df3['NewCol4'].fillna(df['Col2'])

df3现在看起来是这样的

    Col1    Col2      Col3  NewCol4
--  ------  ------  ------  ---------
 0  A       ABC        100  ABC
 1  B       BCD        200  BCD
 2  C       CDA        300  CDA_1
 3  D       CDA        400  CDA_2
 4  E       CDA        500  CDA_3
 5  F       EFG        600  EFG
 6  G       XYZ        700  XYZ_1
 7  H       XYZ        800  XYZ_2
 8  I       PQR        900  PQR

1
一些“一行代码”放在一起可以解决问题,但这种方法可以进行优化:
# Assign a number to each consecutive group of equal "Col2" values
df["g_rank"] = (df["Col2"] != df["Col2"].shift()).cumsum()

# Add a column with the size of each group
df = df.join(df.groupby("g_rank")["Col2"].size().rename("size"), on="g_rank")

# Now add a different number to each member of a group
df["l_rank"] = df.groupby("g_rank")["size"].rank("first")

# Finally, calculate all the "NewCol4" and remove the unnecesary ones
df["NewCol4"] = df["Col2"] + "_" + df["l_rank"].astype(int).astype(str)
df.loc[df["size"] <= 1, "NewCol4"] = None

我故意留下了一些列,以便更容易理解每个步骤。这应该是输出结果:
  Col1 Col2  Col3  g_rank  size  l_rank NewCol4
0    A  ABC   100       1     1     1.0    None
1    B  BCD   200       2     1     1.0    None
2    C  CDA   300       3     3     1.0   CDA_1
3    D  CDA   400       3     3     2.0   CDA_2
4    E  CDA   500       3     3     3.0   CDA_3
5    F  EFG   600       4     1     1.0    None
6    G  XYZ   700       5     2     1.0   XYZ_1
7    H  XYZ   800       5     2     2.0   XYZ_2
8    I  PQR   900       6     1     1.0    None

0
使用groupby和df.loc创建一个NewCol4。 groupby结果可以使用apply函数返回数据框索引层次结构和分组结果集的列表值。
data="""Col1    Col2    Col3   NewCol4
A       ABC     100    ABC
B       BCD     200    BCD
C       CDA     300    CDA_1
D       CDA     400    CDA_2
E       CDA     500    CDA_3
F       EFG     600    EFG
G       XYZ     700    XYZ_1
H       XYZ     800    XYZ_2
I       PQR     900    PQR"""

df = pd.read_csv(StringIO(data), sep="\s+")

grouped=df.groupby('Col2')['Col3']

index=[]
values=[]
def count_consecutive(df):
    index.append(df.index)
    values.append(df.values)

grouped.apply(count_consecutive)        
#[print(x) for x in index]
#[print(x) for x in values]

for x in index:
    count=0
    old_value=0
    for i in x:
        field=df.loc[i,'Col2']
        value=df.loc[i,'Col3']
        #print(value)
        if value>old_value:
            count+=1
        df.loc[i,'NewCol4']=field+"_"+str(count)
        old_value=value
    
print(df)

输出:

Col1 Col2  Col3 NewCol4
0    A  ABC   100   ABC_1
1    B  BCD   200   BCD_1
2    C  CDA   300   CDA_1
3    D  CDA   400   CDA_2
4    E  CDA   500   CDA_3
5    F  EFG   600   EFG_1
6    G  XYZ   700   XYZ_1
7    H  XYZ   800   XYZ_2
8    I  PQR   900   PQR_1

0

我相信我的答案不是最好的方法,也应该有一些单行的方法,但你可以使用:

checkNumber = [0]
checkValue = [df["Col2"].values[0]]
def toApply(value):
  if sum(df["Col2"] == value) > 1:
    if checkValue[0] == value:
      checkNumber[0] += 1
      return value + "_" + str(checkNumber[0])
    else:
      checkNumber[0] = 0
      checkValue[0] = value
      print(value)
      return toApply(value)
  else:
    return value
df["NewCol4"] = df["Col2"].apply(toApply)
df

输出

列1 列2 列3 新列4
0 A ABC 100 ABC
1 B BCD 200 BCD
2 C CDA 300 CDA_1
3 D CDA 400 CDA_2
4 E CDA 500 CDA_3
5 F EFG 600 EFG
6 G XYZ 700 XYZ_1
7 H XYZ 800 XYZ_2
8 I PQR 900 PQR

0

或许这个能帮到你

# get repeating values
counts = df['Col2'].value_counts()
multi = counts.index[counts>1]
# Get a counter for each Col2 value
new = df.groupby('Col2').apply(pd.DataFrame.reset_index)
new = new.reset_index(1)
new['level_1'] += 1
# Build a NewCol4 with a suffix for all
new['NewCol4'] = new['Col2'] + "_" + new['level_1'].astype(str)
# Undo those not needed
stay = ~new['Col2'].isin(multi)
new.loc[stay, "NewCol4"] = new.loc[stay, "Col2"]
# remove unneeded columns
new.set_index('index', drop=True, inplace=True)
new.pop('level_1')
new

这还没有被优化,但可以作为一个起点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接