0
votes

Définir les entrées Pandas DataFrame par index booléen sur des valeurs de tuple

J'ai une trame de données

   a          b
0  1  (0, 0, 1)
1  1  (0, 0, 1)
2  2  (1, 1, 0)
3  1  (0, 0, 1)

(créé par d = pd.DataFrame({'a':[1,1,2,1], 'b':[(1,1,0)]*4}) ).

Je voudrais attribuer des valeurs de tuple aux entrées indexées par des valeurs booléennes, par exemple

   a             b
0  1  ((0, 0, 1),)
1  1  ((0, 0, 1),)
2  2     (1, 1, 0)
3  1  ((0, 0, 1),)

pour changer les valeurs des lignes 0,1,3 en (0,0,1) . Cela ne fonctionne pas et lève une ValueError: Must have equal len keys and value when setting with an ndarray . Notez que d.loc[d['a']==1, 'b'] = [((0,0,1),)]*3 ne renvoie pas d'erreur, mais le résultat est

d.loc[d['a']==1, 'b'] = [(0,0,1)] * 3

Comment obtenir le résultat

   a          b
0  1  (1, 1, 0)
1  1  (1, 1, 0)
2  2  (1, 1, 0)
3  1  (1, 1, 0)

utiliser l'indexation logique pour les lignes?


2 commentaires

Pourquoi utiliser des tuples au lieu de colonnes?


Dans ce cas, ce sont des valeurs RVB, qui peuvent également être des valeurs RVBA. Il semble plus naturel de stocker dans une cellule que de le rassembler dynamiquement à partir de plusieurs colonnes, en tenant compte des valeurs A potentiellement manquantes.


4 Réponses :


0
votes

Une façon de faire est de créer une série. Cependant, les indices doivent correspondre:

d.loc[d['a']==1, 'b'] = pd.Series([(0,0,1)]*len(d.loc[d['a']==1, 'b']), index=d.loc[d['a']==1, 'b'].index)

Cela semble un peu fastidieux et j'espère que quelqu'un d'autre publiera une meilleure solution.

(En utilisant le naïf d.loc[d['a']==1, 'b'] = pd.Series([(0,0,1)]*len(d.loc[d['a']==1, 'b'])) produit NaN dans la dernière ligne, car l'index 3 de la trame de données n'est pas satisfait par un index correspondant dans la série. Ceci: d.loc[d['a']==1, 'b'] = pd.Series([(0,0,1)]*len(d)) semble également fonctionner, mais semble terriblement inefficace surtout lorsque la plupart des conditions sont fausses.)


0 commentaires

2
votes

Voici une façon de faire:

d.loc[ixs, 'b'] = pd.Series(vals, index=ixs)

Pour les pandas> = 1.0, vous pouvez faire:

# set values
ixs = [0,1,3]
vals = [[(0,0,1)]*len(ixs)]

# replace values
d.loc[ixs,['b']] = vals

   a          b
0  1  (0, 0, 1)
1  1  (0, 0, 1)
2  2  (1, 1, 0)
3  1  (0, 0, 1)


3 commentaires

à votre santé. que la permutation des crochets ne m'était pas venue à l'esprit ...


Malheureusement, cette approche rompt avec Pandas 1.1.0


@zeeMonkeez J'ai mis à jour la solution avec une nouvelle approche.



1
votes

Enveloppez simplement le tuple dans la liste

d.loc[d['a']==1, 'b'] = [[(0, 0, 1)]]

Out[78]:
   a          b
0  1  (0, 0, 1)
1  1  (0, 0, 1)
2  2  (1, 1, 0)
3  1  (0, 0, 1)


3 commentaires

OK, c'est encore mieux. Quelle est la raison pour laquelle les doubles listes sont nécessaires?


Malheureusement, cette approche rompt avec Pandas 1.1.0


@zeeMonkeez: pandas 1.1.0+ ne permet pas d'attribuer avec une longueur incompatible comme dans ce cas. Une recarray de contournement consiste à attribuer à l'aide de recarray de numpy.