#python #pandas
#python #pandas
Вопрос:
Я работаю с Pandas, и у меня есть большой список деталей с основным узлом, вспомогательным узлом I, вспомогательным узлом II и вспомогательным узлом III. Только один столбец «Assy» на строку может быть заполнен строкой в фрейме данных. Цель состоит в том, чтобы перенести расположение деталей в систему нумерации.В следующей таблице показан ожидаемый результат:
Main Assy Sub Assy I Sub Assy II Sub Assy III Level I Level II Level III Level IV
asd 1 0 0 0
fgd 1 1 0 0
sdd 1 1 1 0
dsd 1 1 2 0
fhg 1 1 3 0
tdc 1 1 3 1
dyx 1 1 3 2
dsg 1 1 3 3
dfg 1 2 0 0
cvf 1 2 1 0
ngs 1 2 2 0
vbn 1 2 3 0
dsd 1 2 3 1
vcd 1 2 3 2
cbn 1 2 3 3
ged 2 0 0 0
dfs 2 1 0 0
aef 2 1 1 0
Мой план состоял в том, чтобы накапливать по строкам в столбцах «Level» до тех пор, пока на более высоком уровне нет изменений. При изменении, таким образом, нового номера на более высоком уровне, выбранная ячейка на более низком уровне должна вернуться к нулю. Нет ли изменений, он сохраняет тот же номер. Я попробовал следующее:
df[lambda df: df.columns[0:4]] = df[lambda df: df.columns[0:4]].isna()
for index in range(0,4):
mask = ((df.iloc[:,index] == False))
print(mask)
df.iloc[:,(index 4)] = mask.groupby((~mask).cumsum()).cumsum().astype(int)
Поэтому я проверяю, заполнена ли ячейка, путем поиска пропущенных значений. Я не хочу использовать цикл с большим количеством условий для каждой строки из-за большого фрейма данных. Я использовал только этот цикл FOR для столбцов и попытался кумулировать, создав маску, которая показывает изменения с FALSE на TRUE.
Фактический результат:
Main Assy Sub Assy I Sub Assy II Sub Assy III Level I Level II Level III Level IV
asd 1 0 0 0
fgd 0 1 0 0
sdd 0 0 1 0
dsd 0 0 2 0
fhg 0 0 3 0
tdc 0 0 0 1
dyx 0 0 0 2
dsg 0 0 0 3
dfg 0 2 0 0
cvf 0 0 1 0
ngs 0 0 2 0
vbn 0 0 3 0
dsd 0 0 0 1
vcd 0 0 0 2
cbn 0 0 0 3
ged 2 0 0 0
dfs 0 1 0 0
aef 0 0 1 0
Каков был бы правильный способ настройки упомянутого условного подсчета без использования циклов?
Ответ №1:
Клавиша
Изменение выходных данных, которое будет применяться к каждой строке, может быть полностью определено текущим «уровнем» и предыдущим уровнем. Здесь «уровень» означает номер индекса столбца, имеющего ненулевую запись.
Другими словами, переменной состояния, сохраняющей уровень предыдущей строки, достаточно для правильного заполнения текущей строки.
Код
# the working dataset
df2 = df.iloc[:, :4].reset_index(drop=True) # make a copy
df2.columns = range(4) # rename columns to (0,1,2,3) for convenience
# output container
arr = np.zeros(df2.shape, dtype=int)
# state variable: level of the last row
last_lv = 0
for idx, row in df2.iterrows():
# get current indentation level
lv = row.first_valid_index()
if idx > 0:
# case 1: same or decreased level
if lv <= last_lv:
# keep previous levels except current level
arr[idx, :lv] = arr[idx-1, :lv]
# current level
arr[idx, lv] = arr[idx-1, lv] 1
# case 2: increased level
elif lv > last_lv:
# keep previous levels
arr[idx, :last_lv 1] = arr[idx - 1, :last_lv 1]
# start counting the new levels
arr[idx, last_lv 1:lv 1] = 1
# the first row
else:
arr[0, 0] = 1
# update state variable for next use
last_lv = lv
# append result to dataframe
df[["Level I", "Level II", "Level III", "Level IV"]] = arr
Результат
print(df[["Level I", "Level II", "Level III", "Level IV"]])
Level I Level II Level III Level IV
0 1 0 0 0
1 1 1 0 0
2 1 1 1 0
3 1 1 2 0
4 1 1 3 0
5 1 1 3 1
6 1 1 3 2
7 1 1 3 3
8 1 2 0 0
9 1 2 1 0
10 1 2 2 0
11 1 2 3 0
12 1 2 3 1
13 1 2 3 2
14 1 2 3 3
15 2 0 0 0
16 2 1 0 0
17 2 1 1 0
Примечания
- Код просто демонстрирует, как выглядит логика при переходе по каждой строке. Она не совсем оптимизирована, поэтому рассмотрите возможность использования более эффективных представлений данных (например, массива numpy или просто списка номеров уровней), когда эффективность становится проблемой.
- Я исследовал библиотеки для
tree
структур данных, таких как anytree и treelib, в надежде найти автоматизированный способ автоматического вывода иерархии дерева. К сожалению, функции ввода-вывода, подходящие для чтения текстовых файлов с отступом или сопоставимых форматов, похоже, отсутствовали. Это основная причина, по которой я все равно решаю изобрести велосипед.
Комментарии:
1. Привет @Bill Huang, спасибо за подробное объяснение. К сожалению, я также не нашел лучшего способа решить ее без циклов. Тем не менее, ваше решение более элегантное, чем я мог бы решить в любом случае. Большое вам спасибо. Это работает.