使用列中的NaN值复制DataFrame

时间:2019-05-09 09:08:57

标签: python pandas dataframe

我有一个类似于以下示例的DataFrame。

# define DataFrame for reproducability

df = pd.DataFrame({'date': ['2019-05-06', '2019-05-07', '2019-05-07', '2019-05-09', '2019-05-10', '2019-05-11'],
                   'Identifier': [1, 1, 1, 1, 1, 1],
                   'B': [2.4, 3.9, 3.9, 4.3, 2.5, 3.14],
                   'C': [0.214, 0.985, 0.985, 0.839, 0.555, 0.159],
                   'Name': [np.nan, "CD", "AD", np.nan, np.nan, np.nan]})

print(df)

    date        Identifier  B       C       Name
0   2019-05-06  1           2.40    0.214   NaN
1   2019-05-07  1           3.90    0.985   CD
2   2019-05-07  1           3.90    0.985   AD
3   2019-05-09  1           4.30    0.839   NaN
4   2019-05-10  1           2.50    0.555   NaN
5   2019-05-11  1           3.14    0.159   NaN

可以看到的是,对于给定的标识符,可以有多个。但是,该名称仅在单个日期一次附加到DataFrame。我需要的是基本上在每个日期向前向后填写姓名。目前,我有一个可行的解决方案,但是对于我正在处理的整个数据帧而言,这是非常慢的。代码如下所示

final_df = pd.DataFrame()

for i in df.Identifier.unique():
    # select the current identifier
    identifier_df = df.loc[df.Identifier == i]
    # allow a given identifier to have different names
    for n in df.Name.unique():
        if pd.isna(n):
            continue
        else:
            intermediate = identifier_df.copy()
            intermediate.loc[:,"Name"] = np.repeat(n, len(intermediate))
            final_df = final_df.append(intermediate)

final_df = final_df.drop_duplicates()

请注意,完整的DataFrame需要通过标识符循环。但是,在这种情况下,这似乎毫无意义。但是,此代码会导致以下DataFrame(这是我希望输出的样子):

print(final_df)

    date        Identifier  B       C       Name
0   2019-05-06  1           2.40    0.214   CD
1   2019-05-07  1           3.90    0.985   CD
3   2019-05-09  1           4.30    0.839   CD
4   2019-05-10  1           2.50    0.555   CD
5   2019-05-11  1           3.14    0.159   CD
0   2019-05-06  1           2.40    0.214   AD
1   2019-05-07  1           3.90    0.985   AD
3   2019-05-09  1           4.30    0.839   AD
4   2019-05-10  1           2.50    0.555   AD
5   2019-05-11  1           3.14    0.159   AD

是否可以通过groupby来执行此操作,或者是否可以通过其他方式使其更快?

谢谢!

4 个答案:

答案 0 :(得分:2)

据我了解,如果对日期进行排序并且每个日期都具有相同的长度:

from itertools import islice,cycle
m=df.name.isna() #pull where name is NaN
l=df.loc[~m,'name'].tolist() #create a list for not null names
df.loc[m,'name']=list(islice(cycle(l),len(df[m]))) #repeat the list for all dates and assign to NaN
print(df)

         date  identifier    B      C name
0  2019-05-07           1  2.4  0.214   AB
1  2019-05-07           1  2.4  0.214   CD
2  2019-05-08           1  3.9  0.985   AB
3  2019-05-08           1  3.9  0.985   CD
4  2019-05-09           1  2.5  0.555   AB
5  2019-05-09           1  2.5  0.555   CD

答案 1 :(得分:1)

itertools.product用于所有3列的所有组合:

from  itertools import product

df1 = pd.DataFrame(list(product(df['date'].unique(), 
                                df['Identifier'].unique(),
                                df['Name'].dropna().unique())), 
                   columns=['date','Identifier','Name'])
print (df1)
         date  Identifier Name
0  2019-05-06           1   CD
1  2019-05-06           1   AD
2  2019-05-07           1   CD
3  2019-05-07           1   AD
4  2019-05-09           1   CD
5  2019-05-09           1   AD
6  2019-05-10           1   CD
7  2019-05-10           1   AD
8  2019-05-11           1   CD
9  2019-05-11           1   AD

通过DataFrame.merge左加入并通过DataFrame.set_index创建MultiIndex

df2 = df1.merge(df, how='left').set_index(['date','Identifier'])

使用DataFrame.drop_duplicates可能用DataFrame.combine_first替换丢失的值:

df3 = df.drop_duplicates(['date','Identifier']).set_index(['date','Identifier'])
print (df3)
                          B      C Name
date       Identifier                  
2019-05-06 1           2.40  0.214  NaN
2019-05-07 1           3.90  0.985   CD
2019-05-09 1           4.30  0.839  NaN
2019-05-10 1           2.50  0.555  NaN
2019-05-11 1           3.14  0.159  NaN

df4 = df2.combine_first(df3).reset_index()
print (df4)
         date  Identifier     B      C Name
0  2019-05-06           1  2.40  0.214   CD
1  2019-05-06           1  2.40  0.214   AD
2  2019-05-07           1  3.90  0.985   CD
3  2019-05-07           1  3.90  0.985   AD
4  2019-05-09           1  4.30  0.839   CD
5  2019-05-09           1  4.30  0.839   AD
6  2019-05-10           1  2.50  0.555   CD
7  2019-05-10           1  2.50  0.555   AD
8  2019-05-11           1  3.14  0.159   CD
9  2019-05-11           1  3.14  0.159   AD

答案 2 :(得分:0)

尝试以下这种单线concatreplace,切片和ffill

print(pd.concat([df[::2],df[::2].replace('AB','CD')]).ffill())

输出:

         date  identifier    B      C name
0  2019-05-07           1  2.4  0.214   AB
2  2019-05-08           1  3.9  0.985   AB
4  2019-05-09           1  2.5  0.555   AB
0  2019-05-07           1  2.4  0.214   CD
2  2019-05-08           1  3.9  0.985   CD
4  2019-05-09           1  2.5  0.555   CD

答案 3 :(得分:0)

一种大幅提高此代码速度的方法是,首先将中间DataFrame追加到列表中,并在最后一步使用pd.concat()连接DataFrame列表。

这将使代码如下所示:

final_df = []

for i in df.Identifier.unique():
    # select the current identifier
    identifier_df = df.loc[df.Identifier == i]
    # allow a given identifier to have different names
    for n in df.Name.unique():
        if pd.isna(n):
            continue
        else:
            intermediate = identifier_df.copy()
            intermediate.loc[:,"Name"] = np.repeat(n, len(intermediate))
            final_df.append(intermediate)


final_df = pd.concat(final_df).drop_duplicates()

这个简单的解决方案使我大大减少了执行时间。希望它也可以帮助其他人。