折叠/分组列表以汇总最大/最小的Pythonic方法

时间:2018-11-04 19:56:00

标签: python list aggregate list-comprehension

可以说我在python中有以下列表。首先按设备排序,然后按日期排序:

my_list = [
    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-01'},
    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-02'},
    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-03'},
    {'Equip': 'A-1', 'Job': 'Job 2', 'Date': '2018-01-04'},
    {'Equip': 'A-1', 'Job': 'Job 2', 'Date': '2018-01-05'},
    {'Equip': 'A-2', 'Job': 'Job 1', 'Date': '2018-01-03'},
    {'Equip': 'A-2', 'Job': 'Job 3', 'Date': '2018-01-04'},
    {'Equip': 'A-2', 'Job': 'Job 3', 'Date': '2018-01-05'}
]

我要做的是按给定的设备工作不改变的每个集合折叠列表,并获取设备在那里的第一个和最后一个日期。例如,这个简单的示例应更改为:

list_by_job = [
    {'Equip': 'A-1', 'Job': 'Job 1', 'First': '2018-01-01', 'Last': '2018-01-03'},
    {'Equip': 'A-1', 'Job': 'Job 2', 'First': '2018-01-04', 'Last': '2018-01-05'},
    {'Equip': 'A-2', 'Job': 'Job 1', 'First': '2018-01-03', 'Last': '2018-01-03'},
    {'Equip': 'A-2', 'Job': 'Job 3', 'First': '2018-01-04', 'Last': '2018-01-05'}
]

需要注意的几件事:

  1. A-2上的Job 1仅存在一天,因此其FirstLast日期应该相同。
  2. 一件设备可能正在工作,请离开该工作,然后再回来。在这种情况下,我需要在每次工作时都看到一个条目,而不仅仅是一个摘要。
  3. 如前所述,该列表已首先按Equip排序,然后按Date排序,以便可以进行排序。 (如果有更好的方法来完成此操作,我会很惊讶)

第3点,列表

my_list = [
    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-01'},
    {'Equip': 'A-1', 'Job': 'Job 2', 'Date': '2018-01-02'},
    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-03'}
]

应该屈服

    list_by_job = [
        {'Equip': 'A-1', 'Job': 'Job 1', 'First': '2018-01-01', 'Last': '2018-01-01'},
        {'Equip': 'A-2', 'Job': 'Job 2', 'First': '2018-01-02', 'Last': '2018-01-02'},
        {'Equip': 'A-1', 'Job': 'Job 1', 'First': '2018-01-03', 'Last': '2018-01-03'}
    ]

目前,我正在以一种简单的循环/非Python方式进行操作:

list_by_job = []

last_entry = None
for entry in my_list:
    if last_entry is None or last_entry['Equip'] != entry['Equip'] or last_entry['Job'] != entry['Job']:
      list_by_job.append({'Equip': entry['Equip'], 'Job': entry['Job'], 'First': entry['Date'], 'Last': entry['Date']})
    else:
      list_by_job[-1]['Last'] = entry['Date']
    last_entry = entry

使用Python的列表理解等方法是否还有更多的Python方式?

3 个答案:

答案 0 :(得分:12)

您可以使用string[] nameArray = new string[] { "Tyler", "Kyle", "Roger", "Rick" }; // Input. Console.WriteLine(String.Format("Select an item from the array using numbers {0}-{1}", 0, nameArray.Length)); string selectedIndexString = Console.ReadLine(); // Processing. int selectedIndex = Convert.ToInt32(selectedIndexString); if (selectedIndex < 0 || selectedIndex >= nameArray.Length) { throw new ArgumentException(String.Format("The index must belong to the range: [{0}:{1}]", 0, nameArray.Length)); } string selectedString = nameArray[selectedIndex]; // Output. Console.WriteLine(String.Format("You have choosen: {0}", selectedString));

itertools.groupby

输出:

import itertools
def _key(d):
  return (d['Equip'], d['Job'])

my_list = [{'Date': '2018-01-01', 'Equip': 'A-1', 'Job': 'Job 1'}, {'Date': '2018-01-02', 'Equip': 'A-1', 'Job': 'Job 1'}, {'Date': '2018-01-03', 'Equip': 'A-1', 'Job': 'Job 1'}, {'Date': '2018-01-04', 'Equip': 'A-1', 'Job': 'Job 2'}, {'Date': '2018-01-05', 'Equip': 'A-1', 'Job': 'Job 2'}, {'Date': '2018-01-03', 'Equip': 'A-2', 'Job': 'Job 1'}, {'Date': '2018-01-04', 'Equip': 'A-2', 'Job': 'Job 3'}, {'Date': '2018-01-05', 'Equip': 'A-2', 'Job': 'Job 3'}]
new_data = [[a, list(b)] for a, b in itertools.groupby(my_list, key=_key)]
final_result = [{"Equip":c, 'Job':d, 'First':b[0]['Date'], 'Last':b[-1]['Date']} for [c, d], b in new_data]

编辑:

使用注释中建议的数据:

[{'Equip': 'A-1', 'Job': 'Job 1', 'Last': '2018-01-03', 'First': '2018-01-01'}, 
 {'Equip': 'A-1', 'Job': 'Job 2', 'Last': '2018-01-05', 'First': '2018-01-04'}, 
 {'Equip': 'A-2', 'Job': 'Job 1', 'Last': '2018-01-03', 'First': '2018-01-03'}, 
 {'Equip': 'A-2', 'Job': 'Job 3', 'Last': '2018-01-05', 'First': '2018-01-04'}]

输出:

my_list = [{'Date': '2018-01-01', 'Equip': 'A-1', 'Job': 'Job 1'}, {'Date': '2018-01-02', 'Equip': 'A-1', 'Job': 'Job 2'}, {'Date': '2018-01-03', 'Equip': 'A-1', 'Job': 'Job 1'}, {'Date': '2018-01-04', 'Equip': 'A-1', 'Job': 'Job 2'}, {'Date': '2018-01-05', 'Equip': 'A-1', 'Job': 'Job 2'}, {'Date': '2018-01-03', 'Equip': 'A-2', 'Job': 'Job 1'}, {'Date': '2018-01-04', 'Equip': 'A-2', 'Job': 'Job 3'}, {'Date': '2018-01-05', 'Equip': 'A-2', 'Job': 'Job 3'}]

答案 1 :(得分:3)

我建议为此使用pandas

itertools.groupby很酷,但是IMO却很难理解。

>>> import pandas as pd
>>>
>>> my_list = [
...:    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-01'},
...:    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-02'},
...:    {'Equip': 'A-1', 'Job': 'Job 1', 'Date': '2018-01-03'},
...:    {'Equip': 'A-1', 'Job': 'Job 2', 'Date': '2018-01-04'},
...:    {'Equip': 'A-1', 'Job': 'Job 2', 'Date': '2018-01-05'},
...:    {'Equip': 'A-2', 'Job': 'Job 1', 'Date': '2018-01-03'},
...:    {'Equip': 'A-2', 'Job': 'Job 3', 'Date': '2018-01-04'},
...:    {'Equip': 'A-2', 'Job': 'Job 3', 'Date': '2018-01-05'}
...:]
>>>
>>> df = pd.DataFrame(my_list)
>>> df['Date'] = pd.to_datetime(df['Date'])
>>> groups = df.groupby(['Equip', 'Job']).agg({'Date': [min, max]}).reset_index()    
>>> groups.columns = ['Equip', 'Job', 'First', 'Last']
>>> groups
>>> 
  Equip    Job      First       Last
0   A-1  Job 1 2018-01-01 2018-01-03
1   A-1  Job 2 2018-01-04 2018-01-05
2   A-2  Job 1 2018-01-03 2018-01-03
3   A-2  Job 3 2018-01-04 2018-01-05
>>>
>>> groups.to_dict(orient='records')
>>> 
[{'Equip': 'A-1',
  'First': Timestamp('2018-01-01 00:00:00'),
  'Job': 'Job 1',
  'Last': Timestamp('2018-01-03 00:00:00')},
 {'Equip': 'A-1',
  'First': Timestamp('2018-01-04 00:00:00'),
  'Job': 'Job 2',
  'Last': Timestamp('2018-01-05 00:00:00')},
 {'Equip': 'A-2',
  'First': Timestamp('2018-01-03 00:00:00'),
  'Job': 'Job 1',
  'Last': Timestamp('2018-01-03 00:00:00')},
 {'Equip': 'A-2',
  'First': Timestamp('2018-01-04 00:00:00'),
  'Job': 'Job 3',
  'Last': Timestamp('2018-01-05 00:00:00')}]

我建议保留日期作为时间戳。

答案 2 :(得分:2)

您可以在此处使用pandas,这是某种用于数据的“数据库接口”:

import pandas as pd

df = pd.DataFrame(my_list)
df2 = df.groupby(['Equip', 'Job']).agg(['min', 'max']).rename(columns={'min': 'First', 'max': 'Last'})
df2.columns = df2.columns.droplevel()
df2 = df2.reset_index()
result = df2.to_dict('records')

对于给定的样本输入,得出:

>>> df2.to_dict('records')
[{'Equip': 'A-1', 'Job': 'Job 1', 'First': '2018-01-01', 'Last': '2018-01-03'},
 {'Equip': 'A-1', 'Job': 'Job 2', 'First': '2018-01-04', 'Last': '2018-01-05'},
 {'Equip': 'A-2', 'Job': 'Job 1', 'First': '2018-01-03', 'Last': '2018-01-03'},
 {'Equip': 'A-2', 'Job': 'Job 3', 'First': '2018-01-04', 'Last': '2018-01-05'}]

如果日期格式为 not '%Y-%m-%d',则首先需要使用pd.to_datetime(..)进行转换,例如:

import pandas as pd

df = pd.DataFrame(my_list)
df['Date'] = pd.to_datetime(df['Date'])
df2 = df.groupby(['Equip', 'Job']).agg(['min', 'max']).rename(columns={'min': 'First', 'max': 'Last'})
df2.columns = df2.columns.droplevel()
df2 = df2.reset_index()
result = df2.to_dict('records')