[:]とiloc [:]での割り当てがパンダで異なる結果をもたらすのはなぜですか?


13

私はilocパンダで使用しているさまざまなインデックス付け方法にとても混乱しています。

1-dデータフレームを2-dデータフレームに変換しようとしているとしましょう。まず、次の1-dデータフレームがあります

a_array = [1,2,3,4,5,6,7,8]
a_df = pd.DataFrame(a_array).T

そして、それをのサイズの2Dデータフレームに変換し2x4ます。2Dデータフレームを次のようにプリセットすることから始めます。

b_df = pd.DataFrame(columns=range(4),index=range(2))

次に、forループを使用して、次のコードでa_df(1-d)をb_df(2-d)に変換します

for i in range(2):
    b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]

それは私に次の結果を与えるだけです

     0    1    2    3
0    1    2    3    4
1  NaN  NaN  NaN  NaN

しかし、私がに変更b_df.iloc[i,:]したときb_df.iloc[i][:]。結果は次のように正しいです、これは私が欲しいものです

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

.iloc[i,:]との違いを誰かに説明してもらえますか、.iloc[i][:]なぜ.iloc[i][:]上記の私の例ではうまくいったのですか?.iloc[i,:]


これは奇妙です。b_df.iloc[1] = a_df.iloc[0, 4:8]indexのシリーズをindexのシリーズに割り当て[4, 5, 6, 7]ます[0, 1, 2, 3]。重複がないためNaN、すべての要素に割り当てられます。この時点まで、私には理にかなっています。しかし、あなたのように、なぜb_df.iloc[1][:] = ...動作が異なるのかははっきりしません。オブジェクトb_df.iloc[1]を検査しb_df.iloc[1][:]、インデックスの違いを明らかにしません。私の推測では、コピー([:])に直接割り当てることは、Pandasによって特別なケースとして扱われるため、割り当て先のインデックスが無視され、この矛盾が生じます。
セブ

それはインデックスが原因であると思います。最初の行は同じインデックスを持つため成功しました
Phung Duy Phong

1
パンダについて覚えておくべき重要なことは、「本質的なデータ配置」と呼ばれる概念を使用するパンダでのほとんどすべての操作です。つまり、パンダで行うほとんどすべての操作は、ステートメントの両側のインデックスを揃えます。ここでは、インデックス0を使用してインデックス1を設定しようとしています。その割り当ての右側にインデックス0がないため、パンダはナンを割り当てます。また、列ヘッダーもインデックスであることを忘れないでください。したがって、パンダは列ヘッダーを列ヘッダーに揃えます。
スコットボストン

3
次に、.iloc [i] [:]の使用はインデックスチェーンと呼ばれ、通常、パンダではかなり大きな「禁止」です。パンダがオブジェクトのビューを作成したり、メモリに新しいオブジェクトを作成したりすると、予期しない結果が生じる可能性があります。
スコットボストン

すべての有効な回答に賛成することを忘れないでください、そしてあなたが最も好きなものを受け入れてください。多分あなたはこれを知っていますが、これはコミュニティにどの回答が役に立ったかを知らせ、人々に彼らの時間と努力に対して報酬を与えることです;)これを参照してくださいmeta.stackexchange.com/questions/5234/とmeta.stackexchange.com/ question / 173399 /
alan.elkin

回答:


3

代入するとき、との間には非常に大きな違いがseries.iloc[:]ありseries[:]ます。(i)loc常に、割り当て元が割り当て対象のインデックスと一致することを確認します。一方、[:]構文は、インデックスのアライメントをバイパスして、基になるNumPy配列に割り当てます。

s = pd.Series(index=[0, 1, 2, 3], dtype='float')  
s                                                                          

0   NaN
1   NaN
2   NaN
3   NaN
dtype: float64

# Let's get a reference to the underlying array with `copy=False`
arr = s.to_numpy(copy=False) 
arr 
# array([nan, nan, nan, nan])

# Reassign using slicing syntax
s[:] = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])                 
s                                                                          

0    1
1    2
2    3
3    4
dtype: int64

arr 
# array([1., 2., 3., 4.]) # underlying array has changed

# Now, reassign again with `iloc`
s.iloc[:] = pd.Series([5, 6, 7, 8], index=[3, 4, 5, 6]) 
s                                                                          

0    NaN
1    NaN
2    NaN
3    5.0
dtype: float64

arr 
# array([1., 2., 3., 4.])  # `iloc` created a new array for the series
                           # during reassignment leaving this unchanged

s.to_numpy(copy=False)     # the new underlying array, for reference                                                   
# array([nan, nan, nan,  5.]) 

違いを理解したところで、コードで何が起こるか見てみましょう。ループのRHSを印刷して、何を割り当てているかを確認します。

for i in range(2): 
    print(a_df.iloc[0, i*4:(i+1)*4]) 

# output - first row                                                                   
0    1
1    2
2    3
3    4
Name: 0, dtype: int64
# second row. Notice the index is different
4    5
5    6
6    7
7    8
Name: 0, dtype: int64   

b_df.iloc[i, :]2番目の反復でに割り当てる場合、インデックスが異なるため、何も割り当てられず、NaNのみが表示されます。ただし、に変更 b_df.iloc[i, :]するb_df.iloc[i][:]と、基になるNumPy配列に割り当てられるため、インデックスの配置はバイパスされます。この操作は次のように表現されます

for i in range(2):
    b_df.iloc[i, :] = a_df.iloc[0, i*4:(i+1)*4].to_numpy()

b_df                                                                       

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

また、これが連鎖代入の一種であることもお勧めします。これは良いことではなく、コードが読みにくくなり、理解しにくくなります。


1
今私はそれを理解しています、ありがとう。バウンティを授与する前に、「[:]構文が基になるNumPy配列に割り当てる」というリファレンスを追加できますか?
セブ

@Sebこれは実装の詳細であるため、ドキュメントでこれに対する参照を実際に見つけることはできません。これを担当するGitHubのコードを見つける方が簡単かもしれませんが、最も簡単な方法は、何が起こるかを示すことです。回答の上部にある小さな例を編集して、さまざまな種類の再割り当て中に基になる配列がどのように操作されるかを示しました。物事がより明確になることを願っています!
cs95

どうもありがとうございます!今ではもっとはっきりしています。
Tommy Yip

0

違いは、最初のケースではPythonインタープリターがコードを次のように実行したことです。

b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__setitem__((i, slice(None)), value)

ここで、値は方程式の右辺になります。一方、2番目のケースでは、Pythonインタープリターがコードを次のように実行しました。

b_df.iloc[i][:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__getitem__(i).__setitem__(slice(None), value)

ここでも、値は方程式の右辺になります。

これら2つのケースのそれぞれで、キー(i、slice(None))とslice(None)の違いにより、setitem内で異なるメソッドが呼び出されます。したがって、動作は異なります。


b_df.iloc[i]b_df.iloc[i][:]同じインデックスを持っています。インデックスが一致しないシリーズを一方に割り当てることができ、もう一方に割り当てることができないのはなぜですか?
セブ

最初のケースでは_set_itemが呼び出され、2番目のケースではone_setitem_sliceが呼び出されます。我々は上記の動作を持っているこれらの方法の違いに起因するので、容疑者
MaPy

0

誰も私に何の違い説明できる.iloc[i,:].iloc[i][:]あるが

違い.iloc[i,:].iloc[i][:]

.iloc[i,:]特定の位置に直接アクセスしている場合は、th行のDataFrameすべての(:)列を選択しますi。私の知る限り、2番目の次元を指定しないでおくことと同じ.iloc[i]です()。

.iloc[i][:]あなたが2つの連鎖操作を実行している場合。したがって、の結果はの.iloc[i]影響を受け[:]ます。設定値にこれを使用するとパンダ自体によって推奨され、ここであなたがそれを使用するべきではありませんので、注意して:

設定操作でコピーまたは参照が返されるかどうかは、コンテキストによって異なります。これはチェーン割り当てと呼ばれることがあり、避ける必要があります


...そしてなぜ.iloc[i][:]上記の私の例ではうまくいったが、そうではなかったのか.iloc[i,:]

@ScottがOPのコメントで述べたように、データアライメントは組み込みです。したがって=、左側に存在しない場合、右側のインデックスは含まれません。これがNaN、2行目に値がある理由です。

だから、物事を明確にするために、次のようにすることができます:

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Reset the indices
    a_slice.reset_index(drop=True, inplace=True)
    # Set the slice into b_df
    b_df.iloc[i,:] = a_slice

それとも、に変換することができますlist代わりに使用しますreset_index

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Convert the slice into a list and set it into b_df
    b_df.iloc[i,:] = list(a_slice)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.