在忽略NaN的情况下串联熊猫DataFrame中的列值

时间:2019-01-31 20:08:20

标签: python string pandas dataframe nan

我有一个下面的熊猫表

df:

 EVNT_ID col1 col2 col3 col4
 123454   1    Nan   4    5
 628392   Nan   3   Nan   7
 293899   2    Nan  Nan   6
 127820   9    11    12   19

现在,我试图合并第一列以外的所有列,并希望我的数据框以以下方式显示

new_df:

 EVNT_ID col1 col2 col3 col4 new_col
 123454   1    Nan   4    5   1|4|5
 628392   Nan   3   Nan   7    3|7
 293899   2    Nan  Nan   6    2|6
 127820   9    11    12   19  9|11|12|19

我正在使用以下代码

df['new_column'] = df[~df.EVNT_ID].apply(lambda x: '|'.join(x.dropna().astype(str).values), axis=1)

但是它给了我以下错误

ufunc 'invert' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

如果有人能给我我错的地方,我将不胜感激。我真的很感激。

3 个答案:

答案 0 :(得分:1)

您可以使用intfilter进行此操作:

agg

或者,

df.filter(like='col').agg(
    lambda x: x.dropna().astype(int).astype(str).str.cat(sep='|'), axis=1)

0         1|4|5
1           3|7
2           2|6
3    9|11|12|19
dtype: object

如果性能很重要,你可以使用列表理解:

df.drop('EVNT_ID', 1).agg(
        lambda x: x.dropna().astype(int).astype(str).str.cat(sep='|'), axis=1)

0         1|4|5
1           3|7
2           2|6
3    9|11|12|19
dtype: object

如果您可以原谅分配给DataFrame的开销,请参考以下两种最快解决方案的时间安排。

joined = [
    '|'.join([str(int(x)) for x in r if pd.notna(x)]) 
    for r in df.iloc[:,1:].values.tolist()
]
joined
# ['1|4|5', '3|7', '2|6', '9|11|12|19']

df.assign(new_col=joined)   

   EVNT_ID  col1  col2  col3  col4     new_col
0   123454   1.0   NaN   4.0     5       1|4|5
1   628392   NaN   3.0   NaN     7         3|7
2   293899   2.0   NaN   NaN     6         2|6
3   127820   9.0  11.0  12.0    19  9|11|12|19

尽管请注意,答案并不相同,因为@RafaelC的代码会产生浮点数:df = pd.concat([df] * 1000, ignore_index=True) # In this post. %%timeit [ '|'.join([str(int(x)) for x in r if pd.notna(x)]) for r in df.iloc[:,1:].values.tolist() ] # RafaelC's answer. %%timeit [ '|'.join([k for k in a if k]) for a in zip(*df.fillna('').astype(str).iloc[:, 1:].values.tolist()) ] 31.9 ms ± 800 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 23.7 ms ± 409 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 。如果这是好的,那也不错。否则,你就需要转换为INT它增加了更多的开销。

答案 1 :(得分:1)

使用列表理解和zip

>>> [['|'.join([k for k in a if k])] for a in zip(*df.fillna('').astype(str).iloc[:, 1:].values)]

时间似乎还可以

df = pd.concat([df]*1000)

%timeit [['|'.join([k for k in a if k])] for a in zip(*df.fillna('').astype(str).iloc[:, 1:].values)]
10.8 ms ± 568 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.filter(like='col').agg(lambda x: x.dropna().astype(int).astype(str).str.cat(sep='|'), axis=1)
1.68 s ± 91.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit df.iloc[:, 1:].apply(lambda x: '|'.join(str(el) for el in x if str(el) != 'nan'), axis=1)
87.8 ms ± 5.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit df.assign(new_col=['|'.join([str(int(x)) for x in r if ~np.isnan(x)]) for r in df.iloc[:,1:].values])
45.1 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

答案 2 :(得分:1)

尝试以下代码:

df['new_col'] = df.iloc[:, 1:].apply(lambda x:
    '|'.join(str(el) for el in x if str(el) != 'nan'), axis=1)

最初,我想到的是x.dropna()而不是x if str(el) != 'nan', 但是%timeit显示dropna()的运行速度要慢得多。