如何根据pandas中的多个条件匹配和计算行?

时间:2017-02-14 18:04:15

标签: python pandas dataframe

我目前正在研究如下所示的csv数据集(请参阅下面的测试版df):

enter image description here

这些基本上是移动设备位置记录。 “标识符”唯一标识移动设备,“from_city”和“to_city”是相应的出发和到达城市。因此,对于标识符“A1”,该人于1月2日离开渥太华前往伦敦(记录号2)并于1月3日返回(记录号5)。对于具有标识符A2,B1,B2,B3和B6的记录,由于没有伦敦到渥太华记录,它们将被视为无回报。

最终,我想要做的是找出所有出发和返回的比赛,并计算每个从一对城市对。例如:

从渥太华到伦敦:共计100次旅行,其中80次在3天内返回,10次在3天后返回,10次没有返回。

我想我需要使用Identifier和其他列在pandas中进行groupby。但问题是如何识别标识符组中的返回匹配?

基本上,标准应该是:

  • 相同的标识符
  • from_city和to_city在两个记录之间相反
  • 返回时间应晚于出发时间

另外,我如何嵌入3天内的标准?

提前感谢您的帮助!

以下是测试的数据框:

df = pd.DataFrame({
    'Identifier': ['A1', 'A2', 'A3', 'A1', 'A3', 'B1', 'C3', 'B2', 'B3', 'B6', 'C3'],
    'OS': ['iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone'],
    'from_city': ['ottawa', 'ottawa', 'ottawa', 'london', 'london', 'ottawa', 'ottawa', 'ottawa', 'ottawa', 'ottawa', 'london'],
    'to_city': ['london', 'london', 'london', 'ottawa', 'ottawa', 'london', 'london', 'london', 'london', 'london', 'ottawa'],
    'time': ['1/2/2017 21:00', '1/2/2017 20:00', '12/24/2016 13:00', '1/3/2017 21:00', '1/6/2017 0:00',
            '12/10/2016 17:00', '12/22/2016 21:00', '12/17/2016 23:00', '12/14/2016 19:00', '12/20/2016 18:00', '1/3/2017 0:00']
})

注意:上面img中第5行的日期应该是“1/3/2017”,这在上面的代码中是固定的。

3 个答案:

答案 0 :(得分:2)

# change the type of 'time' column to timestamp

df['timestamp']=pd.to_datetime(df['timestamp'], format='%m/%d/%Y %H:%M')

# first use merge to get leave time and back time

df = df.merge(df.set_index(['Identifier','from_city'])[['timestamp']], how='left', left_on=['Identifier','to_city'], right_index=True, suffixes=['_leave','_back'])

# filter out invalid leave date and back date

df = df.loc[~(df['timestamp_leave']>=df['timestamp_back'])]

# calculate travel time

df['duration'] = (df['timestamp_back'] - df['timestamp_leave']).dt.days

use pd.cut to groupby

df['group'] = pd.cut(df['duration'], [0,3,10,100])

返回值:

    Identifier  OS  from_city   timestamp_leave to_city timestamp_back  duration    group
1   A2  iphone  ottawa  2017-01-02 20:00:00 london  NaT NaN NaN
2   A3  iphone  ottawa  2016-12-24 13:00:00 london  2017-01-06 00:00:00 12.0    (10, 100]
3   A1  iphone  london  2016-01-03 21:00:00 ottawa  2017-01-02 21:00:00 365.0   NaN
5   B1  iphone  ottawa  2016-12-10 17:00:00 london  NaT NaN NaN
6   C3  iphone  ottawa  2016-12-22 21:00:00 london  2017-01-03 00:00:00 11.0    (10, 100]
7   B2  iphone  ottawa  2016-12-17 23:00:00 london  NaT NaN NaN
8   B3  iphone  ottawa  2016-12-14 19:00:00 london  NaT NaN NaN
9   B6  iphone  ottawa  2016-12-20 18:00:00 london  NaT NaN NaN

答案 1 :(得分:1)

如果每个标识符在数据集中只有一次往返,则此方法可行。另外,我将示例数据框中time列的第4个元素的年份更改为2017年。

首先将df['time']转换为日期时间。

duration = df.groupby('Identifier')['time'].apply(lambda x: max(list(x)) - min(list(x)))

然后在Identifier上使用groupby:

duration = df.groupby('Identifier')['time'].apply(lambda x: max(list(x)) - min(list(x)))

duration现在看起来像: A1 1 days 00:00:00 A2 0 days 00:00:00 A3 12 days 11:00:00 B1 0 days 00:00:00 B2 0 days 00:00:00 B3 0 days 00:00:00 B6 0 days 00:00:00 C3 11 days 03:00:00

现在选择大于0天但少于3天的行。

duration[(duration > pd.Timedelta(days=0)) & (duration <= pd.Timedelta(days=3))]

结果为: Identifier A1 1 days Name: time, dtype: timedelta64[ns]

答案 2 :(得分:1)

我终于想出办法来做到这一点:

def combine_cities(row):
    if row['from_city'] < row['to_city']:
        return row['from_city'] + ', ' + row['to_city']
    else:
        return row['to_city'] + ', ' + row['from_city']

df['cities'] = df.apply(combine_cities, axis=1)

def count_return(grp):
    if grp.nunique() == 1:
        return np.nan
    else:
        return 'return found'

df.groupby(['cities', 'Identifier'])['from_city'].apply(count_return).dropna()