18066


pandasで日付の処理をしており、該当する日付がなければ新しい行を加えるという処理をしようとしています。  
そこで次のようなコードを書きました。  
リスト内には存在するのにin演算子には引っかからないという現象が起きてしまい、原因がさっぱりわかりません。もしよろしければ原因を教えてください。

[code]

    def complement_date(self,pandas_arr):
        keys = pandas_arr.keys()
        dates = pandas_arr[keys[0]]# date info in first index
        dateidx = START# START = datetime.datitme(2013,8,1,0,0)
        while END>=dateidx:
            if not pd.to_datetime(dateidx) in dates:
                pdb.set_trace()
                tmp = pd.Series([dateidx,0],index=keys)
                pandas_arr = pandas_arr.append(tmp,ignore_index=True)
            dateidx = dateidx + DAY# DAY = datetime.timedelta(days=1)
        return pandas_arr
    
[/code]

[![デバッグ画面](https://i.stack.imgur.com/xVs9w.png)](https://i.stack.imgur.com/xVs9w.png)


CC BY-SA 3.0
2015-10-25T11:25:48.603
18056
2015-10-27T13:03:58.263
12962
question
python pandas
python pandas による日付処理


自己解決しました。

原因はいまだにわかりませんが  
dates = pandas_arr[keys[0]]# date info in first index  
の部分を  
dates = list(pandas_arr[keys[0]])  
としてやるとうまくいったようです。  
元のコードだとlist型ではなく1列のpandasのDataFrame型ができるだけのようですね  
動作原理がわかる方いらっしゃればご教授下さいm(_ _)m


CC BY-SA 3.0
2015-10-25T12:27:51.647
18057
2015-10-25T12:27:51.647
12962
18056
answer


`in` は `Index` との比較になるためです。

[code]

    import pandas as pd
    dates = pd.Series(pd.date_range('2011-01-01', '2011-01-05', freq='D'))
    dates
    # 0   2011-01-01
    # 1   2011-01-02
    # 2   2011-01-03
    # 3   2011-01-04
    # 4   2011-01-05
    # dtype: datetime64[ns]
    
    t = pd.Timestamp('2011-01-01')
    t == dates[0]
    # True
    
    t in dates
    # False
    
    0 in dates
    # True
    
[/code]

値に含まれるかは以下で確認できます。

[code]

    (t == dates).any()
    # True
    
[/code]

* * *

コメントに対する追記:

> t in dates.values #False の解釈

まず、`dates.values` は `numpy.ndarray` 型を返します。そのため、以下の記載は `pandas` ではなく `NumPy`
の挙動に関する説明になります。

[code]

    dates.values
    # array(['2011-01-01T09:00:00.000000000+0900',
    #        '2011-01-02T09:00:00.000000000+0900',
    #        '2011-01-03T09:00:00.000000000+0900',
    #        '2011-01-04T09:00:00.000000000+0900',
    #        '2011-01-05T09:00:00.000000000+0900'], dtype='datetime64[ns]')
    
    type(dates.values)
    # <type 'numpy.ndarray'>
    
[/code]

`numpy.ndarray` は `in` 演算子をサポートしています。`NumPy` の日時型である
`numpy.datetime64`を渡せば期待通り動きます。

[code]

    import numpy as np
    
    np.datetime64('2011-01-01T09:00:00.000000000+0900') in dates.values
    # True
    
[/code]

`pandas` の `Timestamp` 型から `numpy.datetime64` 型への変換は `asm8` プロパティで可能です。そのため、
`in` を使う場合は下のようになります。

[code]

    t.asm8
    # numpy.datetime64('2011-01-01T09:00:00.000000000+0900')
    
    t.asm8 in dates.values
    # True
    
[/code]


CC BY-SA 3.0
2015-10-25T23:39:53.160
18066
2015-10-26T11:29:29.103
2015-10-26T11:29:29.103
12966
12966
18056
answer


> 私の権限では 下の回答にコメントできなかったためこちらに追記します。 **contains** の処理を変更すると pandas
> の処理が壊れますので絶対に止めてください。影響範囲は調査する気にならないほど大きいです。※申し遅れましたが、私はpandas 開発チームのメンバーです。

何だか怒られてしまいましたので、`in` 演算子の override ではなく、新たに演算子を作ってみました。

# `dateidx in list(dates)` とすれば良いだけのことなのですが(苦笑)

まず、`Infix` クラスを定義して `|in_df|` 演算子を作ります。

[code]

    class Infix:
      def __init__(self, function):
        self.function = function
      def __call__(self, value1, value2):
        return self.function(value1, value2)
      def __or__(self, other):
        return self.function(other)
      def __ror__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    
    in_df = Infix(lambda item, obj: item in obj.tolist())
    
[/code]

実際に試してみます。

[code]

    > dateidx = datetime.datetime(2013,8,1,0,0)
    > dateidx
    datetime.datetime(2013, 8, 1, 0, 0)
    
    > dates = pd.Series(pd.date_range('2013-08-01', '2013-08-05', freq='D'))
    > dates
    0   2013-08-01
    1   2013-08-02
    2   2013-08-03
    3   2013-08-04
    4   2013-08-05
    dtype: datetime64[ns]
    
    > dateidx in dates
    False
    
    > dateidx |in_df| dates
    True
    
[/code]

~~  
`in` 演算子の実体である `__contains__`(special method)を再定義するという方法もあります。

[code]

    > dateidx = datetime.datetime(2013,8,1,0,0)
    > dateidx
    datetime.datetime(2013, 8, 1, 0, 0)
    
    > dates = pd.Series(pd.date_range('2013-08-01', '2013-08-05', freq='D'))
    > dates
    0   2013-08-01
    1   2013-08-02
    2   2013-08-03
    3   2013-08-04
    4   2013-08-05
    dtype: datetime64[ns]
    
    > dateidx in dates
    False
    
    > type(dates)
    pandas.core.series.Series
    
    > pd.core.series.Series.__contains__ = lambda self, item: item in self.tolist()
    > dateidx in dates
    True
    
[/code]

ただし、再定義をしてしまうと `pandas.core.series.Series` オブジェクトに対する `in` 演算子本来の使い方(data
frame の index を対象)ができなくなります。

[code]

    > 1 in dates
    False # 再定義していない場合は True となる
    
[/code]

~~


CC BY-SA 3.0
2015-10-27T02:03:55.243
18103
2015-10-27T13:03:58.263
2015-10-27T13:03:58.263
18056
answer