Скопируйте DataFrame со значениями NaN в столбце - PullRequest
2 голосов
/ 09 мая 2019

У меня есть 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. В этом случае, однако, это кажется довольно бессмысленным. Этот код, тем не менее, приводит к следующему кадру данных (именно так я и хотел бы выводить):

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

Есть ли способ выполнить эту операцию с групповым или есть другой способ сделать это быстрее?

Спасибо!

Ответы [ 4 ]

2 голосов
/ 09 мая 2019

Из того, что я понимаю, если даты отсортированы и каждая дата имеет одинаковую длину:

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 голос
/ 09 мая 2019

Используйте 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 и создание MultiIndex по DataFrame.set_index:

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
0 голосов
/ 31 мая 2019

Один из способов ускорить этот код на значительную величину - сначала добавить промежуточные кадры данных в список и объединить список кадров данных в один последний шаг, используя pd.concat().

Это заставит код выглядеть следующим образом:

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()

Это простое решение заставило меня значительно сократить время выполнения.Надеюсь, это поможет кому-то еще.

0 голосов
/ 09 мая 2019

Попробуйте эту однострочную concat, replace, нарезку и 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...