Как установить все значения после определенного значения в 0?

#python #pandas #dataframe

Вопрос:

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

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

        1        2        3        4        5        6        7        8     Select     Value  
--------------------------------------------------------------------------------------------------
 0    54       33       46       23       35       22       36       36          3        46
 1    36       54       32       14       32       21       54       55          4        14
 2    34       29       11       14       21       29       33       46          6        29
 3    35       19       22       45       55       16       21       74          5        55
 4    27       39       43       22       22       24       22       55          3        43
 

Столбец «Выбрать» содержит значение, соответствующее столбцу, на который я хочу ссылаться. И поэтому для каждой строки я хочу установить каждое значение после значения в указанном столбце равным 0. Столбец «Значение» содержит это значение, но для целей данной проблемы будет проигнорирован.

И поэтому я пытаюсь создать следующий фрейм данных:

        1        2        3        4        5        6        7        8     Select     Value
-------------------------------------------------------------------------------------------------- 
 0    54       33       46        0        0        0        0        0          3        46
 1    36       54       32       14        0        0        0        0          4        14
 2    34       29       11       14       21       29        0        0          6        29
 3    35       19       22       45       55        0        0        0          5        55
 4    27       39       43        0        0        0        0        0          3        43
 

Как вы можете видеть, значение в столбце, на которое ссылается столбец «Выбрать», использовалось в качестве точки, для которой все значения после 0 были установлены.

Есть ли простой способ сделать это в python? У меня возникли проблемы с решением этой проблемы, так как я не уверен, как ссылаться на значения в столбцах, которые указаны отдельным столбцом. Я не понимаю, как заставить python видеть связь между значениями в столбце «Выбрать» и столбцами, на которые они должны ссылаться.

Ответ №1:

Одно из возможных решений-отфильтровать фрейм данных для числовых столбцов и использовать .apply , где с помощью логического массива «маскировать» правильные значения:

 numbered = df.filter(regex=r"^d $").columns
nums = numbered.astype(int)

df.loc[:, numbered] = df.apply(
    lambda x: x[numbered] * (nums <= x["Select"]),
    axis=1,
)
print(df)
 

С принтами:

     1   2   3   4   5   6  7  8  Select  Value
0  54  33  46   0   0   0  0  0       3     46
1  36  54  32  14   0   0  0  0       4     14
2  34  29  11  14  21  29  0  0       6     29
3  35  19  22  45  55   0  0  0       5     55
4  27  39  43   0   0   0  0  0       3     43
 

Комментарии:

1. Спасибо вам за это решение, хотя я забыл включить столбец в свой фрейм данных. У меня есть еще один столбец, который является первым столбцом, прямо перед столбцом «1». Это изменило бы то, как будет работать ваш код?

2. @LostinSpatialAnalysis Это не должно изменить то, как работает код. Этот пример работает только с числовыми столбцами (1, 2, 3…) и столбцом «Выбрать»

3. Я попробовал код еще раз и получил следующую ошибку: IndexError: ('only integers, slices ( : ), ellipsis ( ), numpy.newaxis ( Нет ) and integer or boolean arrays are valid indices', 'occurred at index 0') . Хотя, если мои столбцы имеют номера от 10, 20, 30, 40 и т.д., Приведет ли это к сбою кода, а не к тому, что столбцы будут просто пронумерованы 1,2,3,4 и т.д.?

Ответ №2:

Это не самый чистый способ, но он сделал бы свое дело. Кроме того, приведенное ниже также охватывает ваше второе требование!

 import pandas as pd

COLS = [ '1', '2', '3', '4', '5', '6', '7', '8', 'Select', 'Value']

df = pd.DataFrame(
    columns=COLS,
    data=[
        ['54', '33', '46', '23', '35', '22', '36', '36', '3', '46'],
        ['36', '54', '32', '14', '32', '21', '54', '55', '4', '14'],
        ['34', '29', '11', '14', '21', '29', '33', '46', '6', '29'],
        ['35', '19', '22', '45', '55', '16', '21', '74', '5', '55'],
        ['27', '39', '43', '22', '22', '24', '22', '55', '3', '43'],
    ]
)

new_rows = []
def first_found_filter(rows):
    trigger_zeros = False
    new_row = []
    for col in COLS:
        if col == 'Select':
            break
        if col == rows['Select'] and rows[col] == rows.Value:
            trigger_zeros = True
        elif trigger_zeros:
            rows[col] = 0
        new_row.append(rows[col])

    new_row.append(rows['Select'])
    new_row.append(rows['Value'])
    new_rows.append(new_row)

df.apply(first_found_filter, axis=1)


df2 = pd.DataFrame(
    columns=COLS,
    data=new_rows
)

print(df)
print(df2)

 

Ответ №3:

Предполагая, что столбцы являются числовыми, мы можем использовать np.tile и broadcasting для создания маски. Затем используйте DataFrame.mask для замены значений на 0:

 import numpy as np
import pandas as pd

df = pd.DataFrame({
    1: [54, 36, 34, 35, 27], 2: [33, 54, 29, 19, 39],
    3: [46, 32, 11, 22, 43], 4: [23, 14, 14, 45, 22],
    5: [35, 32, 21, 55, 22], 6: [22, 21, 29, 16, 24],
    7: [36, 54, 33, 21, 22], 8: [36, 55, 46, 74, 55],
    'Select': [3, 4, 6, 5, 3], 'Value': [46, 14, 29, 55, 43]
})

# Get list of columns (can be done programmatically as well)
# cols = df.columns[0:8]  # (For example)
cols = [1, 2, 3, 4, 5, 6, 7, 8]
# Create Array representing the column space
# and compare to the values in Select Column
df[cols] = df[cols].mask(
    np.tile(cols, (df.shape[0], 1)) > df['Select'].values[:, None],
    0
)
print(df)
 

df :

     1   2   3   4   5   6  7  8  Select  Value
0  54  33  46   0   0   0  0  0       3     46
1  36  54  32  14   0   0  0  0       4     14
2  34  29  11  14  21  29  0  0       6     29
3  35  19  22  45  55   0  0  0       5     55
4  27  39  43   0   0   0  0  0       3     43
 

Создание маски:

 np.tile(cols, (df.shape[0], 1))

[[1 2 3 4 5 6 7 8]
 [1 2 3 4 5 6 7 8]
 [1 2 3 4 5 6 7 8]
 [1 2 3 4 5 6 7 8]
 [1 2 3 4 5 6 7 8]]
 
 np.tile(cols, (df.shape[0], 1)) > df['Select'].values[:, None]

[[False False False  True  True  True  True  True]
 [False False False False  True  True  True  True]
 [False False False False False False  True  True]
 [False False False False False  True  True  True]
 [False False False  True  True  True  True  True]]
 

В случае, если значения столбцов являются более сложными и не приписываются определенному порядку, мы можем использовать Index.get_indexer их для преобразования в числовые значения:

 import numpy as np
import pandas as pd

df = pd.DataFrame({
    'col': [54, 36, 34],
    'other col': [33, 54, 29],
    'yet another col': [46, 32, 11],
    'Select': ['col', 'yet another col', 'other col'],
    'Value': [54, 32, 29]
})

cols = ['col', 'other col', 'yet another col']
df[cols] = df[cols].mask(
    np.tile(df.columns.get_indexer(cols), (df.shape[0], 1))
    > df.columns.get_indexer(df['Select'])[:, None],
    0
)
print(df)
 

df :

    col  other col  yet another col           Select  Value
0   54          0                0              col     54
1   36         54               32  yet another col     32
2   34         29                0        other col     29
 

Создание маски в нечисловом случае

Преобразование строковых столбцов в числовые значения:

 np.tile(df.columns.get_indexer(cols), (df.shape[0], 1))

[[0 1 2]
 [0 1 2]
 [0 1 2]]
 

Сравните со Select столбцом, преобразованным в индексаторы того же типа:

 (
        np.tile(df.columns.get_indexer(cols), (df.shape[0], 1))
        > df.columns.get_indexer(df['Select'])[:, None]
)

[[False  True  True]
 [False False False]
 [False False  True]]