Функция локатора панд ссылается на другую строку при записи и чтении -> ValueError - PullRequest
0 голосов
/ 12 ноября 2018

При выполнении приведенного ниже примера кода я получаю

ValueError: cannot set using a multi-index selection indexer with a different
length than the value

Ошибка возникает при выполнении

df.loc[(9, 0), ("clouds", "type")] = np.array([None, None])

здесь:

~\Anaconda3\lib\site-packages\pandas\core\indexing.py in _setitem_with_indexer(self, indexer, value)
    492 
    493                     if len(obj[idx]) != len(value):
--> 494                         raise ValueError

Проблема, похоже, связана с записью массива numy в «ячейку» кадра данных. Кажется, что obj[idx] относится к индексу (20,) в кадре данных, тогда как он должен ссылаться на (9,0). За несколько итераций до той, которая выдает ошибку, при выполнении

df.loc[(6, 0), ("clouds", "type")] = np.array([None, None])

ошибка не возникает, так как совпадение obj[idx] относится к индексу (17,) в кадре данных, который имеет 2 субиндекса, так что случайно len(obj[idx])==len(value)==2.

Примечание:

Когда я читаю

df.loc[(9, 0), ("clouds", "type")].values

правильно возвращает [104].

Вопрос:

Я неправильно использую функцию .loc? Я делаю что-то еще не так? Или это проблема внутри панд? Как я мог избежать этого?

Я очень ценю любую помощь, поскольку проблема застряла на несколько дней: /

Код:

import pandas as pd
import numpy as np

mi = pd.MultiIndex(levels=[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22],
                           [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]],
                   labels=[[0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 12, 13, 14, 14,
                            14, 15, 16, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22],
                           [0, 1, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 2, 0, 0,
                            0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2, 0, 1, 2]])


mc = pd.MultiIndex(levels=[['clouds', 'group', 'header', 'vertical_visibility', 'visibility', 'weather', 'wind', 'windshear'],
                           ['', 'BR', 'DS', 'DU', 'DZ', 'FC', 'FG', 'FU', 'GR', 'GS', 'HZ', 'IC', 'PL', 'PO', 'PY', 'RA', 'SA', 'SG', 'SN', 'SQ', 'SS', 'UP', 'VA', 'altitude', 'ceiling', 'direction', 'form', 'from_date', 'from_hours', 'from_minutes', 'gust', 'icao_code', 'layer', 'more', 'origin_date', 'origin_hours', 'origin_minutes', 'probability', 'range', 'speed', 'till_date', 'till_hours', 'till_minutes', 'type', 'unit', 'valid_from_date', 'valid_from_hours', 'valid_till_date', 'valid_till_hours'],
                           ['bool', 'intensity', 'modifier']],
                   labels=[[0, 0, 0, 1, 1, 1],
                           [24, 32, 43, 27, 28, 29],
                           [-1, -1, -1, -1, -1, -1]])

arr = np.array(range(0,len(mi)*len(mc))).reshape(len(mi),len(mc))

df = pd.DataFrame(arr, index=mi, columns=mc)


values = {0: {0: [None]}, 1: {0: [None], 1: [None], 2: [None], 3: [None]}, 2: {0: [None], 2: [None]}, 3: {0: [None], 1: [None], 2: [None], 3: [None], 4: [None], 5: [None]}, 4: {0: [None]}, 6: {0: [None, None]}, 9: {0: [None, None]}}


for i, val in values.items():
    for j, v in val.items():
        df.loc[(i,j),("clouds", "type")] = np.array(v)

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Я думаю, вы должны либо:

  1. создать один столбец для каждого возможного облачного слоя (если важен порядок) или
  2. использовать битовую маску, например, столбец dtype 'u8', который имеет 64 бита, и вы можете установить столько типов облаков, сколько применимо к этой строке (если порядок не имеет значения).
0 голосов
/ 12 ноября 2018

Столбец ("clouds", "type", None) имеет целочисленный тип d:

In [28]: df[("clouds", "type", None)].dtype
Out[28]: dtype('int64')

Поэтому, если вы хотите назначить массивы NumPy этому столбцу, сначала измените dtype на object:

df[("clouds", "type", None)] = df[("clouds", "type", None)].astype('object')

  • Используйте df.at или df.iat, чтобы выбрать или присвоить значения определенным ячейкам DataFrame.
  • Используйте df.loc или df.iloc для выбора или назначения значений столбцам, строкам или подкадрам данных.

Поэтому используйте df.at здесь:

df[("clouds", "type", None)] = df[("clouds", "type", None)].astype('object')
for i, val in values.items():
    for j, v in val.items():
        df.at[(i, j), ("clouds", "type", None)] = np.array(v)

, который дает df, который выглядит как

      clouds                         group                        
     ceiling layer          type from_date from_hours from_minutes
         NaN   NaN           NaN       NaN        NaN          NaN
0  0       0     1        [None]         3          4            5
   1       6     7             8         9         10           11
1  0      12    13        [None]        15         16           17
   1      18    19        [None]        21         22           23
   2      24    25        [None]        27         28           29
   3      30    31        [None]        33         34           35
2  0      36    37        [None]        39         40           41
   1      42    43            44        45         46           47
   2      48    49        [None]        51         52           53
3  0      54    55        [None]        57         58           59
   1      60    61        [None]        63         64           65
   2      66    67        [None]        69         70           71
   3      72    73        [None]        75         76           77
   4      78    79        [None]        81         82           83
   5      84    85        [None]        87         88           89
4  0      90    91        [None]        93         94           95
5  0      96    97            98        99        100          101
6  0     102   103  [None, None]       105        106          107
7  0     108   109           110       111        112          113
8  0     114   115           116       117        118          119
9  0     120   121  [None, None]       123        124          125
...

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

Столбцы с категориальными данными должны содержать хешируемые значения. Как правило, не имеет смысла делать изменяемые объекты хэшируемыми. Так, например, изменяемые встроенные Python (такие как списки) или массивы NumPy не являются хэшируемыми. Но неизменяемые встроенные функции Python (такие как кортежи) являются хэшируемыми. Поэтому, если вы используете

df.at[(i, j), ("clouds", "type", None)] = tuple(v)

тогда вы можете сделать столбец ("clouds", "type", None) типа category dtype:

df[("clouds", "type", None)] = df[("clouds", "type", None)].astype('object')
for i, val in values.items():
    for j, v in val.items():
        df.at[(i, j), ("clouds", "type", None)] = tuple(v)

df[("clouds", "type", None)] = df[("clouds", "type", None)].astype('category')

Обратите внимание, что сначала необходимо создать столбец object dtype, чтобы он мог содержать объекты Python, такие как кортежи, а затем преобразовать в category dtype только после того, как все возможные значения были назначены.


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

Например,

6  0     102   103  'foo'       105        106          107
6  0     102   103  'bar'       105        106          107

вместо

6  0     102   103  ('foo', 'bar')       105        106          107

Одним из преимуществ использования нескольких строк является выбор всех строк с облаком / типом «foo» теперь легко:

df.loc[df[("clouds", "type", None)] == 'foo']

или для выбора всех строк с помощью foo или bar облако / тип:

df.loc[df[("clouds", "type", None)].isin(['foo', 'bar'])]

Если вы используете кортежи, вам придется использовать что-то вроде

df.loc[[any(kind in item for kind in ('foo', 'bar')) 
       for item in df[("clouds", "type", None)]]]

Обратите внимание, что это длиннее и труднее для чтения, но и медленнее.

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

Итак, в целом, используйте аккуратные данные , используйте несколько строк и сохраняйте простые типы данных DataFrame - используйте целые числа, плавающие всякий раз, когда это возможно, 'строки', если необходимо. Старайтесь не использовать кортежи, списки или массивы NumPy в качестве значений DataFrame.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...