Установите значение для конкретной ячейки в панде DataFrame, используя индекс


479

Я создал Pandas DataFrame

df = DataFrame(index=['A','B','C'], columns=['x','y'])

и получил это

    ху
NaN NaN
B NaN NaN
C NaN NaN


Затем я хочу присвоить значение определенной ячейке, например, для строки «C» и столбца «x». Я ожидал получить такой результат:

    ху
NaN NaN
B NaN NaN
C 10 NaN

с этим кодом:

df.xs('C')['x'] = 10

но содержание dfне изменилось. Это снова только NaNв DataFrame.

Какие-либо предложения?


29
Не используйте 'цепную индексацию' ( df['x']['C']), используйте df.ix['x','C'].
Ярив

3
Порядок доступа к индексу должен быть следующим: dataframe[column (series)] [row (Series index)]многие люди (включая меня) более привыкли кdataframe[row][column] порядку. Как программист на Matlab и R, последний чувствует себя более интуитивно понятным для меня, но, видимо, это не тот способ, которым работает
Панда

1
я попробовал это, но в итоге я добавил еще одно имя строки x и другое имя столбца C. сначала нужно сделать строку, а затем столбец. поэтому df.ix ['C', 'x'] = 10
Матфея

5
К @ комментарию Ярива. Предупреждение: Начиная с версии 0.20.0, индексатор .ix устарел в пользу более строгих индексаторов .iloc и .loc. pandas.pydata.org/pandas-docs/stable/generated/… . df.at выглядит так, будто он торчит.
Джеффал

Ответы:


594

Ответ RukTech , df.set_value('C', 'x', 10)намного быстрее, чем варианты, которые я предложил ниже. Однако, это было намечено для обесценивания .

В дальнейшем рекомендуемый метод.iat/.at .


Почему df.xs('C')['x']=10не работает:

df.xs('C')по умолчанию возвращает новый фрейм данных с копией данных, поэтому

df.xs('C')['x']=10

изменяет только этот новый фрейм данных.

df['x']возвращает представление информационного dfкадра, поэтому

df['x']['C'] = 10

модифицирует dfсебя

Предупреждение : иногда трудно предсказать, вернет ли операция копию или представление. По этой причине в документах рекомендуется избегать назначений с «цепной индексацией» .


Таким образом, рекомендуемая альтернатива

df.at['C', 'x'] = 10

который делает изменение df.


In [18]: %timeit df.set_value('C', 'x', 10)
100000 loops, best of 3: 2.9 µs per loop

In [20]: %timeit df['x']['C'] = 10
100000 loops, best of 3: 6.31 µs per loop

In [81]: %timeit df.at['C', 'x'] = 10
100000 loops, best of 3: 9.2 µs per loop

Там нет такого понятия, как df.xв API . Что ты имел в виду?
smci

3
@smci: 'x'имя столбца в df. df.xвозвращает Seriesсо значениями в столбце x. Я изменю его на, df['x']поскольку эта нотация будет работать с любым именем столбца (в отличие от точечной нотации), и я думаю, что это более понятно.
unutbu

1
Я знал это, я думал, что вы говорите, что это df.xбыл какой-то неизвестный новый метод рядомdf.xs, df.ix
smci

df.xs(..., copy=True)возвращает копию, и это поведение по умолчанию. df.xs(..., copy=False)возвращает оригинал.
smci

7
По словам сопровождающих, это не рекомендуемый способ установки значения. См. Stackoverflow.com/a/21287235/1579844 и мой ответ.
Ярив

225

Обновление: .set_valueметод будет объявлен устаревшим . .iat/.atявляются хорошими заменами, к сожалению, панды предоставляет мало документации


Самый быстрый способ сделать это - использовать set_value . Этот метод в ~ 100 раз быстрее, чем .ixметод. Например:

df.set_value('C', 'x', 10)


5
Это даже лучше чем df['x']['C'] = 10 .
ALH

6
1000 циклов, лучшее из 3: 195 мкс на цикл "df ['x'] ['C'] = 10" 1000 циклов, лучшее из 3: 310 мкс на цикл "df.ix ['C', 'x'] = 10 "1000 циклов, лучшее из 3: 189 мкс на цикл" df.xs ('C', copy = False) ['x'] = 10 "1000 циклов, лучшее из 3: 7,22 мкс на цикл" df.set_value ('C', 'x', 10) "
propjk007

1
это также работает для добавления новой строки / столбца в фрейм данных?
st.ph.n

Да, это так (для панд 0.16.2)
RukTech

Можно ли использовать это, чтобы установить значение в df=df.append(df.sum(numeric_only=True),ignore_index=True)?
Ctrl-Alt-Delete

95

Вы также можете использовать условный поиск, .locкак показано здесь:

df.loc[df[<some_column_name>] == <condition>, [<another_column_name>]] = <value_to_add>

где <some_column_name- столбец, с которым вы хотите проверить <condition>переменную, и <another_column_name>столбец, к которому вы хотите добавить (может быть новый столбец или столбец, который уже существует). <value_to_add>это значение, которое вы хотите добавить в этот столбец / строку.

Этот пример не работает точно с рассматриваемым вопросом, но он может быть полезен для тех, кто хочет добавить определенное значение на основе условия.


8
второй столбец должен быть в скобках, иначе все столбцы будут перезаписаны значением. Как это:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei

Я не могу заставить это работать, когда <some_column_name> является моим индексом (скажем, индексом unixtime), и я пытаюсь добавить временную метку, которая еще не завершается (т.е. новое чтение временной метки). Какие-нибудь мысли?
yeliabsalohcin

Можно ли изменить значение на основе индекса и значений ячейки?
BND

@BND Я не уверен, но вы могли бы обойти эту очевидную ловушку, но просто дублировать столбец индекса с другим столбцом с тем же значением? Краткий ответ: я не знаю.
Blairg23

@yeliabsalohcin см. ответ выше.
Blairg23

40

Рекомендуемый способ (по словам сопровождающих) установить значение:

df.ix['x','C']=10

Использование «цепной индексации» ( df['x']['C']) может привести к проблемам.

Видеть:


7
ixявляется устаревшим: pandas-docs.github.io/pandas-docs-travis/...
ECOE

работает отлично! хотя когда-нибудь это будет устаревшим!
Павлос Понос

35

Попробуйте использовать df.loc[row_index,col_indexer] = value


6
Добро пожаловать в переполнение стека! Пожалуйста, отредактируйте ваш пост, чтобы добавить больше объяснений о том, что делает ваш код и почему это решит проблему. Ответ, который в основном содержит только код (даже если он работает), обычно не помогает ОП понять их проблему. Также рекомендуется не публиковать ответ, если это всего лишь предположение. Хороший ответ будет иметь правдоподобную причину, по которой он может решить проблему ОП.
SuperBiasedMan

22

Это единственное, что сработало для меня!

df.loc['C', 'x'] = 10

Узнайте больше о .loc здесь .


сделал .locзамену .iat/.at?
Габриэль Ярмарка

1
atАналогично тому loc, что оба обеспечивают поиск на основе меток. Используйте, atесли вам нужно только получить или установить одно значение в DataFrame или Series. Из падас док
Рутрус

Хорошо, это работало для меня, когда мои элементы индекса были числовыми.
Кристофер Джон

Это не работает для сочетания числовых и строковых индексов.
Seanny123

12

.iat/.atэто хорошее решение. Предположим, у вас есть этот простой data_frame:

   A   B   C
0  1   8   4 
1  3   9   6
2  22 33  52

если мы хотим изменить значение ячейки, [0,"A"]вы можете использовать одно из этих решений:

  1. df.iat[0,0] = 2
  2. df.at[0,'A'] = 2

И вот полный пример того, как использовать, iatчтобы получить и установить значение ячейки:

def prepossessing(df):
  for index in range(0,len(df)): 
      df.iat[index,0] = df.iat[index,0] * 2
  return df

y_train до:

    0
0   54
1   15
2   15
3   8
4   31
5   63
6   11

y_train после вызова функции prepossessing, которую iatнужно изменить, чтобы умножить значение каждой ячейки на 2:

     0
0   108
1   30
2   30
3   16
4   62
5   126
6   22

8

Чтобы установить значения, используйте:

df.at[0, 'clm1'] = 0
  • Самый быстрый рекомендуемый метод для установки переменных.
  • set_value, ix не рекомендуется.
  • Нет предупреждения, в отличие от ilocиloc

1
Я пришел к точно такому же выводу .
прости

6

Вы можете использовать .iloc.

df.iloc[[2], [0]] = 10

Этот метод, кажется, не поддерживает несколько значений, например, df.iloc[[2:8], [0]] = [2,3,4,5,6,7]которые метод df.loc()делает изначально.
strpeter

1
работает отлично, без предупреждения об устаревании!
Павлос Понос

6

В моем примере я просто изменить его в выбранной ячейке

    for index, row in result.iterrows():
        if np.isnan(row['weight']):
            result.at[index, 'weight'] = 0.0

'result' - это поле данных со столбцом 'weight'


4

set_value() устарел.

Начиная с версии 0.23.4, Pandas " объявляет о будущем " ...

>>> df
                   Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        245.0
2      Chevrolet Malibu        190.0
>>> df.set_value(2, 'Prices (U$)', 240.0)
__main__:1: FutureWarning: set_value is deprecated and will be removed in a future release.
Please use .at[] or .iat[] accessors instead

                   Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        245.0
2      Chevrolet Malibu        240.0

Учитывая этот совет, вот демонстрация того, как их использовать:

  • по целым позициям строки / столбца

>>> df.iat[1, 1] = 260.0
>>> df
                   Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        260.0
2      Chevrolet Malibu        240.0
  • по меткам строк / столбцов

>>> df.at[2, "Cars"] = "Chevrolet Corvette"
>>> df
                  Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        260.0
2    Chevrolet Corvette        240.0

Ссылки:


3

Вот сводка действительных решений, предоставленных всеми пользователями, для фреймов данных, индексированных по целому числу и строке.

df.iloc, df.loc и df.at работают для обоих типов фреймов данных, df.iloc работает только с целочисленными индексами строк / столбцов, df.loc и df.at поддерживают установку значений с использованием имен столбцов и / или целочисленных индексов ,

Если указанный индекс не существует, и df.loc, и df.at добавят вновь вставленные строки / столбцы в существующий фрейм данных, но df.iloc вызовет «IndexError: позиционные индексаторы выходят за пределы». Рабочий пример, протестированный в Python 2.7 и 3.7, выглядит следующим образом:

import numpy as np, pandas as pd

df1 = pd.DataFrame(index=np.arange(3), columns=['x','y','z'])
df1['x'] = ['A','B','C']
df1.at[2,'y'] = 400

# rows/columns specified does not exist, appends new rows/columns to existing data frame
df1.at['D','w'] = 9000
df1.loc['E','q'] = 499

# using df[<some_column_name>] == <condition> to retrieve target rows
df1.at[df1['x']=='B', 'y'] = 10000
df1.loc[df1['x']=='B', ['z','w']] = 10000

# using a list of index to setup values
df1.iloc[[1,2,4], 2] = 9999
df1.loc[[0,'D','E'],'w'] = 7500
df1.at[[0,2,"D"],'x'] = 10
df1.at[:, ['y', 'w']] = 8000

df1
>>> df1
     x     y     z     w      q
0   10  8000   NaN  8000    NaN
1    B  8000  9999  8000    NaN
2   10  8000  9999  8000    NaN
D   10  8000   NaN  8000    NaN
E  NaN  8000  9999  8000  499.0

3

Я проверил, и вывод df.set_valueнемного быстрее, но официальный метод df.atвыглядит как самый быстрый и не устаревший способ сделать это.

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.rand(100, 100))

%timeit df.iat[50,50]=50 # ✓
%timeit df.at[50,50]=50 #  ✔
%timeit df.set_value(50,50,50) # will deprecate
%timeit df.iloc[50,50]=50
%timeit df.loc[50,50]=50

7.06 µs ± 118 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
5.52 µs ± 64.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
3.68 µs ± 80.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
98.7 µs ± 1.07 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
109 µs ± 1.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Обратите внимание, что это установка значения для одной ячейки. Для векторов locи ilocдолжны быть лучшие варианты, так как они векторизованы.


3

Один из способов использования индекса с условием - сначала получить индекс всех строк, которые удовлетворяют вашему условию, а затем просто использовать эти индексы строк несколькими способами.

conditional_index = df.loc[ df['col name'] <condition> ].index

Пример условия как

==5, >10 , =="Any string", >= DateTime

Затем вы можете использовать эти индексы строк различными способами, такими как

  1. Заменить значение одного столбца для conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Заменить значение нескольких столбцов для conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Одним из преимуществ сохранения условного индекса является то, что вы можете назначить значение одного столбца другому столбцу с тем же индексом строки
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

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


как насчет смены строк?
ФабиоСпагетти

просто используйте, df.loc [conditional_index,] = <новое значение> Он заменит новое значение во всех столбцах строк, которые удовлетворяют условию
Atta Jutt


1

В дополнение к ответам, приведенным выше, приведен сравнительный анализ различных способов добавления строк данных в уже существующий фрейм данных. Это показывает, что использование at или set-value является наиболее эффективным способом для больших фреймов данных (по крайней мере, для этих условий тестирования).

  • Создайте новый фрейм данных для каждой строки и ...
    • ... добавить его (13,0 с)
    • ... объединить его (13,1 с)
  • Сначала сохраните все новые строки в другом контейнере, один раз преобразуйте в новый фрейм данных и добавьте ...
    • контейнер = списки списков (2,0 с)
    • контейнер = словарь списков (1,9 с)
  • Предварительно распределите весь фрейм данных, переберите новые строки и все столбцы и заполните, используя
    • ... в (0,6 с)
    • ... set_value (0,4 с)

Для теста использовался существующий фрейм данных, состоящий из 100 000 строк и 1000 столбцов и случайных числовых значений. К этому фрейму данных было добавлено 100 новых строк.

Код смотри ниже:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 21 16:38:46 2018

@author: gebbissimo
"""

import pandas as pd
import numpy as np
import time

NUM_ROWS = 100000
NUM_COLS = 1000
data = np.random.rand(NUM_ROWS,NUM_COLS)
df = pd.DataFrame(data)

NUM_ROWS_NEW = 100
data_tot = np.random.rand(NUM_ROWS + NUM_ROWS_NEW,NUM_COLS)
df_tot = pd.DataFrame(data_tot)

DATA_NEW = np.random.rand(1,NUM_COLS)


#%% FUNCTIONS

# create and append
def create_and_append(df):
    for i in range(NUM_ROWS_NEW):
        df_new = pd.DataFrame(DATA_NEW)
        df = df.append(df_new)
    return df

# create and concatenate
def create_and_concat(df):
    for i in range(NUM_ROWS_NEW):
        df_new = pd.DataFrame(DATA_NEW)
        df = pd.concat((df, df_new))
    return df


# store as dict and 
def store_as_list(df):
    lst = [[] for i in range(NUM_ROWS_NEW)]
    for i in range(NUM_ROWS_NEW):
        for j in range(NUM_COLS):
            lst[i].append(DATA_NEW[0,j])
    df_new = pd.DataFrame(lst)
    df_tot = df.append(df_new)
    return df_tot

# store as dict and 
def store_as_dict(df):
    dct = {}
    for j in range(NUM_COLS):
        dct[j] = []
        for i in range(NUM_ROWS_NEW):
            dct[j].append(DATA_NEW[0,j])
    df_new = pd.DataFrame(dct)
    df_tot = df.append(df_new)
    return df_tot




# preallocate and fill using .at
def fill_using_at(df):
    for i in range(NUM_ROWS_NEW):
        for j in range(NUM_COLS):
            #print("i,j={},{}".format(i,j))
            df.at[NUM_ROWS+i,j] = DATA_NEW[0,j]
    return df


# preallocate and fill using .at
def fill_using_set(df):
    for i in range(NUM_ROWS_NEW):
        for j in range(NUM_COLS):
            #print("i,j={},{}".format(i,j))
            df.set_value(NUM_ROWS+i,j,DATA_NEW[0,j])
    return df


#%% TESTS
t0 = time.time()    
create_and_append(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
create_and_concat(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
store_as_list(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
store_as_dict(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
fill_using_at(df_tot)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
fill_using_set(df_tot)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

0

Если вы хотите изменить значения не для всей строки, а только для некоторых столбцов:

x = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
x.iloc[1] = dict(A=10, B=-10)

0

С версии 0.21.1 вы также можете использовать .atметод. Есть некоторые отличия по сравнению с .locупомянутыми здесь - pandas .at и .loc , но это быстрее при замене одного значения


0

Так, ваш вопрос, чтобы преобразовать NaN в ['x', C] в значение 10

ответ..

df['x'].loc['C':]=10
df

альтернативный код

df.loc['C':'x']=10
df

-4

Я тоже искал эту тему, и я собрал способ перебирать DataFrame и обновлять его поисковыми значениями из второго DataFrame. Вот мой код

src_df = pd.read_sql_query(src_sql,src_connection)
for index1, row1 in src_df.iterrows():
    for index, row in vertical_df.iterrows():
        src_df.set_value(index=index1,col=u'etl_load_key',value=etl_load_key)
        if (row1[u'src_id'] == row['SRC_ID']) is True:
            src_df.set_value(index=index1,col=u'vertical',value=row['VERTICAL'])
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.