У меня есть фрейм данных pandas с именами столбцов Index, A, B, C, D. Значения в столбце D имеют либо положительные, либо отрицательные числа. Если, скажем, начальное значение индекса 0 для столбца D является отрицательным числом, то оно будет продолжаться таким же образом еще несколько строк, прежде чем переключиться на положительное число. Как только он переключится, он будет иметь тот же шаблон: в столбце D появится несколько положительных чисел, прежде чем он снова переключится на отрицательное.
Я хочу создать новый фрейм данных и сохранять в нем ссылку на индекс каждый раз, когда значения изменяются с положительного на отрицательное в столбце D или с отрицательного на положительное в столбце D. Следовательно, в приведенном ниже примере необходимо захватить индексы 3 и 7. :
Index Col D
0 23.3
1 24.4
2 43
3 -45
4 -54
5 -56
6 -89
7 89
8 90
Код
Имея дело с концепцией смены знаков, важно уточнить, как обращаться с 0. Поскольку в тексте нет объяснений, я предположил, что 0 не существует, и сгенерировал ответ.
out = df[df['Col D'].mul(df['Col D'].shift()).lt(0)].index
вне:
Int64Index([3, 7], dtype='int64')
Пример кода
import pandas as pd
data = {'Col D': [23.3, 24.4, 43.0, -45.0, -54.0, -56.0, -89.0, 89.0, 90.0]}
df = pd.DataFrame(data)
дф
Col D
0 23.3
1 24.4
2 43.0
3 -45.0
4 -54.0
5 -56.0
6 -89.0
7 89.0
8 90.0
Вы можете использовать маску и дифф.
Если вы хотите считать 0 положительным числом:
s = df['Col D'].ge(0)
out = df.index[s.diff().fillna(False)]
Если вы хотите рассматривать 0 отдельно от отрицательного/положительного, сначала используйте numpy.sign:
import numpy as np
s = np.sign(df['Col D'])
out = df.index[s.diff().fillna(0).ne(0)]
Выход:
Index([3, 7], dtype='int64')
считая 0 положительным числом:
Col D s s.diff() s.diff().fillna(False)
0 1 True NaN False
1 2 True False False
2 -1 False True True
3 -2 False False False
4 0 True True True
5 1 True False False # 1 and 0 have the same sign
учитывая 0 независимо:
Col D s s.diff() s.diff().fillna(0).ne(0)
0 1 1 NaN False
1 2 1 0.0 False
2 -1 -1 -2.0 True
3 -2 -1 0.0 False
4 0 0 1.0 True
5 1 1 1.0 True # 1 and 0 have a different sign
Функция numpy.sign возвращает -1 if x < 0, 0 if x==0, 1 if x > 0
. Абсолютная разница между возвращаемыми значениями между двумя строками при изменении знака равна 2. Используйте метод series.diff, чтобы найти, где это происходит.
import numpy as np
import pandas as pd
sample = {
"D": [23.3, 24.4, 43, -45, -54, -56, -89, 89, 90]
}
df = pd.DataFrame(sample)
df.index[df.D.map(np.sign).diff().abs().eq(2)]
Выход:
Index([3, 7], dtype='int64')
Объяснение операций с данными:
df.D
: выбирает столбец «D» из DataFrame df
, что эквивалентно df["D"]
.
.map(np.sign)
: применяет функцию NumPy np.sign
поэлементно к значениям в столбце «D». Функция np.sign
возвращает -1 для отрицательных чисел, 0 для нуля и 1 для положительных чисел.
.diff()
: вычисляет разницу между последовательными элементами ряда, полученными после применения np.sign
к столбцу «D». Учитывая, что в столбце нет нулевого значения, возможными результатами являются -2 и 2 (Отрицательный -> Положительный: -1 - 1 = -2; Положительный -> Отрицательный: 1 - -1 =2)
.abs()
: вычисляет абсолютное значение результирующей серии на основе .diff()
. Этот шаг гарантирует, что все различия положительны, поэтому при изменении знака результат всегда равен 2.
.eq(2)
: Сравнивает каждый элемент результирующей серии с 2, возвращая логическую серию, где True
указывает, что разница равна 2, а False
в противном случае.
df.index[df.D.map(np.sign).diff().abs().eq(2)]
: окончательное выражение использует логическое индексирование индекса DataFrame для извлечения индексов, в которых условие .eq(2)
имеет значение True.
Означает ли это, что у [-1, 0, 1]
не будет смены знака? ;)
Да. В вопросе нет требования к 0
.
Спасибо, Дэн, за твой ответ. Это сработало. Если у вас есть минутка, могу ли я попросить вас объяснить методы, которые вы использовали для решения этой проблемы, и что они делают в этом контексте?
Я обновил ответ, включив в него объяснение того, как работает код.
import numpy as np
import pandas as pd
data = {'Index': [0, 1, 2, 3, 4, 5, 6, 7, 8],
'Col D': [23.3, 24.4, 43, -45, -54, -56, -89, 89, 90]}
df = pd.DataFrame(data)
print(df)
# Convert Column D to a NumPy array
col_d_values = df['Col D'].to_numpy()#df['Col D'].values
# Find the sign of each value in Column D
signs = np.sign(col_d_values)
print(signs)#[ 1. 1. 1. -1. -1. -1. -1. 1. 1.]
# Find the indices where the sign changes
sign_changes = np.where(np.diff(signs) != 0)[0] + 1
# Get the index references where the sign changes
index_references = df.loc[sign_changes, 'Index'].tolist()
print(index_references)#[3, 7]
Спасибо. Ваш код работает... однако я не совсем уверен, что понимаю, как он достигает результата. Могу ли я попросить вас дать краткое объяснение того, как связаны методы и как они работают в каждой строке без использования цикла for. Спасибо :)