在其他列为 NaN 的地方填写相同数量的字符

Fill in same amount of characters where other column is NaN

我有以下虚拟数据框:

df = pd.DataFrame({'Col1':['a,b,c,d', 'e,f,g,h', 'i,j,k,l,m'],
                   'Col2':['aa~bb~cc~dd', np.NaN, 'ii~jj~kk~ll~mm']})

        Col1            Col2
0    a,b,c,d     aa~bb~cc~dd
1    e,f,g,h             NaN
2  i,j,k,l,m  ii~jj~kk~ll~mm

真实数据集的形状为 500000, 90

我需要将这些值取消嵌套到行中,为此我使用了新的 explode 方法,效果很好。

问题是NaN,这会导致explode之后的长度不等,所以我需要填写与填充值相同数量的分隔符。在这种情况下 ~~~ 因为第 1 行有三个逗号。


预期输出

        Col1            Col2
0    a,b,c,d     aa~bb~cc~dd
1    e,f,g,h             ~~~
2  i,j,k,l,m  ii~jj~kk~ll~mm

尝试 1:

df['Col2'].fillna(df['Col1'].str.count(',')*'~')

尝试 2:

np.where(df['Col2'].isna(), df['Col1'].str.count(',')*'~', df['Col2'])

这个可行,但我觉得有更简单的方法:

characters = df['Col1'].str.replace('\w', '').str.replace(',', '~')
df['Col2'] = df['Col2'].fillna(characters)

print(df)

        Col1            Col2
0    a,b,c,d     aa~bb~cc~dd
1    e,f,g,h             ~~~
2  i,j,k,l,m  ii~jj~kk~ll~mm

d1 = df.assign(Col1=df['Col1'].str.split(',')).explode('Col1')[['Col1']]
d2 = df.assign(Col2=df['Col2'].str.split('~')).explode('Col2')[['Col2']]

final = pd.concat([d1,d2], axis=1)
print(final)

  Col1 Col2
0    a   aa
0    b   bb
0    c   cc
0    d   dd
1    e     
1    f     
1    g     
1    h     
2    i   ii
2    j   jj
2    k   kk
2    l   ll
2    m   mm

问题:有没有更简单更通用的方法?还是我的方法没问题。

一种方法是使用 str.repeatfillna() 不确定这有多有效:

df.Col2.fillna(pd.Series(['~']*len(df)).str.repeat(df.Col1.str.count(',')))

0       aa~bb~cc~dd
1               ~~~
2    ii~jj~kk~ll~mm
Name: Col2, dtype: object

zip_longest 在这里很有用,因为您不需要原始索引。无论哪个列有更多拆分,它都会起作用:

from itertools import zip_longest, chain

df = pd.DataFrame({'Col1':['a,b,c,d', 'e,f,g,h', 'i,j,k,l,m', 'x,y'],
                   'Col2':['aa~bb~cc~dd', np.NaN, 'ii~jj~kk~ll~mm', 'xx~yy~zz']})
#        Col1            Col2
#0    a,b,c,d     aa~bb~cc~dd
#1    e,f,g,h             NaN
#2  i,j,k,l,m  ii~jj~kk~ll~mm
#3        x,y        xx~yy~zz

l = [zip_longest(*x, fillvalue='') 
     for x in zip(df.Col1.str.split(',').fillna(''), 
                  df.Col2.str.split('~').fillna(''))]

pd.DataFrame(chain.from_iterable(l))

    0   1
0   a  aa
1   b  bb
2   c  cc
3   d  dd
4   e    
5   f    
6   g    
7   h    
8   i  ii
9   j  jj
10  k  kk
11  l  ll
12  m  mm
13  x  xx
14  y  yy
15     zz

只需将数据帧拆分为两个

df1=df.dropna()
df2=df.drop(df1.index)

d1 = df1['Col1'].str.split(',').explode()
d2 = df1['Col2'].str.split('~').explode()
d3 = df2['Col1'].str.split(',').explode()

final = pd.concat([d1, d2], axis=1).append(d3.to_frame(),sort=False)
Out[77]: 
  Col1 Col2
0    a   aa
0    b   bb
0    c   cc
0    d   dd
2    i   ii
2    j   jj
2    k   kk
2    l   ll
2    m   mm
1    e  NaN
1    f  NaN
1    g  NaN
1    h  NaN

pd.concat

delims = {'Col1': ',', 'Col2': '~'}
pd.concat({
    k: df[k].str.split(delims[k], expand=True)
    for k in df}, axis=1
).stack()

    Col1 Col2
0 0    a   aa
  1    b   bb
  2    c   cc
  3    d   dd
1 0    e  NaN
  1    f  NaN
  2    g  NaN
  3    h  NaN
2 0    i   ii
  1    j   jj
  2    k   kk
  3    l   ll
  4    m   mm

这在 df 中的列上循环。循环 delims 字典中的键可能更明智。

delims = {'Col1': ',', 'Col2': '~'}
pd.concat({
    k: df[k].str.split(delims[k], expand=True)
    for k in delims}, axis=1
).stack()

一样的东西,不一样的样子

delims = {'Col1': ',', 'Col2': '~'}
def f(c): return df[c].str.split(delims[c], expand=True)
pd.concat(map(f, delims), keys=delims, axis=1).stack()