Изменение значений в панде датафрейм не работает

Im имеет проблему изменения значений в кадре данных. Я также хочу посоветоваться с проблемой, которую мне нужно решить, и правильным способом использования pandas для ее решения. Я буду признателен за помощь в обоих. У меня есть файл, содержащий информацию о степени соответствия звуковых файлов динамикам. Файл выглядит примерно так:

wave_path   spk_name    spk_example#    score   mark    comments    isUsed
190  122_65_02.04.51.800.wav     idoD    idoD    88  NaN     NaN     False
191  121_110_20.17.27.400.wav    idoD    idoD    87  NaN     NaN     False
192  121_111_00.34.57.300.wav    idoD    idoD    87  NaN     NaN     False
193  103_31_18.59.12.800.wav     idoD    idoD_0  99  HIT     VP  False
194  131_101_02.08.06.500.wav    idoD    idoD_0  96  HIT     VP  False

Что мне нужно сделать, это своего рода сложный подсчет. Мне нужно сгруппировать результаты с помощью динамика и рассчитать для каждого динамика некоторые вычисления. Затем я перейду к динамику, который сделал для меня лучший расчет, но прежде чем продолжить, мне нужно отметить все файлы, которые я использовал для вычисления, как используемые, т.е. Изменить значение isUsed для каждой строки, в которой они отображаются (файлы могут появляться более одного раза) до ИСТИНЫ. Затем я делаю еще одну итерацию. Вычислите для каждого динамика, отметьте используемые файлы и т.д., Пока больше не осталось больше динамиков.

Я много думал о том, как реализовать этот процесс с помощью pandas (его довольно легко реализовать на регулярном питоне, но для его выполнения потребуется много циклов и структурирования данных, что, по моему мнению, значительно замедлит процесс, а также Я использую этот процесс, чтобы более глубоко изучить способности pandas)

Я вышел со следующим решением. В качестве этапов подготовки, Ill группирует имя динамика и задает имя файла как индекс методом set_index. Затем я переберу группу groupObj и примените функцию вычисления, которая вернет выбранный динамик и файлы, которые будут помечены как используемые.

Затем Ill перебирайте файлы и помечайте их как используемые (это было бы быстрым и простым, поскольку я заранее задавал их как индексы) и так далее, пока не закончу вычисление.

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

Сначала я проиндексирован по имени файла, здесь нет проблем:

In [53]:

    marked_results['isUsed'] = False
    ind_res = marked_results.set_index('wave_path')
    ind_res.head()

Out[53]:
    spk_name    spk_example#    score   mark    comments    isUsed
    wave_path                       
    103_31_18.59.12.800.wav      idoD    idoD    99  HIT     VP  False
    131_101_02.08.06.500.wav     idoD    idoD    99  HIT     VP  False
    144_35_22.46.38.700.wav      idoD    idoD    96  HIT     VP  False
    41_09_17.10.11.700.wav       idoD    idoD    93  HIT     TEST    False
    122_188_03.19.20.400.wav     idoD    idoD    93  NaN     NaN     False

Затем я выбираю файл и проверяю, что получаю записи, относящиеся к этому файлу:

In [54]:

    example_file = ind_res.index[0];
    ind_res.ix[example_file]

Out[54]:
    spk_name    spk_example#    score   mark    comments    isUsed
    wave_path                       
    103_31_18.59.12.800.wav  idoD    idoD    99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_0  99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_1  97  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_2  95  HIT     VP  False

Теперь проблемы здесь тоже. Затем я попытался изменить значение isUsed для этого файла на True, и там, где у меня возникла проблема:

In [56]:

    ind_res.ix[example_file]['isUsed'] = True
    ind_res.ix[example_file].isUsed = True
    ind_res.ix[example_file]
Out[56]:
    spk_name    spk_example#    score   mark    comments    isUsed
    wave_path                       
    103_31_18.59.12.800.wav  idoD    idoD    99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_0  99  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_1  97  HIT     VP  False
    103_31_18.59.12.800.wav  idoD    idoD_2  95  HIT     VP  False

Итак, вы видите проблему. Ничего не изменилось. Что я делаю не так? Описанная выше проблема должна быть решена с помощью pandas?

А также: 1. Как я могу подойти к определенной группе с помощью объекта groupby? bcz Я думал, может быть, вместо того, чтобы устанавливать файлы как индексированные, группируя по файлу, и используя эту группу, чтобы obj применить изменяющуюся функцию ко всем ее вхождениям. Но я не нашел способ приблизиться к определенной группе и передать имя группы в качестве параметра и вызвать действие применительно ко всем группам, а затем действовать только на одном из них, казалось, не "правильно" для меня.

Надеюсь, это не долго...:)

Ответ 1

Индексирование объектов Panda может возвращать два принципиально разных объекта: вид или копию.

Если mask - основной срез, то df.ix[mask] возвращает вид df. Представления разделяют те же базовые данные, что и исходный объект (df). Таким образом, изменение вида также изменяет исходный объект.

Если mask является чем-то более сложным, например, произвольной последовательностью индексов, то df.ix[mask] возвращает копию некоторых строк в df. Изменение копии не влияет на оригинал.

В вашем случае, поскольку строки, которые имеют один и тот же wave_path, происходят в произвольных местах, ind_res.ix[example_file] возвращает копию. Так

ind_res.ix[example_file]['isUsed'] = True

не влияет на ind_res.

Вместо этого вы можете использовать

ind_res.ix[example_file, 'isUsed'] = True

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

Джефф уже предоставил ссылку в Pandas docs, в которой указано, что

Правила о том, когда возвращается представление данных, полностью зависит от NumPy.

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


Вот простой пример, который использует основной срез. Вид возвращается df.ix, поэтому модификация subdf также изменяет df:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.arange(12).reshape(4,3), 
         columns=list('ABC'), index=[0,1,2,3])

subdf = df.ix[0]
print(subdf.values)
# [0 1 2]
subdf.values[0] = 100
print(subdf)
# A    100
# B      1
# C      2
# Name: 0, dtype: int32

print(df)           # df is modified
#      A   B   C
# 0  100   1   2
# 1    3   4   5
# 2    6   7   8
# 3    9  10  11

Вот простой пример, который использует "причудливую индексацию" (произвольные строки выбраны). Копия возвращается df.ix. Поэтому изменение subdf не влияет на df.

df = pd.DataFrame(np.arange(12).reshape(4,3), 
         columns=list('ABC'), index=[0,1,0,3])

subdf = df.ix[0]
print(subdf.values)
# [[0 1 2]
#  [6 7 8]]

subdf.values[0] = 100
print(subdf)
#      A    B    C
# 0  100  100  100
# 0    6    7    8

print(df)          # df is NOT modified
#    A   B   C
# 0  0   1   2
# 1  3   4   5
# 0  6   7   8
# 3  9  10  11

Обратите внимание, что единственное различие между двумя примерами заключается в том, что в первом, где возвращается представление, индекс был [0,1,2,3], тогда как во втором, где копия возвращается, индекс был [0,1,0,3].

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


Несмотря на то, что набросал тонкость Pandas/NumPy, я действительно не думаю, что

ind_res.ix[example_file, 'isUsed'] = True

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

import pandas as pd
import numpy as np

df = pd.DataFrame(np.arange(12).reshape(4,3), 
                  columns=list('ABC'))
df['A'] = df['A']%2
print(df)
#    A   B   C
# 0  0   1   2
# 1  1   4   5
# 2  0   7   8
# 3  1  10  11

def calculation(grp):
    grp['C'] = True
    return grp

newdf = df.groupby('A').apply(calculation)
print(newdf)

что дает

   A   B     C
0  0   1  True
1  1   4  True
2  0   7  True
3  1  10  True