Условная генерация новой колонки - Pandas - PullRequest
4 голосов
/ 04 ноября 2019

Я пытаюсь создать новый столбец на основе условной логики для уже существующих столбцов. Я понимаю, что могут быть более эффективные способы достижения этого, но у меня есть несколько условий, которые необходимо включить. Это только первый шаг.

Общая задача состоит в том, чтобы создать два новых столбца, сопоставленных с 1 и 2. Они ссылаются на столбец Object, поскольку у меня может быть несколько строк для каждой временной точки.

Object2 и Value определяют способ сопоставления новых столбцов. Поэтому, если Value is == X, я хочу сопоставить оба столбца Object, чтобы вернуть соответствующие 1 и 2 для этого момента времени в новый столбец. Тот же процесс должен произойти, если Value is == Y. Если Value is == Z, я хочу вставить 0, 0. Все остальное должно быть NaN

df = pd.DataFrame({   
        'Time' : ['2019-08-02 09:50:10.1','2019-08-02 09:50:10.1','2019-08-02 09:50:10.2','2019-08-02 09:50:10.3','2019-08-02 09:50:10.3','2019-08-02 09:50:10.4','2019-08-02 09:50:10.5','2019-08-02 09:50:10.6','2019-08-02 09:50:10.6'],
        'Object' : ['B','A','A','A','C','C','C','B','B'],
        '1' : [1,3,5,7,9,11,13,15,17],  
        '2' : [0,1,4,6,8,10,12,14,16],     
        'Object2' : ['A','A',np.nan,'C','C','C','C','B','A'],                 
        'Value' : ['X','X',np.nan,'Y','Y','Y','Y','Z',np.nan],                  
        })

def map_12(df):

for i in df['Value']:
    if i == 'X':
        df['A1'] = df['1']
        df['A2'] = df['2']
    elif i == 'Y':
        df['A1'] = df['1']
        df['A2'] = df['2']     
    elif i == 'Z':
        df['A1'] = 0
        df['A2'] = 0             
    else:
        df['A1'] = np.nan
        df['A2'] = np.nan              

return df

Предполагаемый вывод:

                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      A   1   0       A     X   1.0   0.0 # Match A-A at this time point, so output is 1,0
1  2019-08-02 09:50:10.1      B   3   1       A     X   1.0   0.0 # Still at same time point so use 1,0 
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN # No Value so NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y   7.0   6.0 # Match C-C at this time point, so output is 7,6
4  2019-08-02 09:50:10.3      A   9   8       C     Y   7.0   6.0 # Still at same time point so use 7,6 
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0 # Match C-C at this time point, so output is 11,10
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0 # Match C-C at this time point, so output is 13,12
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0 # Z so 0,0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN # No Value so NaN

Новый пример df:

 df = pd.DataFrame({   
        'Time' : ['2019-08-02 09:50:10.1','2019-08-02 09:50:10.1','2019-08-02 09:50:10.2','2019-08-02 09:50:10.3','2019-08-02 09:50:10.3','2019-08-02 09:50:10.4','2019-08-02 09:50:10.5','2019-08-02 09:50:10.6','2019-08-02 09:50:10.6'],
        'Object' : ['B','A','A','A','C','C','C','B','B'],
        '1' : [1,3,5,7,9,11,13,15,17],  
        '2' : [0,1,4,6,8,10,12,14,16],     
        'Object2' : ['A','A',np.nan,'C','C','C','C','B','A'],                 
        'Value' : ['X','X',np.nan,'Y','Y','Y','Y','Z',np.nan],                
        })

Предполагаемый вывод:

                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      B   1   0       A     X   3.0   1.0 # Match A-A at this time point, so output is 3,1
1  2019-08-02 09:50:10.1      A   3   1       A     X   3.0   1.0 # Still at same time point so use 3,1 
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN # No Value so NaN
3  2019-08-02 09:50:10.3      A   7   6       C     Y   9.0   8.0 # Match C-C at this time point, so output is 9,8
4  2019-08-02 09:50:10.3      C   9   8       C     Y   9.0   8.0 # Still at same time point so use 9,8 
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0 # Match C-C at this time point, so output is 11,10
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0 # Match C-C at this time point, so output is 13,12
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0 # Z so 0,0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN # No Value so NaN

Ответы [ 4 ]

2 голосов
/ 06 ноября 2019

Используйте DataFrame.where + DataFrame.eq, чтобы создать DataFrame, подобный df[['1','2']], но только со строками, где совпадения равны True, а остальные с NaN,Затем сгруппируйте по временным точкам, используя DataFrame.groupby, и заполните недостающие данные каждой группы существующими значениями, где Object и Object2 (matches==True) совпадают. Используйте DataFrame.where для сброса значений, где df['Value'] равно NaN. Наконец, используйте [DataFrame.mask] для установки 0, когда Z находится в столбце Value

#matches
matches=df.Object.eq(df.Object2)
#Creating conditions
condition_z=df['Value']=='Z'
not_null=df['Value'].notnull()
#Creating DataFrame to fill
df12=( df[['1','2']].where(matches)
                    .groupby(df['Time'],sort=False)
                    .apply(lambda x: x.ffill().bfill()) )
#fill 0 on Value is Z and discarting NaN
df[['A1','A2']] =df12.where(not_null).mask(condition_z,0)
print(df)

Выход

                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      B   1   0       A     X   3.0   1.0
1  2019-08-02 09:50:10.1      A   3   1       A     X   3.0   1.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN
3  2019-08-02 09:50:10.3      A   7   6       C     Y   9.0   8.0
4  2019-08-02 09:50:10.3      C   9   8       C     Y   9.0   8.0
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN

Мы также можем использовать GroupBy.transform:

#matches
matches=df.Object.eq(df.Object2)
#Creating conditions
condition_z=df['Value']=='Z'
not_null=df['Value'].notnull()
#Creating DataFrame to fill
df12=( df[['1','2']].where(matches)
                    .groupby(df['Time'],sort=False)
                    .transform('first') )
#fill 0 on Value is Z and discarting NaN
df[['A1','A2']] =df12.where(not_null).mask(condition_z,0)
print(df)
1 голос
/ 06 ноября 2019

Если только несколько условий, используйте DataFrame.loc для назначения значений по условию:

m1 = df['Value'].isin(['X','Y'])
m2 = df['Value'] == 'Z'

df[['A1','A2']] = df.loc[m1, ['1','2']]
df.loc[m2, ['A1','A2']] = 0
print(df)
                    Time Object   1   2 Object2 Value   A1   A2
0  2019-08-02 09:50:10.1      A   1   0       A     X  1.0  0.0
1  2019-08-02 09:50:10.1      B   1   1       A     X  1.0  1.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN  NaN  NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y  7.0  6.0
4  2019-08-02 09:50:10.3      A   9   8       C     Y  9.0  8.0
5  2019-08-02 09:50:10.4      C  11  10     NaN   NaN  NaN  NaN
6  2019-08-02 09:50:10.5      C  13  12       B   NaN  NaN  NaN
7  2019-08-02 09:50:10.6      B  15  14       B     Z  0.0  0.0
8  2019-08-02 09:50:10.6      B  17  16       B   NaN  NaN  NaN

Другое решение с numpy.select и трансляцией масок:

m1 = df['Value'].isin(['X','Y'])
m2 = df['Value'] == 'Z'

masks = [m1.values[:, None], m2.values[:, None]]
values = [df[['1','2']].values, 0]

df[['A1','A2']] = pd.DataFrame(np.select(masks,values, default=np.nan), index=df.index)
print(df)
                    Time Object   1   2 Object2 Value   A1   A2
0  2019-08-02 09:50:10.1      A   1   0       A     X  1.0  0.0
1  2019-08-02 09:50:10.1      B   1   1       A     X  1.0  1.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN  NaN  NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y  7.0  6.0
4  2019-08-02 09:50:10.3      A   9   8       C     Y  9.0  8.0
5  2019-08-02 09:50:10.4      C  11  10     NaN   NaN  NaN  NaN
6  2019-08-02 09:50:10.5      C  13  12       B   NaN  NaN  NaN
7  2019-08-02 09:50:10.6      B  15  14       B     Z  0.0  0.0
8  2019-08-02 09:50:10.6      B  17  16       B   NaN  NaN  NaN
0 голосов
/ 06 ноября 2019

Мне пришлось внести несколько корректировок в ваш фрейм данных, так как он не соответствовал желаемому результату в вашем вопросе.

df = pd.DataFrame(
    {
        "Time": [
            "2019-08-02 09:50:10.1",
            "2019-08-02 09:50:10.1",
            "2019-08-02 09:50:10.2",
            "2019-08-02 09:50:10.3",
            "2019-08-02 09:50:10.3",
            "2019-08-02 09:50:10.4",
            "2019-08-02 09:50:10.5",
            "2019-08-02 09:50:10.6",
            "2019-08-02 09:50:10.6",
        ],
        "Object": ["A", "B", "A", "C", "A", "C", "C", "B", "B"],
        "1": [1, 1, 5, 7, 9, 11, 13, 15, 17],
        "2": [0, 1, 4, 6, 8, 10, 12, 14, 16],
        "Object2": ["A", "A", np.nan, "C", "C", "C", "C", "B", "A"],
        "Value": ["X", "X", np.nan, "Y", "Y", "Y", "Y", "Z", np.nan],
    }
)

Это векторизованное решение, которое должно хорошо работать с большими данными.

Первый шаг - убедиться, что кадр данных отсортирован по времени.

df = df.sort_values("Time")

Скопируйте столбцы 1 и 2

df["A1"] = df["1"]
df["A2"] = df["2"]

Использование значений индекса для получения первой строки каждой временной группы.

df = df.reset_index()

Я не очень доволен списком / isinрешение. Любопытно, если кто-нибудь знает менее хакерский способ сделать это?

li = df.groupby("Time").index.first().tolist()

print(li)
[0, 2, 3, 5, 6, 7]

print(df)
   index                   Time Object   1   2 Object2 Value  A1  A2
0      0  2019-08-02 09:50:10.1      A   1   0       A     X   1   0
1      1  2019-08-02 09:50:10.1      B   1   1       A     X   1   1
2      2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   5   4
3      3  2019-08-02 09:50:10.3      C   7   6       C     Y   7   6
4      4  2019-08-02 09:50:10.3      A   9   8       C     Y   9   8
5      5  2019-08-02 09:50:10.4      C  11  10       C     Y  11  10
6      6  2019-08-02 09:50:10.5      C  13  12       C     Y  13  12
7      7  2019-08-02 09:50:10.6      B  15  14       B     Z  15  14
8      8  2019-08-02 09:50:10.6      B  17  16       A   NaN  17  16

Отфильтруйте фрейм данных, чтобы получить все строки, кроме тех, что в списке, затем установите для них значение np.NaN

df.loc[~df.index.isin(li), ["A1", "A2"]] = np.NaN

Заполните вперед значения первой строки.

df[["A1", "A2"]] = df[["A1", "A2"]].ffill(axis=0)

Установите z в 0 и np.NaN в np.NaN

df.loc[df["Value"] == "Z", ["A1", "A2"]] = 0
df.loc[df["Value"].isnull(), ["A1", "A2"]] = np.NaN

Удалить столбец индекса

df = df.drop("index", axis=1)

print(df)
                    Time Object   1   2 Object2 Value    A1    A2
0  2019-08-02 09:50:10.1      A   1   0       A     X   1.0   0.0
1  2019-08-02 09:50:10.1      B   1   1       A     X   1.0   0.0
2  2019-08-02 09:50:10.2      A   5   4     NaN   NaN   NaN   NaN
3  2019-08-02 09:50:10.3      C   7   6       C     Y   7.0   6.0
4  2019-08-02 09:50:10.3      A   9   8       C     Y   7.0   6.0
5  2019-08-02 09:50:10.4      C  11  10       C     Y  11.0  10.0
6  2019-08-02 09:50:10.5      C  13  12       C     Y  13.0  12.0
7  2019-08-02 09:50:10.6      B  15  14       B     Z   0.0   0.0
8  2019-08-02 09:50:10.6      B  17  16       A   NaN   NaN   NaN
0 голосов
/ 04 ноября 2019

Посмотрите на Применяемый кадр данных

df['A1'] = df.apply(lambda row: row['1'] if row['Value'] == 'X' else np.nan, axis=1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...