【问题标题】:Re-assign column values in a pandas df重新分配熊猫 df 中的列值
【发布时间】:2019-03-14 19:30:14
【问题描述】:

此问题与排班或人员配备有关。我正在尝试将各种工作分配给个人(员工)。使用下面的df

`[Person]` = Individuals (employees)
`[Area]` and `[Place]` = unique jobs
`[On]` = How many unique jobs are occurring at each point in time

所以[Area][Place] 一起构成unique 值,它们是不同的工作。这些值将分配给个人,总体目标是使用尽可能少的个人。 assigned 对任何人来说最独特的值是 3。[On] 显示 [Place][Area] 的当前 unique 值有多少正在发生。因此,这为我需要多少人提供了具体指南。例如,

1-3 unique values occurring = 1 individual
4-6 unique values occurring = 2 individuals
7-9 unique values occurring = 3 individuals etc

问题: unique[Area][Place] 中的值的数量大于 3 给我带来了麻烦。我不能在 assign 第一个 3 unique valuesindividual 1 和接下来的 3 个 unique 值到 individual 2 等处执行 groupby 等。我想在 [Area] 和 @987654342 中对唯一值进行分组@@[Area]。因此,将assign 中的[Area] 中的相同值查看给个人(最多3 个)。然后,如果有 剩余 个值 (

我设想这项工作的方式是:展望未来hour。对于每个新的row 值,script 应该看到有多少值是[On](这表明需要多少个人)。如果unique 的值>3,则它们应该是assigned 乘以grouping[Area] 中的相同值。如果有 剩余 值,则无论如何都应该将它们组合成一组 3。

将其放入一个逐步的过程中:

1) 使用[On]Column 来确定@​​987654356@ 展望未来需要多少人

2) 如果出现超过 3 个 unique 值,请首先在 [Area] 中分配相同的值。

3) 如果有任何 leftover 值,那么无论如何都要寻找组合。

对于下面的 df[Place][Area] 出现 9 个 unique 值,并带有 hour。所以我们应该有3个人assigned。当unique 值>3 时,它应该由[Area] 分配,并查看是否出现相同的值。 剩余值应与具有少于 3 个unique 值的其他个体组合。

import pandas as pd
import numpy as np

d = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','08:40:00','08:42:00','08:45:00','08:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 4','House 5','House 1','House 2','House 3','House 2'],                 
    'Area' : ['A','B','C','D','E','D','E','F','G'],     
    'On' : ['1','2','3','4','5','6','7','8','9'], 
    'Person' : ['Person 1','Person 2','Person 3','Person 4','Person 5','Person 4','Person 5','Person 6','Person 7'],   
     })

df = pd.DataFrame(data=d)

这是我的尝试:

def reduce_df(df):
    values = df['Area'] + df['Place']
    df1 = df.loc[~values.duplicated(),:] # ignore duplicate values for this part..
    person_count = df1.groupby('Person')['Person'].agg('count')
    leftover_count = person_count[person_count < 3] # the 'leftovers'

    # try merging pairs together
    nleft = leftover_count.shape[0]
    to_try = np.arange(nleft - 1)
    to_merge = (leftover_count.values[to_try] + 
                leftover_count.values[to_try + 1]) <= 3
    to_merge[1:] = to_merge[1:] & ~to_merge[:-1]
    to_merge = to_try[to_merge]
    merge_dict = dict(zip(leftover_count.index.values[to_merge+1], 
                leftover_count.index.values[to_merge]))
    def change_person(p):
        if p in merge_dict.keys():
            return merge_dict[p]
        return p
    reduced_df = df.copy()
    # update df with the merges you found
    reduced_df['Person'] = reduced_df['Person'].apply(change_person)
    return reduced_df

df1 = (reduce_df(reduce_df(df)))

这是输出:

       Time    Place Area On    Person
0   8:03:00  House 1    A  1  Person 1
1   8:17:00  House 2    B  2  Person 1
2   8:20:00  House 3    C  3  Person 1
3   8:28:00  House 4    D  4  Person 4
4   8:35:00  House 5    E  5  Person 5
5   8:40:00  House 1    D  6  Person 4
6   8:42:00  House 2    E  7  Person 5
7   8:45:00  House 3    F  8  Person 5
8   8:50:00  House 2    G  9  Person 7

这是我的预期输出:

       Time    Place Area On    Person
0   8:03:00  House 1    A  1  Person 1
1   8:17:00  House 2    B  2  Person 1
2   8:20:00  House 3    C  3  Person 1
3   8:28:00  House 4    D  4  Person 2
4   8:35:00  House 5    E  5  Person 3
5   8:40:00  House 6    D  6  Person 2
6   8:42:00  House 2    E  7  Person 3
7   8:45:00  House 3    F  8  Person 2
8   8:50:00  House 2    G  9  Person 3

关于我希望如何获得此输出的说明:

Index 0: One `unique` value occurring. So `assign` to individual 1
Index 1: Two `unique` values occurring. So `assign` to individual 1
Index 2: Three `unique` values occurring. So `assign` to individual 1
Index 3: Four `unique` values on. So `assign` to individual 2
Index 4: Five `unique` values on. This one is a bit tricky and hard to conceptualise. But there is another `E` within an `hour`. So `assign` to a new individual so it can be combined with the other `E`
Index 5: Six `unique` values on. Should be `assigned` with the other `D`. So individual 2
Index 6: Seven `unique` values on. Should be `assigned` with other `E`. So individual 3
Index 7: Eight `unique` values on. New value in `[Area]`, which is a _leftover_. `Assign` to either individual 2 or 3
Index 8: Nine `unique` values on. New value in `[Area]`, which is a _leftover_. `Assign` to either individual 2 or 3

示例 2:

d = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','8:40:00','8:42:00','8:45:00','8:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 1','House 2','House 3','House 1','House 2','House 3'],                 
    'Area' : ['X','X','X','X','X','X','X','X','X'],     
    'On' : ['1','2','3','3','3','3','3','3','3'], 
    'Person' : ['Person 1','Person 1','Person 1','Person 1','Person 1','Person 1','Person 1','Person 1','Person 1'],   
    })

    df = pd.DataFrame(data=d)

我收到一个错误:

 IndexError: index 1 is out of bounds for axis 1 with size 1

在这一行:

df.loc[:,'Person'] = df['Person'].unique()[assignedPeople]

但是,如果我将 Person 更改为 1、2、3 重复,它会返回以下内容:

'Person' : ['Person 1','Person 2','Person 3','Person 1','Person 2','Person 3','Person 1','Person 2','Person 3'], 

      Time    Place Area On    Person
0  8:03:00  House 1    X  1  Person 1
1  8:17:00  House 2    X  2  Person 1
2  8:20:00  House 3    X  3  Person 1
3  8:28:00  House 1    X  3  Person 2
4  8:35:00  House 2    X  3  Person 2
5  8:40:00  House 3    X  3  Person 2
6  8:42:00  House 1    X  3  Person 3
7  8:45:00  House 2    X  3  Person 3
8  8:50:00  House 3    X  3  Person 3

预期输出:

      Time    Place Area On    Person
0  8:03:00  House 1    X  1  Person 1
1  8:17:00  House 2    X  2  Person 1
2  8:20:00  House 3    X  3  Person 1
3  8:28:00  House 1    X  3  Person 1
4  8:35:00  House 2    X  3  Person 1
5  8:40:00  House 3    X  3  Person 1
6  8:42:00  House 1    X  3  Person 1
7  8:45:00  House 2    X  3  Person 1
8  8:50:00  House 3    X  3  Person 1

示例 2 的主要内容是:

1) There are <3 unique values on so assign to individual 1

【问题讨论】:

  • 我对所需的输出感到困惑,为什么 Person 4 不是您所需输出的一部分?我认为问题的限制不是很清楚
  • @PeterJames123 你的输出看起来不错。根据需要,您总共有 3 个人。(Person1、Person2 和 Person4)。为什么你不能使用这个输出?如果此人的顺序很重要,您可以检查顺序并查看 Person3 缺失,然后您可以将 Person4 替换为 Person3 并继续......
  • 在您的问题中,您说值的唯一性取决于HourArea。同时您说您的输入d 有 9 个唯一值“在一小时内发生”,即使它是将 9 个值(观察值)分成多个小时和 7 个唯一区域?我不跟。您的问题中也有许多不同的解释(在评论部分)。我认为审查它们并选择一个对您是有益的。
  • 我也很难理解问题。我认为如果您提供一些背景信息可能会有所帮助。每一行数据代表什么?有哪些地方,有哪些人?这是关于为资源分配员工吗?
  • 我不怀疑我(和其他人)误解了您的标准,但也许这表明您的描述不清楚?我会接受@TomWojcik:s 的建议并重组你的问题。关注 1) 输入和 2) 预期输出。您现在显示了多个输入(根据我的计数为 3 个),这让事情更加混乱。尝试显示代表您的问题的一个输入和一个输出。

标签: python pandas numpy dataframe assign


【解决方案1】:

更新

There's a live version of this answer online that you can try for yourself.

这是allocatePeople 函数形式的答案。它基于预先计算区域在一小时内重复的所有索引:

from collections import Counter
import numpy as np
import pandas as pd

def getAssignedPeople(df, areasPerPerson):
    areas = df['Area'].values
    places = df['Place'].values
    times = pd.to_datetime(df['Time']).values
    maxPerson = np.ceil(areas.size / float(areasPerPerson)) - 1
    assignmentCount = Counter()
    assignedPeople = []
    assignedPlaces = {}
    heldPeople = {}
    heldAreas = {}
    holdAvailable = True
    person = 0

    # search for repeated areas. Mark them if the next repeat occurs within an hour
    ixrep = np.argmax(np.triu(areas.reshape(-1, 1)==areas, k=1), axis=1)
    holds = np.zeros(areas.size, dtype=bool)
    holds[ixrep.nonzero()] = (times[ixrep[ixrep.nonzero()]] - times[ixrep.nonzero()]) < np.timedelta64(1, 'h')

    for area,place,hold in zip(areas, places, holds):
        if (area, place) in assignedPlaces:
            # this unique (area, place) has already been assigned to someone
            assignedPeople.append(assignedPlaces[(area, place)])
            continue

        if assignmentCount[person] >= areasPerPerson:
            # the current person is already assigned to enough areas, move on to the next
            a = heldPeople.pop(person, None)
            heldAreas.pop(a, None)
            person += 1

        if area in heldAreas:
            # assign to the person held in this area
            p = heldAreas.pop(area)
            heldPeople.pop(p)
        else:
            # get the first non-held person. If we need to hold in this area, 
            # also make sure the person has at least 2 free assignment slots,
            # though if it's the last person assign to them anyway 
            p = person
            while p in heldPeople or (hold and holdAvailable and (areasPerPerson - assignmentCount[p] < 2)) and not p==maxPerson:
                p += 1

        assignmentCount.update([p])
        assignedPlaces[(area, place)] = p
        assignedPeople.append(p)

        if hold:
            if p==maxPerson:
                # mark that there are no more people available to perform holds
                holdAvailable = False

            # this area recurrs in an hour, mark that the person should be held here
            heldPeople[p] = area
            heldAreas[area] = p

    return assignedPeople

def allocatePeople(df, areasPerPerson=3):
    assignedPeople = getAssignedPeople(df, areasPerPerson=areasPerPerson)
    df = df.copy()
    df.loc[:,'Person'] = df['Person'].unique()[assignedPeople]
    return df

注意df['Person'].unique()allocatePeople 中的使用。这处理了人们在输入中重复的情况。假设输入中人员的顺序是分配这些人员的期望顺序。

我针对 OP 的示例输入(example1example2)测试了 allocatePeople,还针对我认为(?)与 OP 所需算法匹配的几个边缘案例进行了测试:

ds = dict(
example1 = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','08:40:00','08:42:00','08:45:00','08:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 4','House 5','House 1','House 2','House 3','House 2'],                 
    'Area' : ['A','B','C','D','E','D','E','F','G'],     
    'On' : ['1','2','3','4','5','6','7','8','9'], 
    'Person' : ['Person 1','Person 2','Person 3','Person 4','Person 5','Person 4','Person 5','Person 6','Person 7'],   
    }),
example2 = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','8:40:00','8:42:00','8:45:00','8:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 1','House 2','House 3','House 1','House 2','House 3'],                 
    'Area' : ['X','X','X','X','X','X','X','X','X'],     
    'On' : ['1','2','3','3','3','3','3','3','3'], 
    'Person' : ['Person 1','Person 1','Person 1','Person 1','Person 1','Person 1','Person 1','Person 1','Person 1'],   
    }),

long_repeats = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:25:00','8:30:00','8:31:00','8:35:00','8:45:00','8:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 4','House 1','House 1','House 2','House 3','House 2'],                 
    'Area' : ['A','A','A','A','B','C','C','C','B'],  
    'Person' : ['Person 1','Person 1','Person 1','Person 2','Person 3','Person 4','Person 4','Person 4','Person 3'],   
    'On' : ['1','2','3','4','5','6','7','8','9'],                      
    }),
many_repeats = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','08:40:00','08:42:00','08:45:00','08:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 4','House 1','House 1','House 2','House 1','House 2'],                 
    'Area' : ['A', 'B', 'C', 'D', 'D', 'E', 'E', 'F', 'F'],     
    'On' : ['1','2','3','4','5','6','7','8','9'], 
    'Person' : ['Person 1','Person 1','Person 1','Person 2','Person 3','Person 4','Person 3','Person 5','Person 6'],   
    }),
large_gap = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','08:40:00','08:42:00','08:45:00','08:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 4','House 1','House 1','House 2','House 1','House 3'],                 
    'Area' : ['A', 'B', 'C', 'D', 'E', 'F', 'D', 'D', 'D'],     
    'On' : ['1','2','3','4','5','6','7','8','9'], 
    'Person' : ['Person 1','Person 1','Person 1','Person 2','Person 3','Person 4','Person 3','Person 5','Person 6'],   
    }),
different_times = ({
    'Time' : ['8:03:00','8:17:00','8:20:00','8:28:00','8:35:00','08:40:00','09:42:00','09:45:00','09:50:00'],                 
    'Place' : ['House 1','House 2','House 3','House 4','House 1','House 1','House 2','House 1','House 1'],                 
    'Area' : ['A', 'B', 'C', 'D', 'D', 'E', 'E', 'F', 'G'],     
    'On' : ['1','2','3','4','5','6','7','8','9'], 
    'Person' : ['Person 1','Person 1','Person 1','Person 2','Person 3','Person 4','Person 3','Person 5','Person 6'],   
    })
)

expectedPeoples = dict(
    example1 = [1,1,1,2,3,2,3,2,3],
    example2 = [1,1,1,1,1,1,1,1,1],
    long_repeats = [1,1,1,2,2,3,3,3,2],
    many_repeats = [1,1,1,2,2,3,3,2,3],
    large_gap = [1,1,1,2,3,3,2,2,3],
    different_times = [1,1,1,2,2,2,3,3,3],
)

for name,d in ds.items():
    df = pd.DataFrame(d)
    expected = ['Person %d' % i for i in expectedPeoples[name]]
    ap = allocatePeople(df)

    print(name, ap, sep='\n', end='\n\n')
    np.testing.assert_array_equal(ap['Person'], expected)

assert_array_equal 语句通过,输出与 OP 的预期输出匹配:

example1
       Time    Place Area On    Person
0   8:03:00  House 1    A  1  Person 1
1   8:17:00  House 2    B  2  Person 1
2   8:20:00  House 3    C  3  Person 1
3   8:28:00  House 4    D  4  Person 2
4   8:35:00  House 5    E  5  Person 3
5  08:40:00  House 1    D  6  Person 2
6  08:42:00  House 2    E  7  Person 3
7  08:45:00  House 3    F  8  Person 2
8  08:50:00  House 2    G  9  Person 3

example2
      Time    Place Area On    Person
0  8:03:00  House 1    X  1  Person 1
1  8:17:00  House 2    X  2  Person 1
2  8:20:00  House 3    X  3  Person 1
3  8:28:00  House 1    X  3  Person 1
4  8:35:00  House 2    X  3  Person 1
5  8:40:00  House 3    X  3  Person 1
6  8:42:00  House 1    X  3  Person 1
7  8:45:00  House 2    X  3  Person 1
8  8:50:00  House 3    X  3  Person 1

我的测试用例的输出也符合我的期望:

long_repeats
      Time    Place Area    Person On
0  8:03:00  House 1    A  Person 1  1
1  8:17:00  House 2    A  Person 1  2
2  8:20:00  House 3    A  Person 1  3
3  8:25:00  House 4    A  Person 2  4
4  8:30:00  House 1    B  Person 2  5
5  8:31:00  House 1    C  Person 3  6
6  8:35:00  House 2    C  Person 3  7
7  8:45:00  House 3    C  Person 3  8
8  8:50:00  House 2    B  Person 2  9

many_repeats
       Time    Place Area On    Person
0   8:03:00  House 1    A  1  Person 1
1   8:17:00  House 2    B  2  Person 1
2   8:20:00  House 3    C  3  Person 1
3   8:28:00  House 4    D  4  Person 2
4   8:35:00  House 1    D  5  Person 2
5  08:40:00  House 1    E  6  Person 3
6  08:42:00  House 2    E  7  Person 3
7  08:45:00  House 1    F  8  Person 2
8  08:50:00  House 2    F  9  Person 3

large_gap
       Time    Place Area On    Person
0   8:03:00  House 1    A  1  Person 1
1   8:17:00  House 2    B  2  Person 1
2   8:20:00  House 3    C  3  Person 1
3   8:28:00  House 4    D  4  Person 2
4   8:35:00  House 1    E  5  Person 3
5  08:40:00  House 1    F  6  Person 3
6  08:42:00  House 2    D  7  Person 2
7  08:45:00  House 1    D  8  Person 2
8  08:50:00  House 3    D  9  Person 3

different_times
       Time    Place Area On    Person
0   8:03:00  House 1    A  1  Person 1
1   8:17:00  House 2    B  2  Person 1
2   8:20:00  House 3    C  3  Person 1
3   8:28:00  House 4    D  4  Person 2
4   8:35:00  House 1    D  5  Person 2
5  08:40:00  House 1    E  6  Person 2
6  09:42:00  House 2    E  7  Person 3
7  09:45:00  House 1    F  8  Person 3
8  09:50:00  House 1    G  9  Person 3

让我知道它是否能满足您的所有需求,或者它是否仍需要一些调整。我想每个人都渴望看到你实现你的愿景。

【讨论】:

  • @PeterJames123 澄清一点,如果输入中的区域是['A', 'B', 'C', 'D', 'E', 'D', 'F', 'D', 'G'],那么输出中的人的顺序应该是[1, 1, 1, 2, 2, 2, 3, 3, 3] 还是[1, 1, 1, 2, 3, 2, 3, 2, 3]
  • 感谢您尝试此操作。我明白这是一个困难的。如果他们在一个小时之内,那就是[1,1,1,2,3,2,3,2,3][Area] 中的第一个 3 项目将首先分组,所有 D's 将分组第二,leftovers 将分组第三。
  • 关于您的代码,它可以在几次迭代中运行,但对于出现的唯一值数量而言,它似乎分配的个体太少。我将在问题的底部附加另一个示例。感谢您再次尝试。
  • @PeterJames123 我发布了第二个版本,它的算法更符合您的想法,即提前一个小时看看是否有人应该留在原地(参见getAssignedPeople 函数中的holdSieve) .让我知道这个是否好。如果没有,请发布更多示例输入/输出数据,我们将再次迭代。
  • 它看起来非常接近@tel!我喜欢这个样子。刚刚发布了一个新的迭代。分配的个人总数是正确的。只需要细化Area的分配
【解决方案2】:

好的,在我们深入研究问题的逻辑之前,值得做一些整理数据并将其转化为更有用的格式:

#Create table of unique people
unique_people = df[['Person']].drop_duplicates().sort_values(['Person']).reset_index(drop=True)

#Reformat time column
df['Time'] = pd.to_datetime(df['Time'])

现在,了解问题的逻辑,将问题分解为多个阶段很有用。首先,我们将希望根据“区域”和它们之间的时间创建单个工作(带有工作编号)。即同一地区的工作,一个小时内可以共享相同的工作编号。

#Assign jobs
df= df.sort_values(['Area','Time']).reset_index(drop=True)
df['Job no'] = 0
current_job = 1   
df.loc[0,'Job no'] = current_job
for i in range(rows-1):
    prev_row = df.loc[i]
    row = df.loc[i+1]
    time_diff = (row['Time'] - prev_row['Time']).seconds //3600
    if (row['Area'] == prev_row['Area'])  & (time_diff == 0):
        pass
    else:
        current_job +=1
    df.loc[i+1,'Job no'] = current_job

现在这一步已经结束,将“人员”分配给各个工作是一件简单的事情:

df= df.sort_values(['Job no']).reset_index(drop=True)
df['Person'] = ""
df_groups = df.groupby('Job no')
for group in df_groups:
    group_size = group[1].count()['Time']
    for person_idx in range(len(unique_people)):
        person = unique_people.loc[person_idx]['Person']
        person_count = df[df['Person']==person]['Person'].count()
        if group_size <= (3-person_count):
            idx = group[1].index.values
            df.loc[idx,'Person'] = person
            break

最后,

df= df.sort_values(['Time']).reset_index(drop=True)
print(df)

我已尝试以一种更容易解开的方式对此进行编码,因此这里很可能会提高效率。然而,目的是阐明所使用的逻辑。

这段代码给出了两个数据集的预期结果,所以我希望它能回答你的问题。

【讨论】:

    【解决方案3】:

    在写我的other answer 时,我慢慢想到,使用专注于工作(可能不同)而不是人(这都是相同的)。这是一个使用以工作为中心的方法的解决方案:

    from collections import Counter
    import numpy as np
    import pandas as pd
    
    def assignJob(job, assignedix, areasPerPerson):
        for i in range(len(assignedix)):
            if (areasPerPerson - len(assignedix[i])) >= len(job):
                assignedix[i].extend(job)
                return True
        else:
            return False
    
    def allocatePeople(df, areasPerPerson=3):
        areas = df['Area'].values
        times = pd.to_datetime(df['Time']).values
        peopleUniq = df['Person'].unique()
        npeople = int(np.ceil(areas.size / float(areasPerPerson)))
    
        # search for repeated areas. Mark them if the next repeat occurs within an hour
        ixrep = np.argmax(np.triu(areas.reshape(-1, 1)==areas, k=1), axis=1)
        holds = np.zeros(areas.size, dtype=bool)
        holds[ixrep.nonzero()] = (times[ixrep[ixrep.nonzero()]] - times[ixrep.nonzero()]) < np.timedelta64(1, 'h')
    
        jobs =[]
        _jobdict = {}
        for i,(area,hold) in enumerate(zip(areas, holds)):
            if hold:
                _jobdict[area] = job = _jobdict.get(area, []) + [i]
                if len(job)==areasPerPerson:
                    jobs.append(_jobdict.pop(area))
            elif area in _jobdict:
                jobs.append(_jobdict.pop(area) + [i])
            else:
                jobs.append([i])
        jobs.sort()
    
        assignedix = [[] for i in range(npeople)]
        for job in jobs:
            if not assignJob(job, assignedix, areasPerPerson):
                # break the job up and try again
                for subjob in ([sj] for sj in job):
                    assignJob(subjob, assignedix, areasPerPerson)
    
        df = df.copy()
        for i,aix in enumerate(assignedix):
            df.loc[aix, 'Person'] = peopleUniq[i]
        return df
    

    这个版本的allocatePeople 也经过了广泛的测试,并通过了我在其他答案中描述的所有相同检查。

    它确实比我的其他解决方案有更多的循环,因此它的效率可能略低(尽管只有当你的数据框非常大时才重要,比如1e6 行及以上)。另一方面,它更短一些,而且我认为更简单易懂。

    【讨论】:

      猜你喜欢
      • 2019-04-23
      • 1970-01-01
      • 2016-11-16
      • 1970-01-01
      • 2018-09-25
      • 2021-02-19
      • 1970-01-01
      • 1970-01-01
      • 2017-11-02
      相关资源
      最近更新 更多