2012-04-11 2 views
2

생성자/반복기를 통해 액세스하는 많은 데이터 집합이 있습니다. 데이터 세트를 처리하는 동안 해당 데이터 세트의 레코드가 처리중인 현재 레코드의 속성과 동일한 값을 갖는 속성을 갖고 있는지 확인해야합니다. 이를 수행하는 한 가지 방법은 중첩 된 for 루프를 사용하는 것입니다.데이터 집합의 모든 요소를 ​​반복하면서 특정 키에 맞는 레코드에 대해 큰 데이터 집합을 필터링하는 방법

def fillStudentList(): 
    # TODO: Add some code here to filll 
    # a student list 
    pass 

students = fillStudentList() 
sameLastNames = list() 
for student1 in students1: 
    students2 = fillStudentList() 
    for student2 in students2: 
    if student1.lastName == student2.lastName: 
     sameLastNames.append((student1, student2)) 

코드 조각 위의 허락을 개선 할 수 꽤 예를 들어, 학생들의 데이터베이스를 처리한다면, 내가 좋아하는 뭔가를 할 수 있습니다. 스 니펫의 목표는 중첩 된 for 루프 패턴을 표시하는 것입니다.

이제 우리는 클래스 Student (반복자) 클래스와 메모리 효율적인 방법으로 데이터에 액세스 할 수있는 클래스 Source (다른 반복자)가 있다고 가정 해 보겠습니다.

아래에서이 코드의 모양을 보여줍니다. 누구든지이 구현을 개선하는 방법에 대한 아이디어가 있습니까? 목표는 필터링 된 집합을 처리 할 수 ​​있도록 동일한 속성을 가진 매우 큰 데이터 세트의 레코드를 찾을 수 있도록하는 것입니다.

#!/usr/bin/python 

from itertools import ifilter 

class Student(object): 
    """ 
    A class that represents the first name, last name, and 
    grade of a student. 
    """ 
    def __init__(self, firstName, lastName, grade='K'): 
     """ 
     Initializes a Student object 
     """ 
     self.firstName = firstName 
     self.lastName = lastName 
     self.grade = grade 

class Students(object): 
    """ 
    An iterator for a collection of students 
    """ 
    def __init__(self, source): 
     """ 
     """ 
     self._source = source 
     self._source_iter = source.get_iter() 
     self._reset = False 

    def __iter__(self): 
     return self 

    def next(self): 
     try: 
      if self._reset: 
       self._source_iter = self._source.get_iter() 
       self._reset = False 
      return self._source_iter.next() 
     except StopIteration: 
      self._reset = True 
      raise StopIteration 

    def select(self, attr, val): 
     """ 
     Return all of the Students with a given 
     attribute 
     """ 
     #select_iter = self._source.get_iter() 
     select_iter = self._source.filter(attr, val) 
     for selection in select_iter: 
      # if (getattr(selection, attr) == val): 
      # yield selection 
      yield(selection) 

class Source(object): 
    """ 
    A source of data that can provide an iterator to 
    all of the data or provide an iterator to the 
    data based on some attribute 
    """ 
    def __init__(self, data): 
     self._data = data 

    def get_iter(self): 
     """ 
     Return an iterator to the data 
     """ 
     return iter(self._data) 

    def filter(self, attr, val): 
     """ 
     Return an iterator to the data filtered by some 
     attribute 
     """ 
     return ifilter(lambda rec: getattr(rec, attr) == val, self._data) 

def test_it(): 
    """ 
    """ 
    studentList = [Student("James","Smith","6"), 
        Student("Jill","Jones","6"), 
        Student("Bill","Deep","5"), 
        Student("Bill","Sun","5")] 
    source = Source(studentList) 
    students = Students(source) 
    for student in students: 
     print student.firstName 

     for same_names in students.select('firstName', student.firstName): 
      if same_names.lastName == student.lastName: 
       continue 
      else: 
       print " %s %s in grade %s has your same first name" % \ 
       (same_names.firstName, same_names.lastName, same_names.grade) 

if __name__ == '__main__': 
    test_it() 
+0

는 데이터베이스와 달리 수동으로 수행하려는 이유라도 (이) N O (인) 당신을 위해 일 것인가? – jdi

+0

불행하게도, 데이터의 ** 소스 **는 단지 데이터베이스가 아닙니다. 그렇지 않으면, 나는 데이터베이스가 작업을하도록 할 것이다. 위에 구현 된 ** Students ** 클래스는 클라이언트에게 단일 데이터 소스에 대한 인상을주기 위해 ** Adapter ** 및 ** Source ** 클래스 ** Facade **가 이상적입니다. 그러나 실제로는 소스가 여러 스트림에서 데이터를 가져 오는 것입니다. –

답변

2

중첩 루프는 O (n ** 2)입니다. 당신은 대신 O (nlogn)의 성능을 위해 정렬 및 itertools.groupby 사용할 수 있습니다 일반적으로

students = fill_student_list() 
same_last_names = [list(group) for lastname, group in 
        groupby(sorted(students, key=operator.attrgetter('lastname'))] 

을, 당신은 데이터베이스에서 백업 ORM이 무엇을하려고 한 것으로 나타났습니다. 직접하기보다는 이미 많은 ORM 중 하나를 사용하십시오. 목록은 What are some good Python ORM solutions?을 참조하십시오. 그들은 스스로를 코드화하는 것보다 더 최적화되고 강력해질 것입니다.

+0

개체 관계 매핑 (ORM) 라이브러리가 아마도 최선의 방법이라고 생각합니다. 나는 ORM에 의해 생성 된 객체에 어댑터를 제공하여 다른 소스에서 생성 된 속성을 추가 할 수 있지만, 지금은 내가 지금하고있는 것과 너무 다르지 않다. 어떤 유형의 그래프를 사용하고 재귀 적 알고리즘을 사용하여 주어진 속성의 모든 복제본을 찾는 것이 좋습니다. 지금은 플래그를 만들고 중복 ID를 캐시 할 수있는 방식으로 데이터를 구조화하려고합니다. 그렇게하면 초기 처리 후에 더 작은 세트를 반복 할 수 있습니다. –

1

아마도 이런 일이

from collections import defaultdict 
students = fillStudentList() 
sameLastNames = defaultdict(list) 
for student in students: 
    sameLastNames[student.lastName].append(student) 

sameLastNames = {k:v for k,v in sameLastNames.iteritems() if len(v)>1} 
+0

원하는 모든 것이 lastName 속성을 색인하기 만하면됩니다. 그리고 그는 기록이 바뀔 때마다 색인 생성을 계속해야 할 것입니다. 하지만 그는 어떤 attr에 대해서도 "쿼리"할 수 있기를 원합니까? 그리고 그는 다중 인덱스를 만들어야 만합니다. 그리고 나서 ... 데이터베이스를 사용하십시오 :-) – jdi

+0

@jdi, 데이터베이스와 함께 효율적으로 작업하기 위해서는 관심있는 속성에 대한 인덱스를 만들어야합니다. .하지만 네, db 쿼리는 아마도 이것을 처리 할 수 ​​있습니다. 수만 개의 레코드가 있다면, 여분의 인덱스가 없다는 것을 알지 못할 수도 있습니다 :) –

+0

글쎄, 나중에 db 하지만이 간단한 파이썬 접근 방식보다 훨씬 효율적이며 항상 기록과 동기화됩니다. 점점 더 순수 Python 접근 방식을 시도하고 조정할수록 휠을 재발 명하고있는 것처럼 보입니다. – jdi

관련 문제