filu_
@filu_

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

なぜ出力される要素が1つ少ないのか

解決したいこと

Pythonとpandasライブラリを使用しています。

100件のレコードがあるDataFrameから、[10 : 100 : 100/4]でスライスしようとしています。

同じように要素を出力する3つの手段で値を出力させようとしたとき、
なぜかstep(100/4)の値によって、出力する要素数が1つ少なかったり、同じだったりします。
なぜなのか、気になりました。

追記(2021/02/05)

公式ドキュメント
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html
の、"Getting values on a DataFrame with an index that has integer labels" の項で、ちゃんと説明がありました
大変申し訳ありませんでした。

該当するソースコード

少ない時

import pandas as pd
import numpy as np

df1 = pd.DataFrame(np.arange(100))

print(df1)

df1 = df1[10:100]

step = len(df1) // 4

print(len(df1))
print(step)
print()

myrange = slice(0,len(df1),step)
print(len(df1.index.values[myrange]))
print(len(df1[0].values[myrange]))
print(len(df1.loc[myrange,[0]].values))
print()
print((df1.index.values[myrange]))
print((df1[0].values[myrange]))
print((df1.loc[myrange,[0]].values))
実行結果
     0
0    0
1    1
2    2
3    3
4    4
..  ..
95  95
96  96
97  97
98  98
99  99

[100 rows x 1 columns]
90
22

5
5
4 ← ここ

[10 32 54 76 98]
[10 32 54 76 98]
[[10]
 [32]
 [54]
 [76]]
同じ時
- step = len(def1) // 4
+ step = len(def1) // 5
import pandas as pd
import numpy as np

df1 = pd.DataFrame(np.arange(100))

print(df1)

df1 = df1[10:100]

step = len(df1) // 5

print(len(df1))
print(step)
print()

myrange = slice(0,len(df1),step)
print(len(df1.index.values[myrange]))
print(len(df1[0].values[myrange]))
print(len(df1.loc[myrange,[0]].values))
print()
print((df1.index.values[myrange]))
print((df1[0].values[myrange]))
print((df1.loc[myrange,[0]].values))
実行結果
     0
0    0
1    1
2    2
3    3
4    4
..  ..
95  95
96  96
97  97
98  98
99  99

[100 rows x 1 columns]
90
18

5
5
5 ← ここ

[10 28 46 64 82]
[10 28 46 64 82]
[[10]
 [28]
 [46]
 [64]
 [82]]
0

2Answer

要素数 90 は 4 で割り切れないからでは?

>>> 90 / 4
22.5
>>> 90 // 4
22
>>> 90 / 5
18.0
>>> 90 // 5
18
1Like

Comments

  1. @filu_

    Questioner

    ご回答ありがとうございます!

    ということは、locプロパティでのスライスの挙動とdf1やvaluesのに対してのスライスの挙動が異なる(切り捨てるのか、四捨五入か)ということですね

pandasもnumpyも知らないので、確証を持ってご説明できないことをお許しください。
試行の結果、下記のような推測に至りました。

推測

  • 課題の本質 (df1パート)
    • df1df1.locで、同じスライスオブジェクトを適用した際の結果が異なる」
  • 試行 (df2パート)
    • 「0~100を作ってから10~100を取り出す」のではなく、最初から「10~100を作る」と、df1df1.locで同じ結果になりました。
    • df1df2の違いは、行ラベルにあります。
    • df1[~]は行数でスライスされますが、df1.loc[]は行ラベルで選別されているようです。
      • 質問者様のコードの場合、ラベル90を超えた部分が出力されないため、ひとつ少なくなったわけです。
  • 試行 (df3パート)
    • .locとの相違を、より端的に表現したものです。
  • 追記
    • dfx.iloc[~]ならdfx[~]と同じになりました。

試行

code
import pandas as pd
import numpy as np

df0 = pd.DataFrame(np.arange(100)) # 0~100のデータフレーム

df1 = df0[10:100] # df0のスライス (10~100)
range4 = slice(0, len(df1), len(df1) // 4)
range5 = slice(0, len(df1), len(df1) // 5)
print(f"df1\n{df1}\n")
print(f"df1[range4] = \n{df1[range4]}\n")
print(f"df1.loc[range4, 0] = \n{df1.loc[range4, 0]}\n")
print(f"df1.iloc[range4, 0] = \n{df1.iloc[range4, 0]}\n")
print(f"df1[range5] = \n{df1[range5]}\n")
print(f"df1.loc[range5, 0] = \n{df1.loc[range5, 0]}\n")
print(f"df1.iloc[range5, 0] = \n{df1.iloc[range5, 0]}\n")

df2 = pd.DataFrame(np.arange(10, 100)) # df1と値は同じ(10~100)だが独自に作ったスライス
print(f"df2\n{df2}\n")
print(f"df2[range4] = \n{df2[range4]}\n")
print(f"df2.loc[range4, 0] = \n{df2.loc[range4, 0]}\n")
print(f"df2.iloc[range4, 0] = \n{df2.iloc[range4, 0]}\n")
print(f"df2[range5] = \n{df2[range5]}\n")
print(f"df2.loc[range5, 0] = \n{df2.loc[range5, 0]}\n")
print(f"df2.iloc[range5, 0] = \n{df2.iloc[range5, 0]}\n")

df3 = df0[90:100] # df0のスライス (90~100)
print(f"df3\n{df3}\n")
print(f"df3[slice(0, 10)] = \n{df3[slice(0, 10)]}\n") # df3のスライス (0~10)
print(f"df3.loc[slice(0, 10)] = \n{df3.loc[slice(0, 10), 0]}\n") # df3の位置 (0~10)
print(f"df3[slice(90, 100)] = \n{df3[slice(90, 100)]}\n") # df3のスライス (90~100)
print(f"df3.loc[slice(90, 100)] = \n{df3.loc[slice(90, 100), 0]}\n") # df3の位置 (90~100)
result
df1
     0
10  10
11  11
12  12
13  13
14  14
..  ..
95  95
96  96
97  97
98  98
99  99

[90 rows x 1 columns]

df1[range4] = 
     0
10  10
32  32
54  54
76  76
98  98

df1.loc[range4, 0] = 
10    10
32    32
54    54
76    76
Name: 0, dtype: int32

df1.iloc[range4, 0] = 
10    10
32    32
54    54
76    76
98    98
Name: 0, dtype: int32

df1[range5] = 
     0
10  10
28  28
46  46
64  64
82  82

df1.loc[range5, 0] = 
10    10
28    28
46    46
64    64
82    82
Name: 0, dtype: int32

df1.iloc[range5, 0] = 
10    10
28    28
46    46
64    64
82    82
Name: 0, dtype: int32

df2
     0
0   10
1   11
2   12
3   13
4   14
..  ..
85  95
86  96
87  97
88  98
89  99

[90 rows x 1 columns]

df2[range4] = 
     0
0   10
22  32
44  54
66  76
88  98

df2.loc[range4, 0] = 
0     10
22    32
44    54
66    76
88    98
Name: 0, dtype: int32

df2.iloc[range4, 0] = 
0     10
22    32
44    54
66    76
88    98
Name: 0, dtype: int32

df2[range5] = 
     0
0   10
18  28
36  46
54  64
72  82

df2.loc[range5, 0] = 
0     10
18    28
36    46
54    64
72    82
Name: 0, dtype: int32

df2.iloc[range5, 0] = 
0     10
18    28
36    46
54    64
72    82
Name: 0, dtype: int32

df3
     0
90  90
91  91
92  92
93  93
94  94
95  95
96  96
97  97
98  98
99  99

df3[slice(0, 10)] = 
     0
90  90
91  91
92  92
93  93
94  94
95  95
96  96
97  97
98  98
99  99

df3.loc[slice(0, 10)] = 
Series([], Name: 0, dtype: int32)

df3[slice(90, 100)] = 
Empty DataFrame
Columns: [0]
Index: []

df3.loc[slice(90, 100)] = 
90    90
91    91
92    92
93    93
94    94
95    95
96    96
97    97
98    98
99    99
Name: 0, dtype: int32
1Like

Comments

  1. @filu_

    Questioner

    @tetr4lab さん

    ご回答ありがとうございます!

    様々試行してくださってありがとうございます!

    要するに、
    dfに対してのスライスは、単純に箱の先頭から〇番目から×番目までの要素を取得することで、
    df.locで指定するスライスは、dfに入っているデータのindex、行ラベルと比較し、要素を取得してくる
    ということですね!

    df[90:100]が入っているdf3だと、df3.loc[90:100]でもちゃんと動作して、indexが90~99の要素を取得するんですね、なるほど!

    今、公式のドキュメントを見てみたのですが、
    https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html
    ここの"Getting values on a DataFrame with an index that has integer labels"
    という項で、説明がありました

    locは、指定の仕方はスライスの形式を取ってはいるけれど、スライスとはまた別な扱いなんですね
    先に公式ドキュメントを読まず、質問してしまい、大変申し訳ありませんでした。

    ただ、検証と解説がとても分かりやすく、私の中ですっと理解ができました!
    ありがとうございました!
  2. お役に立てたなら何よりです。

Your answer might help someone💌