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


Я создал панду 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 не изменилось. Это снова только Нэн в кадре данных.

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

25 ответов

Решение

Ответ 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

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


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

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

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

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

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

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

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

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

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

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

Увидеть:

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

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

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

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

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

.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

Я бы посоветовал:

      df.loc[index_position, "column_name"] = some_value

Избегайте присвоения с помощью цепной индексации

Вы имеете дело с назначением с цепной индексацией, которое приведет к SettingWithCopyпредупреждение . Этого следует избегать во что бы то ни стало.

В вашем задании придется прибегнуть к одному единственному .loc[] или .iloc[]slice, как описано здесь . Следовательно, в вашем случае:

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

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

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

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

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

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

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

Рекомендации:

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

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

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

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

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

Я проверил и вывод 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 должны быть лучшие варианты, так как они векторизованы.

Если кто-то хочет изменить ячейку в позиции (0,0) df на строку, такую ​​как '"236"76"', следующие параметры сделают работу:

      df[0][0] = '"236"76"'
# %timeit df[0][0] = '"236"76"'
# 938 µs ± 83.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Или используя

      df.at[0, 0] = '"236"76"'
#  %timeit df.at[0, 0] = '"236"76"' 
#15 µs ± 2.09 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Или используя pandas.DataFrame.iat

      df.iat[0, 0] = '"236"76"'
#  %timeit df.iat[0, 0] = '"236"76"'
# 41.1 µs ± 3.09 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Или используя pandas.DataFrame.loc

      df.loc[0, 0] = '"236"76"'
#  %timeit df.loc[0, 0] = '"236"76"'
# 5.21 ms ± 401 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Или используя pandas.DataFrame.iloc

      df.iloc[0, 0] = '"236"76"'
#  %timeit df.iloc[0, 0] = '"236"76"'
# 5.12 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Если время имеет значение, используя pandas.DataFrame.at это самый быстрый подход.

Были разочарованы множеством ответов, данных с помощью loc и .iloc, таких как

      df.loc[index_position, "column_name"] = some_value

потому что при попытке этого они продолжают выдавать ошибки или предупреждения, такие как

Must have equal len keys and value when setting with an iterable

и

SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame

**однако я хотел бы разъяснить другим, у кого возникла эта проблема, что **

      df.at[2, "Cars"] = mystuff

устранил предупреждение для меня и работает как шарм!

Су, ваш вопрос о преобразовании NaN в ['x',C] в значение 10

ответ..

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

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

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

df.loc['c','x']=10Это изменит значение c- й строки и x- го столбца.

В дополнение к ответам, приведенным выше, ниже приведен сравнительный анализ различных способов добавления строк данных в уже существующий фрейм данных. Это показывает, что использование 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))

Вы также можете изменить базовый массив ячейки.

values/to_numpy()возвращает представление базового массива DataFrame, поэтому, если определенное значение в массиве изменяется, это изменение также отражается на DataFrame.

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

# change the last value in the first column
df.values[-1, 0] = 10
df.to_numpy()[-1, 0] = 10


     x    y
A  NaN  NaN
B  NaN  NaN
C   10  NaN

Вы также можете выбрать столбец, просмотреть его базовый массив и изменить его по индексу. Этот метод работает, даже если dtype является Extension Dtype.

      # change the last value in column 'x'
df['x'].values[-1] = 100

Изменение представления DataFrame является самым быстрым способом (в 5 раз быстрее, чем следующий самый быстрый метод) для установки значения в ячейке, что становится актуальным, если это делается в цикле.

      df = pd.DataFrame(index=['A', 'B', 'C'], columns=['x', 'y'])
%timeit df.values[-1, 0] = 10   # 1.89 µs ± 85.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit df.iat[-1, 0] = 10      # 10.9 µs ± 380 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
%timeit df.at['C', 'x'] = 10    # 13 µs ± 307 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
%timeit df.loc['C', 'x'] = 10   # 55.4 µs ± 6.16 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit df.iloc[-1, 0] = 10     # 39.7 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

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

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

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

Я тоже искал эту тему, и я собрал способ перебирать 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'])
Другие вопросы по тегам