Сегментация и удаление фреймов данных

#python #pandas #dataframe #iteration

Вопрос:

У меня есть следующий фрейм данных в панд:

 A = [1,10,23,45,24,24,55,67,73,26,13,96,53,23,24,43,90], 
B = [24,23,29, BW,49,59,72, BW,9,183,17, txt,2,49,BW,479,BW]
 

Я хочу создать новый столбец, и в этом столбце я хочу иметь значения из столбца A на основе условия в столбце B. Условия таковы, что если между двумя последовательными «BW» нет «txt», то у меня будут значения в столбце C. Но если между двумя последовательными «BW» есть «txt», я хочу отбросить все эти значения. Таким образом, ожидаемый результат должен выглядеть следующим образом:

 A = [1,10,23,45,24,24,55,67,73,26,13,96,53,23,24,43,90], 
B = [24,23,29, BW,49,59,72, BW,9,183,17, txt,2,49,BW,479,BW]
C = [1,10,23, BW, 24,24,55, BW, nan, nan, nan, nan, nan, nan, BW, 43,BW]
 

Я понятия не имею, как это сделать. Мы очень ценим любую помощь.

Ответ №1:

Редактировать:

Обновленный ответ, в котором отсутствовали значения BW в окончательном df.

 import pandas as pd
import numpy as np

BW = 999
txt = -999
A = [1,10,23,45,24,24,55,67,73,26,13,96,53,23,24,43,90]
B = [24,23,29, BW,49,59,72, BW,9,183,17, txt,2,49,BW,479,BW]

df = pd.DataFrame({'A': A, 'B': B})
df = df.assign(group = (df[~df['B'].between(BW,BW)].index.to_series().diff() > 1).cumsum())
df['C'] = np.where(df.group == df[df.B == txt].group.values[0], np.nan, df.A)
df['C'] = np.where(df['B'] == BW, df['B'], df['C'])
df['C'] = df['C'].astype('Int64')
df = df.drop('group', axis=1)
In [435]: df
Out[435]: 
     A    B     C
0    1   24     1
1   10   23    10
2   23   29    23
3   45  999   999 <-- BW
4   24   49    24
5   24   59    24
6   55   72    55
7   67  999   999 <-- BW
8   73    9  <NA>
9   26  183  <NA>
10  13   17  <NA>
11  96 -999  <NA> <-- txt is in the middle of BW
12  53    2  <NA>
13  23   49  <NA>
14  24  999   999 <-- BW
15  43  479    43
16  90  999   999 <-- BW
 

Вы можете добиться этого таким образом, предполагая BW , что и txt являются конкретными значениями, я просто заполнил их некоторым случайным числом, чтобы отличить их

 In [277]: BW = 999

In [278]: txt = -999

In [293]: A = [1,10,23,45,24,24,55,67,73,26,13,96,53,23,24,43,90]
     ...: B = [24,23,29, BW,49,59,72, BW,9,183,17, txt,49,BW,479,BW]

In [300]: df = pd.DataFrame({'A': A, 'B': B})

In [301]: df
Out[301]: 
     A    B
0    1   24
1   10   23
2   23   29
3   45  999
4   24   49
5   24   59
6   55   72
7   67  999
8   73    9
9   26  183
10  13   17
11  96 -999
12  53    2
13  23   49
14  24  999
15  43  479
16  90  999
 

Сначала давайте разделим различные группы значений, здесь я разделяю их на уникальные группы, где каждая группа содержит значения B , которые находятся между значением BW и следующим BW .

 In [321]: df = df.assign(group = (df[~df['B'].between(BW,BW)].index.to_series().diff() > 1).cumsum())

In [322]: df
Out[322]: 
     A    B      group
0    1   24 0.00000000
1   10   23 0.00000000
2   23   29 0.00000000
3   45  999        NaN
4   24   49 1.00000000
5   24   59 1.00000000
6   55   72 1.00000000
7   67  999        NaN
8   73    9 2.00000000
9   26  183 2.00000000
10  13   17 2.00000000
11  96 -999 2.00000000
12  53    2 2.00000000
13  23   49 2.00000000
14  24  999        NaN
15  43  479 3.00000000
16  90  999        NaN
 

Далее с помощью np.where() мы можем заменить значения в зависимости от заданного вами условия.

 In [360]: df['C'] = np.where(df.group == df[df.B == txt].group.values[0], np.nan, df.B)

In [432]: df
Out[432]: 
     A    B      group            C
0    1   24 0.00000000  24.00000000
1   10   23 0.00000000  23.00000000
2   23   29 0.00000000  29.00000000
3   45  999        NaN 999.00000000
4   24   49 1.00000000  49.00000000
5   24   59 1.00000000  59.00000000
6   55   72 1.00000000  72.00000000
7   67  999        NaN 999.00000000
8   73    9 2.00000000          NaN
9   26  183 2.00000000          NaN
10  13   17 2.00000000          NaN
11  96 -999 2.00000000          NaN
12  53    2 2.00000000          NaN
13  23   49 2.00000000          NaN
14  24  999        NaN 999.00000000
15  43  479 3.00000000 479.00000000
16  90  999        NaN 999.00000000
 

Здесь нам нужно установить, где B равно BW для C обратно к значениям B .

 In [488]: df['C'] = np.where(df['B'] == BW, df['B'], df['C'])

In [489]: df
Out[489]: 
     A    B      group            C
0    1   24 0.00000000  24.00000000
1   10   23 0.00000000  23.00000000
2   23   29 0.00000000  29.00000000
3   45  999        NaN 999.00000000
4   24   49 1.00000000  49.00000000
5   24   59 1.00000000  59.00000000
6   55   72 1.00000000  72.00000000
7   67  999        NaN 999.00000000
8   73    9 2.00000000          NaN
9   26  183 2.00000000          NaN
10  13   17 2.00000000          NaN
11  96 -999 2.00000000          NaN
12  53    2 2.00000000          NaN
13  23   49 2.00000000          NaN
14  24  999        NaN 999.00000000
15  43  479 3.00000000 479.00000000
16  90  999        NaN 999.00000000
 

Наконец, просто преобразуйте столбец с плавающей точкой в int и удалите group столбец, который нам больше не нужен. Если вы хотите сохранить значения NaN np.nan , то игнорируйте преобразование в Int64 .

 In [396]: df.C = df.C.astype('Int64')

In [397]: df
Out[397]: 
     A    B      group     C
0    1   24 0.00000000    24
1   10   23 0.00000000    23
2   23   29 0.00000000    29
3   45  999        NaN   999
4   24   49 1.00000000    49
5   24   59 1.00000000    59
6   55   72 1.00000000    72
7   67  999        NaN   999
8   73    9 2.00000000  <NA>
9   26  183 2.00000000  <NA>
10  13   17 2.00000000  <NA>
11  96 -999 2.00000000  <NA>
12  53    2 2.00000000  <NA>
13  23   49 2.00000000  <NA>
14  24  999        NaN   999
15  43  479 3.00000000   479
16  90  999        NaN   999

In [398]: df = df.drop('group', axis=1)

In [435]: df
Out[435]: 
     A    B     C
0    1   24    24
1   10   23    23
2   23   29    29
3   45  999   999
4   24   49    49
5   24   59    59
6   55   72    72
7   67  999   999
8   73    9  <NA>
9   26  183  <NA>
10  13   17  <NA>
11  96 -999  <NA>
12  53    2  <NA>
13  23   49  <NA>
14  24  999   999
15  43  479   479
16  90  999   999
 

Ответ №2:

Я не знаю, является ли это наиболее эффективным способом сделать это , но вы можете создать новый столбец, вызванный mask из сопоставления значений в столбце B следующим образом: 'BW' to True , 'txt' to False и все остальные значения np.nan .

Затем , если вы переадресуете заполнение NaN из mask и обратно, заполните NaN из mask и логически объедините результаты (установите значение True, если один из столбцов с прямым или обратным заполнением равен False), вы можете создать столбец, называемый final_mask , в котором все значения между последовательными BW, содержащими txt, заполняются True.

Затем вы можете использовать .apply для выбора значения столбца A только в том случае, если значение final_mask равно False, а столбец B не является «BW», выберите столбец B, если final_mask значение равно False, а столбец B является «BW», и np.nan в противном случае.

 import numpy as np
import pandas as pd

A = [1,10,23,45,24,24,55,67,73,26,13,96,53,23,24,43,90]
B = [24,23,29, 'BW',49,59,72, 'BW',9,183,17, 'txt',2,49,'BW',479,'BW']
df = pd.DataFrame({'A':A,'B':B})

df["mask"] = df["B"].apply(lambda x: True if x == 'BW' else False if x == 'txt' else np.nan)
df["ffill"] = df["mask"].fillna(method="ffill")
df["bfill"] = df["mask"].fillna(method="bfill")
df["final_mask"] = (df["ffill"] == False) | (df["bfill"] == False)

df["C"] = df.apply(lambda x: x['A'] if (
    (x['final_mask'] == False) amp; (x['B'] != 'BW')) 
    else x['B'] if ((x['final_mask'] == False) amp; (x['B'] == 'BW')) 
    else np.nan, axis=1
)
 

 >>> df
     A    B   mask  ffill  bfill  final_mask    C
0    1   24    NaN    NaN   True       False    1
1   10   23    NaN    NaN   True       False   10
2   23   29    NaN    NaN   True       False   23
3   45   BW   True   True   True       False   BW
4   24   49    NaN   True   True       False   24
5   24   59    NaN   True   True       False   24
6   55   72    NaN   True   True       False   55
7   67   BW   True   True   True       False   BW
8   73    9    NaN   True  False        True  NaN
9   26  183    NaN   True  False        True  NaN
10  13   17    NaN   True  False        True  NaN
11  96  txt  False  False  False        True  NaN
12  53    2    NaN  False   True        True  NaN
13  23   49    NaN  False   True        True  NaN
14  24   BW   True   True   True       False   BW
15  43  479    NaN   True   True       False   43
16  90   BW   True   True   True       False   BW
 

Удаление столбцов, которые мы создали по пути:

 df.drop(columns=['mask','ffill','bfill','final_mask'])

     A    B    C
0    1   24    1
1   10   23   10
2   23   29   23
3   45   BW   BW
4   24   49   24
5   24   59   24
6   55   72   55
7   67   BW   BW
8   73    9  NaN
9   26  183  NaN
10  13   17  NaN
11  96  txt  NaN
12  53    2  NaN
13  23   49  NaN
14  24   BW   BW
15  43  479   43
16  90   BW   BW