Установить значение первого элемента в слайсе в python pandas

Итак, я хотел бы сделать фрагмент фрейма данных, а затем установить значение первого элемента в этом фрагменте без копирования фрейма данных. Например:

df = pandas.DataFrame(numpy.random.rand(3,1))
df[df[0]>0][0] = 0

Срез здесь не имеет значения и предназначен только для примера и снова вернет весь фрейм данных. Дело в том, что, делая это так, как в примере, вы получаете настройку с предупреждением о копировании (понятно). Я также пробовал сначала нарезать, а затем использовать ILOC/IX/LOC и дважды использовать ILOC, то есть что-то вроде:

df.iloc[df[0]>0,:][0] = 0
df[df[0]>0,:].iloc[0] = 0

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

РЕДАКТИРОВАТЬ: кажется, есть два способа: использовать маску или IdxMax. Метод IdxMax работает, если ваш индекс уникален, а метод маски — в противном случае. В моем случае индекс не уникален, о чем я забыл упомянуть в начальном сообщении.


person RexFuzzle    schedule 28.02.2017    source источник


Ответы (4)


Я думаю, вы можете использовать idxmax для получения индекса первого значения True, а затем устанавливается loc :

np.random.seed(1)
df = pd.DataFrame(np.random.randint(4, size=(5,1)))
print (df)
   0
0  1
1  3
2  0
3  0
4  3

print ((df[0] == 0).idxmax())
2

df.loc[(df[0] == 0).idxmax(), 0] = 100
print (df)
     0
0    1
1    3
2  100
3    0
4    3

df.loc[(df[0] == 3).idxmax(), 0] = 200
print (df)
     0
0    1
1  200
2    0
3    0
4    3

РЕДАКТИРОВАТЬ:

Решение с неуникальным индексом:

np.random.seed(1)
df = pd.DataFrame(np.random.randint(4, size=(5,1)), index=[1,2,2,3,4])
print (df)
   0
1  1
2  3
2  0
3  0
4  3

df = df.reset_index()
df.loc[(df[0] == 3).idxmax(), 0] = 200
df = df.set_index('index')
df.index.name = None
print (df)
     0
1    1
2  200
2    0
3    0
4    3

РЕДАКТИРОВАТЬ1:

Решение с MultiIndex:

np.random.seed(1)
df = pd.DataFrame(np.random.randint(4, size=(5,1)), index=[1,2,2,3,4])
print (df)
   0
1  1
2  3
2  0
3  0
4  3

df.index = [np.arange(len(df.index)), df.index]
print (df)
     0
0 1  1
1 2  3
2 2  0
3 3  0
4 4  3

df.loc[(df[0] == 3).idxmax(), 0] = 200
df = df.reset_index(level=0, drop=True)

print (df)
     0
1    1
2  200
2    0
3    0
4    3

РЕДАКТИРОВАТЬ2:

Решение с двойным cumsum:

np.random.seed(1)
df = pd.DataFrame([4,0,4,7,4], index=[1,2,2,3,4])
print (df)
   0
1  4
2  0
2  4
3  7
4  4

mask = (df[0] == 0).cumsum().cumsum()
print (mask)
1    0
2    1
2    2
3    3
4    4
Name: 0, dtype: int32

df.loc[mask == 1, 0] = 200
print (df)
     0
1    4
2  200
2    4
3    7
4    4
person jezrael    schedule 28.02.2017
comment
Будет ли это работать, если вторая семерка не находится сразу после первой, то есть если результат cumsum в логическом массиве будет иметь несколько 1? - person juanpa.arrivillaga; 28.02.2017
comment
@juanpa.arrivillaga - спасибо, вы правы. Подожди секунду - person jezrael; 28.02.2017
comment
Я думаю, что самый надежный способ, и это надежно только в том случае, если ваш индекс уникален, - это получить индекс из среза, затем получить первое значение из индекса и установить его с использованием этого значения в исходном кадре. - person juanpa.arrivillaga; 28.02.2017
comment
Ах, idxmax, очень-очень умно! - person juanpa.arrivillaga; 28.02.2017
comment
@RexFuzzle - Да, это работает, если значения не совпадают, см. Второе решение с другим условием. - person jezrael; 28.02.2017
comment
Очень круто, к сожалению, мой индекс не уникален :( Исправленный вопрос. Кажется, метод маски может быть единственным в моем случае. Спасибо @jezrael - person RexFuzzle; 28.02.2017
comment
Я также не могу сбросить индекс, так как он мне нужен - предположим, я мог бы сохранить его и вернуть обратно. - person RexFuzzle; 28.02.2017
comment
Почему нельзя использовать reset_index и set_index? - person jezrael; 28.02.2017
comment
Я добавляю еще одно решение с MultiIndex. - person jezrael; 28.02.2017
comment
Для неуникального индекса альтернативой могут быть iloc и argmax. - person ayhan; 28.02.2017

Рассмотрим кадр данных df

df = pd.DataFrame(dict(A=[1, 2, 3, 4, 5]))

print(df)

   A
0  1
1  2
2  3
3  4
4  5

Создайте произвольный фрагмент slc

slc = df[df.A > 2]

print(slc)

   A
2  3
3  4
4  5

Доступ к первой строке slc в пределах df с помощью index[0] и loc

df.loc[slc.index[0]] = 0
print(df)

   A
0  1
1  2
2  0
3  4
4  5
person piRSquared    schedule 05.03.2017
comment
Я надеялся не дублировать какую-либо часть df, так как он большой, и даже срез может быть довольно большим. - person RexFuzzle; 06.03.2017
comment
@RexFuzzle, вы сказали, что фрагмент был произвольным, и я предполагаю, что он уже существует. Из этого фрагмента я беру первое значение индекса и использую его для изменения исходного df. - person piRSquared; 06.03.2017
comment
Я думаю, что что-то вроде df.loc[slice, another_slice] должно потреблять меньше памяти, чем df.loc[slice].loc[:, another_slice]. Это возможно для одновременного разделения строк и столбцов, но, похоже, невозможно сделать это по строкам с разными условиями. На самом деле я не уверен, может быть, то, что я имею в виду, не имеет смысла. - person ayhan; 09.03.2017

import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(6,1),index=[1,2,2,3,3,3])
df[1] = 0
df.columns=['a','b']
df['b'][df['a']>=0.5]=1
df=df.sort(['b','a'],ascending=[0,1])
df.loc[df[df['b']==0].index.tolist()[0],'a']=0

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

df.loc[df[df['b']==0].index.tolist()[n],'a']=0

изменить любой n-й элемент в срезе

df

          a  
1  0.111089  
2  0.255633  
2  0.332682  
3  0.434527  
3  0.730548  
3  0.844724  

df после нарезки и маркировки их

          a  b
1  0.111089  0
2  0.255633  0
2  0.332682  0
3  0.434527  0
3  0.730548  1
3  0.844724  1

После изменения значения первого элемента в срезе (обозначенного как 0) на 0

          a  b
3  0.730548  1
3  0.844724  1
1  0.000000  0
2  0.255633  0
2  0.332682  0
3  0.434527  0
person Samhitha Challa    schedule 09.03.2017

Итак, используя некоторые ответы, мне удалось найти способ сделать это одним вкладышем:

np.random.seed(1)
df = pd.DataFrame(np.random.randint(4, size=(5,1)))
print df
   0
0  1
1  3
2  0
3  0
4  3
df.loc[(df[0] == 0).cumsum()==1,0] = 1
   0
0  1
1  3
2  1
3  0
4  3

По сути, это использование маски, встроенной в cumsum.

person RexFuzzle    schedule 27.03.2017