#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]]