pandas:用loc迭代DataFrame索引

时间:2014-12-16 09:47:19

标签: python pandas indexing

我似乎无法找到.loc行为背后的原因。我知道它是基于标签的,所以如果我遍历Index对象,下面的最小例子应该可行。但事实并非如此。我当然用Google搜索,但我需要一些已经掌握索引的人的其他解释。

import datetime
import pandas as pd

dict_weekday = {1: 'MON', 2: 'TUE', 3: 'WED', 4: 'THU', 5: 'FRI', 6: 'SAT', 7: 'SUN'}
df = pd.DataFrame(pd.date_range(datetime.date(2014, 1, 1), datetime.date(2014, 1, 15), freq='D'),   columns=['Date'])
df['Weekday'] = df['Date'].apply(lambda x: dict_weekday[x.isoweekday()])

for idx in df.index:
    print df.loc[idx, 'Weekday']

2 个答案:

答案 0 :(得分:10)

问题不在df.loc; df.loc[idx, 'Weekday']刚刚回归系列赛。 令人惊讶的行为是由于pd.Series尝试将类似日期时间的值转换为Timestamps的方式。

df.loc[0, 'Weekday']

形成系列

pd.Series(np.array([pd.Timestamp('2014-01-01 00:00:00'), 'WED'], dtype=object))

调用pd.Series(...)时,tries to cast the data为适当的dtype。

如果您浏览代码,您会发现它最终到达these lines in pandas.core.common._possibly_infer_to_datetimelike

sample = v[:min(3,len(v))]
inferred_type = lib.infer_dtype(sample)

正在检查数据的前几个元素并尝试推断dtype。 当其中一个值是pd.Timestamp时,Pandas会检查是否所有数据都可以转换为时间戳。实际上,'Wed'可以转换为pd.Timestamp:

In [138]: pd.Timestamp('Wed')
Out[138]: Timestamp('2014-12-17 00:00:00')

这是问题的根源,导致pd.Series返回 两个时间戳而不是时间戳和字符串:

In [139]: pd.Series(np.array([pd.Timestamp('2014-01-01 00:00:00'), 'WED'], dtype=object))
Out[139]: 
0   2014-01-01
1   2014-12-17
dtype: datetime64[ns]

因此返回

In [140]: df.loc[0, 'Weekday']
Out[140]: Timestamp('2014-12-17 00:00:00')

而不是'Wed'


替代方案:首先选择系列df['Weekday']

有许多变通方法; EdChum表明,向样本添加非日期(整数)值可以防止pd.Series将所有值转换为Timestamps。

或者,您可以使用df['Weekdays'] 之前访问.loc

for idx in df.index:
    print df['Weekday'].loc[idx]

替代方案:df.loc[[idx], 'Weekday']

另一种选择是

for idx in df.index:
    print df.loc[[idx], 'Weekday'].item()

df.loc[[idx], 'Weekday']首先选择 DataFrame df.loc[[idx]]。例如,当idx等于0时,

In [10]: df.loc[[0]]
Out[10]: 
        Date Weekday
0 2014-01-01     WED

df.loc[0]返回系列:

In [11]: df.loc[0]
Out[11]: 
Date      2014-01-01
Weekday   2014-12-17
Name: 0, dtype: datetime64[ns]

系列尝试将值转换为单个有用的dtype。 DataFrame可以为每列提供不同的dtype。因此,Date列中的时间戳不会影响Weekday列中值的dtype。

因此,使用返回DataFrame的索引选择器可以避免问题。


替代方法:在工作日使用整数

另一个替代方法是将isoweekday整数存储在Weekday中,并在打印时仅在结尾处转换为字符串:

import datetime
import pandas as pd

dict_weekday = {1: 'MON', 2: 'TUE', 3: 'WED', 4: 'THU', 5: 'FRI', 6: 'SAT', 7: 'SUN'}
df = pd.DataFrame(pd.date_range(datetime.date(2014, 1, 1), datetime.date(2014, 1, 15), freq='D'),   columns=['Date'])
df['Weekday'] = df['Date'].dt.weekday+1   # add 1 for isoweekday

for idx in df.index:
    print dict_weekday[df.loc[idx, 'Weekday']]

替代方法:使用df.ix

df.loc_LocIndexer,而df.ix_IXIndexer。他们有 不同的__getitem__方法。如果您单步执行代码(例如,使用pdb),您将找到df.ix calls df.getvalue

def __getitem__(self, key):
    if type(key) is tuple:
        try:
            values = self.obj.get_value(*key)

并且DataFrame方法df.get_value成功返回'WED'

In [14]: df.get_value(0, 'Weekday')
Out[14]: 'WED'

这就是为什么df.ix是另一种可行的选择。

答案 1 :(得分:0)

这对我来说似乎是一个错误,供参考我使用的是64位64位的pthon,pandas 0.15.1和numpy 1.9.1:

您的代码显示虽然它是以字符串形式打印,但dtype是时间戳:

In [56]:

df.iloc[0]['Weekday']
Out[56]:
Timestamp('2014-12-17 00:00:00')

如果我执行以下操作,那么它将保持为字符串:

In [58]:

df['Weekday'] = df['Date'].apply(lambda x: dict_weekday[x.isoweekday()])
df['WeekdayInt'] = df['Date'].map(lambda x: x.isoweekday())
df.iloc[0]['Weekday']
Out[58]:
'WED'

以上是奇怪的,因为我所做的只是添加第二列。

同样,如果我创建一个列来存储int day值,然后执行apply,那么它也可以工作:

In [60]:

df['WeekdayInt'] = df['Date'].map(lambda x: x.isoweekday())
df['Weekday'] = df['WeekdayInt'].apply(lambda x: dict_weekday[x])
df.iloc[0]['Weekday']
Out[60]:
'WED'

如果dtype是附加的第一列,看起来dtype以某种方式持久存在或未正确分配。