2010-03-30 22 views
8

Câu hỏi của tôi không phải về một đoạn mã cụ thể nhưng tổng quát hơn, vì vậy hãy chịu theo tôi:python: kỹ thuật hiệu quả để xử lý dữ liệu lồng nhau sâu một cách linh hoạt là gì?

Tôi nên tổ chức dữ liệu như thế nào và sử dụng công cụ nào để quản lý?

Tôi đang sử dụng python và numpy để phân tích dữ liệu. Bởi vì tài liệu python chỉ ra rằng các từ điển rất được tối ưu hóa trong python, và cũng do thực tế là dữ liệu chính nó là rất có cấu trúc, tôi đã lưu nó trong một từ điển lồng nhau sâu sắc.

Đây là một bộ xương của các từ điển: vị trí trong hệ thống phân cấp xác định tính chất của nguyên tố này, và mỗi dòng mới xác định nội dung của một quan trọng trong mức tiền lệ:

[AS091209M02] [AS091209M01] [AS090901M06] ... 
[100113] [100211] [100128] [100121] 
[R16] [R17] [R03] [R15] [R05] [R04] [R07] ... 
[1263399103] ... 
[ImageSize] [FilePath] [Trials] [Depth] [Frames] [Responses] ... 
[N01] [N04] ... 
[Sequential] [Randomized] 
[Ch1] [Ch2] 

Edit: Để giải thích một chút tốt hơn thiết lập dữ liệu của tôi:

[individual] ex: [AS091209M02] 
[imaging session (date string)] ex: [100113] 
[Region imaged] ex: [R16] 
[timestamp of file] ex [1263399103] 
[properties of file] ex: [Responses] 
[regions of interest in image ] ex [N01] 
[format of data] ex [Sequential] 
[channel of acquisition: this key indexes an array of values] ex [Ch1] 

các loại hoạt động tôi thực hiện là ví dụ để tính toán tính chất của các mảng (được liệt kê dưới Ch1, CH2), nhặt mảng để thực hiện một bộ sưu tập mới, ví dụ phân tích phản ứng của N01 từ vùng 16 (R16) của một indi cụ thể vidual tại các thời điểm khác nhau, v.v.

Cấu trúc này hoạt động tốt cho tôi và rất nhanh, như đã hứa. Tôi có thể phân tích tập dữ liệu đầy đủ khá nhanh (và từ điển quá nhỏ để lấp đầy ram của máy tính của tôi: nửa buổi biểu diễn).

Sự cố của tôi xuất phát từ cách cồng kềnh mà tôi cần phải lập trình các hoạt động của từ điển. Tôi thường có những đoạn mã mà đi như thế này:

for mk in dic.keys(): 
    for rgk in dic[mk].keys(): 
     for nk in dic[mk][rgk].keys(): 
      for ik in dic[mk][rgk][nk].keys(): 
       for ek in dic[mk][rgk][nk][ik].keys(): 
        #do something 

đó là xấu xí, cồng kềnh, không thể tái sử dụng, và giòn (cần phải mã hóa lại nó cho bất kỳ biến thể của từ điển).

Tôi đã thử sử dụng các hàm đệ quy, nhưng ngoài các ứng dụng đơn giản nhất, tôi đã gặp phải một số lỗi rất khó chịu và các hành vi kỳ lạ gây ra lãng phí thời gian (không giúp tôi gỡ lỗi với pdb trong ipython khi tôi xử lý các hàm đệ quy lồng nhau sâu sắc). Cuối cùng chức năng đệ quy duy nhất mà tôi sử dụng thường xuyên như sau:

def dicExplorer(dic, depth = -1, stp = 0): 
    '''prints the hierarchy of a dictionary. 
    if depth not specified, will explore all the dictionary 
    ''' 
    if depth - stp == 0: return 
    try : list_keys = dic.keys() 
    except AttributeError: return 
    stp += 1 
    for key in list_keys: 
     else: print '+%s> [\'%s\']' %(stp * '---', key) 
     dicExplorer(dic[key], depth, stp) 

Tôi biết tôi đang làm điều này sai, bởi vì mã của tôi là dài, noodly và không thể tái sử dụng. Tôi cần phải sử dụng các kỹ thuật tốt hơn để thao tác linh hoạt các từ điển, hoặc để đưa dữ liệu vào một số định dạng cơ sở dữ liệu (sqlite?). Vấn đề của tôi là vì tôi (kém) tự học về lập trình, tôi thiếu kinh nghiệm thực tế và kiến ​​thức nền tảng để đánh giá cao các tùy chọn có sẵn. Tôi đã sẵn sàng để học các công cụ mới (SQL, lập trình hướng đối tượng), bất cứ điều gì cần để hoàn thành công việc, nhưng tôi miễn cưỡng đầu tư thời gian và nỗ lực vào thứ gì đó sẽ là một kết thúc chết cho nhu cầu của mình.

Vì vậy, các đề xuất của bạn để giải quyết vấn đề này là gì và có thể mã hóa công cụ của tôi theo cách ngắn gọn, linh hoạt và có thể sử dụng lại được không?

Phụ Lục: ngoài làm cái gì đó với một tiểu từ điển đặc biệt của từ điển dữ liệu, đây là một số ví dụ về các hoạt động tôi thực hiện cho dic bộ dữ liệu, hoặc một cuốn từ điển phụ của nó:

thực sự tôi có một số đệ quy chức năng hoạt động tốt:

def normalizeSeqDic(dic, norm_dic = {}, legend =()): 
    '''returns a normalized dictionary from a seq_amp_dic. Normalization is performed using the first time point as reference 
    ''' 
    try : 
     list_keys = dic.keys() 
     for key in list_keys: 
      next_legend = legend + (key,) 
      normalizeSeqDic(dic[key], norm_dic, next_legend) 
    except AttributeError: 
     # normalization 
     # unpack list 
     mk, ek, nk, tpk = legend 
     #assign values to amplitude dict 
     if mk not in norm_dic: norm_dic[mk] = {} 
     if ek not in norm_dic[mk]: norm_dic[mk][ek] = {} 
     if nk not in norm_dic[mk][ek]: norm_dic[mk][ek][nk] = {} 
     if tpk not in norm_dic[mk][ek][nk]: norm_dic[mk][ek][nk][tpk] = {} 
     new_array = [] 
     for x in range(dic.shape[0]): 
      new_array.append(dic[x][1:]/dic[x][0]) 
     new_array = asarray(new_array) 
     norm_dic[mk][ek][nk][tpk] = new_array 
    return norm_dic 

def poolDic(dic): 
    '''returns a dic in which all the values are pooled, and root (mk) keys are fused 
    these pooled dics can later be combined into another dic 
    ''' 
    pooled_dic = {} 
    for mk in dic.keys(): 
     for ek in dic[mk].keys(): 
      for nk in dic[mk][ek].keys(): 
       for tpk in dic[mk][ek][nk].keys(): 
        #assign values to amplitude dict 
        if ek not in pooled_dic: pooled_dic[ek] = {} 
        if nk not in pooled_dic[ek]: pooled_dic[ek][nk] = {} 
        if tpk not in pooled_dic[ek][nk]: 
         pooled_dic[ek][nk][tpk] = dic[mk][ek][nk][tpk] 
        else: pooled_dic[ek][nk][tpk]= vstack((pooled_dic[ek][nk][tpk], dic[mk][ek][nk][tpk])) 
    return pooled_dic 

def timePointsDic(dic): 
    '''Determines the timepoints for each individual key at root 
    ''' 
    tp_dic = {} 
    for mk in dic.keys(): 
     tp_list = [] 
     for rgk in dic[mk].keys(): 
      tp_list.extend(dic[mk][rgk]['Neuropil'].keys()) 
     tp_dic[mk]=tuple(sorted(list(set(tp_list)))) 
    return tp_dic 

đối với một số hoạt động tôi thấy không có cách nào khác ngoài việc làm phẳng các từ điển:

def flattenDic(dic, label): 
    '''flattens a dic to produce a list of of tuples containing keys and 'label' values 
    ''' 
    flat_list = [] 
    for mk in dic.keys(): 
     for rgk in dic[mk].keys(): 
      for nk in dic[mk][rgk].keys(): 
       for ik in dic[mk][rgk][nk].keys(): 
        for ek in dic[mk][rgk][nk][ik].keys(): 
         flat_list.append((mk, rgk, nk, ik, ek, dic[mk][rgk][nk][ik][ek][label]) 
    return flat_list 

def extractDataSequencePoints(flat_list, mk, nk, tp_list): 
     '''produces a list containing arrays of time point values 
     time_points is a list of the time points wished (can have 2 or 3 elements) 
     ''' 
     nb_tp = len(tp_list) 
     # build tp_seq list 
     tp_seq = [] 
     tp1, tp2, tp3 = [], [], [] 
     if nk == 'Neuropil': 
      tp1.extend(x for x in flat_list if x[0]==mk and x[2] == 'Neuropil' and x[3] == tp_list[0]) 
      tp2.extend(x for x in flat_list if x[0]==mk and x[2] == 'Neuropil'and x[3] == tp_list[1]) 
     else: 
      tp1.extend(x for x in flat_list if x[0]==mk and x[2] != 'Neuropil'and x[3] == tp_list[0]) 
      tp2.extend(x for x in flat_list if x[0]==mk and x[2] != 'Neuropil'and x[3] == tp_list[1]) 
     if nb_tp == 3: 
      if nk == 'Neuropil': 
       tp3.extend(x for x in flat_list if x[0]==mk and x[2] == 'Neuropil'and x[3] == tp_list[2]) 
      else: 
       tp3.extend(x for x in flat_list if x[0]==mk and x[2] != 'Neuropil'and x[3] == tp_list[2]) 
     for x in tp1: 
      for y in tp2: 
       if x[0:3] == y[0:3] : 
        if nb_tp == 3: 
         for z in tp3: 
          if x[0:3] == z[0:3] : 
           tp_seq.append(asarray([x[4],y[4],z[4]])) 
        else: 
         tp_seq.append(asarray([x[4],y[4]])) 
     return tp_seq 
+2

@AlexandreS: Tôi e rằng tôi không thực sự hiểu đủ về dữ liệu mẫu của bạn để có thể đưa ra nhiều lời khuyên. Bạn có thể xây dựng dữ liệu mà bạn đang phân tích và phân tích những gì bạn đang thực hiện? – MattH

+0

@MattH: Tôi đã chỉnh sửa câu hỏi để cung cấp thêm chi tiết. Hãy cho tôi biết nếu nó không đủ – AlexandreS

+0

@ AlexandrS: Cảm ơn bạn đã làm rõ. Nó có thể giúp thêm nếu bạn có thể giải thích cách dữ liệu này được mua/lưu trữ/xuất phát tại thời điểm này. Tôi nghĩ rằng con đường phía trước sẽ là để bạn tạo ra một sơ đồ trừu tượng của bạn được cấu trúc như các đối tượng với các thuộc tính và cách các đối tượng/thuộc tính liên quan đến nhau. Khi tôi quyết định làm thế nào để mã hóa một cấu trúc dữ liệu, tôi sẽ thường phác thảo ra những điều này. – MattH

Trả lời

11

"Tôi lưu trữ nó trong một cuốn từ điển lồng nhau sâu sắc"

Và, như bạn đã thấy, nó không hoạt động diễn ra tốt đẹp.

Giải pháp thay thế là gì?

  1. Phím tổng hợp và từ điển nông. Bạn có khóa gồm 8 phần: (phiên cá nhân, hình ảnh, Vùng được chụp, dấu thời gian của tệp, thuộc tính của tệp, vùng quan tâm trong hình ảnh, định dạng dữ liệu, kênh chuyển đổi), bản đồ tới một mảng giá trị.

    { ('AS091209M02', '100113', 'R16', '1263399103', 'Responses', 'N01', 'Sequential', 'Ch1'): array, 
    ... 
    

    Vấn đề với điều này là tìm kiếm.

  2. Cấu trúc lớp phù hợp. Trên thực tế, định nghĩa Lớp đầy đủ có thể quá mức cần thiết.

"Các loại hoạt động tôi thực hiện là ví dụ để tính toán tính chất của các mảng (liệt kê dưới Ch1, CH2), nhặt mảng để thực hiện một bộ sưu tập mới, ví dụ phân tích phản ứng của N01 từ vùng 16 (R16) của một cá nhân cụ thể tại các thời điểm khác nhau, v.v. "

Khuyến nghị

Thứ nhất, sử dụng một namedtuple cho đối tượng cuối cùng của bạn.

Array = namedtuple('Array', 'individual, session, region, timestamp, properties, roi, format, channel, data') 

Hoặc điều gì đó tương tự. Xây dựng một danh sách đơn giản của các đối tượng có tên tuple này. Bạn có thể chỉ đơn giản là lặp qua chúng.

Thứ hai, sử dụng nhiều thao tác giảm bản đồ đơn giản trên danh sách chính của đối tượng mảng này.

Filtering:

for a in theMasterArrrayList: 
    if a.region = 'R16' and interest = 'N01': 
     # do something on these items only. 

Giảm bởi chính thường gặp:

individual_dict = defaultdict(list) 
for a in theMasterArrayList: 
    individual_dict[ a.individual ].append(a) 

này sẽ tạo ra một tập hợp con trong bản đồ mà có chính xác các mục mà bạn muốn.

Sau đó, bạn có thể thực hiện theo chỉ định ['AS091209M02'] và có tất cả dữ liệu của chúng. Bạn có thể làm điều này cho bất kỳ (hoặc tất cả) của các phím có sẵn.

region_dict = defaultdict(list) 
for a in theMasterArrayList: 
    region_dict[ a.region ].append(a) 

Điều này không sao chép bất kỳ dữ liệu nào. Nó nhanh và tương đối nhỏ gọn trong bộ nhớ.

Mapping (hoặc biến đổi) các mảng:

for a in theMasterArrayList: 
    someTransformationFunction(a.data) 

Nếu mảng là chính nó một danh sách, bạn có thể cập nhật danh sách đó mà không vi phạm các tuple như một toàn thể. Nếu bạn cần tạo một mảng mới từ một mảng hiện có, bạn đang tạo một bộ tuple mới. Không có gì sai với điều này, nhưng nó là một tuple mới. Bạn kết thúc với các chương trình như thế này.

def region_filter(array_list, region_set): 
    for a in array_list: 
     if a.region in region_set: 
      yield a 

def array_map(array_list, someConstant): 
    for a in array_list: 
     yield Array(*(a[:8] + (someTranformation(a.data, someConstant),)) 

def some_result(array_list, region, someConstant): 
    for a in array_map(region_filter(array_list, region), someConstant): 
     yield a 

Bạn có thể xây dựng các phép biến đổi, cắt giảm, ánh xạ thành những thứ phức tạp hơn.

Điều quan trọng nhất là chỉ tạo từ điển bạn cần từ danh sách chính để bạn không thực hiện bất kỳ bộ lọc nào nhiều hơn mức tối thiểu cần thiết.

BTW. Điều này có thể được ánh xạ tới một cơ sở dữ liệu quan hệ một cách tầm thường. Nó sẽ chậm hơn, nhưng bạn có thể có nhiều hoạt động cập nhật đồng thời. Ngoại trừ nhiều bản cập nhật đồng thời, cơ sở dữ liệu quan hệ không cung cấp bất kỳ tính năng nào ở trên.

+0

Điều này rất hữu ích. Tôi đã dùng để làm phẳng từ điển dữ liệu hoặc (tập hợp con của nó) + danh sách để lọc dữ liệu, nhưng mô tả của bạn về việc sử dụng tuple có tên cho phép một cú pháp và mã sạch hơn nhiều so với những gì tôi có. Cộng với việc học tối thiểu để có được kết quả mong muốn, vì vậy tôi nghĩ tôi sẽ làm điều đó, ít nhất là vào lúc này. – AlexandreS

2

Bạn có thể làm cho vòng của bạn trông đẹp hơn, bằng cách thay thế:

for mk in dic.keys(): 
    for rgk in dic[mk].keys(): 
     for nk in dic[mk][rgk].keys(): 
      for ik in dic[mk][rgk][nk].keys(): 
       for ek in dic[mk][rgk][nk][ik].keys(): 
        #do something 

với

for mv in dic.values(): 
    for rgv in mv.values(): 
     for nv in rgv.values(): 
      for iv in nv.values(): 
       for ev in iv.values(): 
        #do something 

Do đó bạn truy cập vào tất cả các giá trị với một mã số tương đối ngắn gọn. Nếu bạn cũng cần một số phím, bạn có thể làm điều gì đó như:

for (mk, mv) in dic.items(): 
    # etc. 

Tùy thuộc vào nhu cầu của bạn, bạn cũng có thể xem xét việc tạo ra và sau đó sử dụng một bộ từ điển với các phím tuple:

dic[(mk, rgk, nv, ik, ek)] 
+1

Lặp lại đối tượng từ điển sẽ trả về các phím của từ điển. Bạn không thể lặp lại khóa và mong muốn truy cập vào giá trị. – MattH

+0

Tôi đã thử khối đầu tiên, nhưng tại cho rgv trong mv: Tôi thấy rgv lặp đi lặp lại thông qua chuỗi mv, thay vì mv.keys()? – AlexandreS

+0

@ McH: bạn nói đúng, xấu của tôi! Đã sửa. Cảm ơn! – EOL

0

Bạn đặt câu hỏi: Tôi nên tổ chức dữ liệu tôi đang phân tích như thế nào và tôi nên sử dụng công cụ nào để quản lý dữ liệu?

Tôi nghi ngờ rằng một từ điển, cho tất cả tối ưu hóa, không phải là câu trả lời đúng cho câu hỏi đó. Tôi nghĩ bạn nên sử dụng XML hoặc, nếu có một ràng buộc Python cho nó, HDF5, thậm chí là NetCDF. Hoặc, như bạn tự đề xuất, một cơ sở dữ liệu.

Nếu dự án của bạn có đủ thời gian và tính hữu ích để đảm bảo học cách sử dụng các công nghệ đó, tôi nghĩ bạn sẽ thấy rằng việc học chúng ngay bây giờ và nhận được cấu trúc dữ liệu đúng là một con đường tốt hơn để thành công hơn đấu vật sai cấu trúc dữ liệu cho toàn bộ dự án. Học XML, hoặc HDF5, hoặc SQL, hoặc bất cứ điều gì bạn chọn, là xây dựng kiến ​​thức chuyên môn chung của bạn và giúp bạn có thể giải quyết tốt hơn dự án tiếp theo. Gắn bó với các cấu trúc dữ liệu đặc biệt, có vấn đề và riêng biệt dẫn đến cùng một tập hợp các vấn đề trong thời gian tới.

+0

Cảm ơn bạn đã cho tôi biết về HDF5 và NetCDF. Python có các ràng buộc cho cả hai, và các giấy phép có thể sử dụng được. Tôi nghĩ rằng tôi sẽ bắt đầu bằng cách đọc về HDF5, và nếu nó trông đầy hứa hẹn, tôi sẽ nghiên cứu cách sử dụng nó với dữ liệu của tôi. Các loại con trỏ này chính xác là những gì tôi đã hy vọng khi tôi viết câu hỏi của mình. – AlexandreS

0

Bạn có thể viết một hàm máy phát điện, cho phép bạn duyệt qua tất cả các yếu tố của một mức độ nhất định:

def elementsAt(dic, level): 
    if not hasattr(dic, 'itervalues'): 
     return 
    for element in dic.itervalues(): 
     if level == 0: 
      yield element 
     else: 
      for subelement in elementsAt(element, level - 1): 
       yield subelement 

nào sau đó có thể được sử dụng như sau:

for element in elementsAt(dic, 4): 
    # Do something with element 

Nếu bạn cũng cần phải các phần tử lọc, trước tiên bạn có thể nhận được tất cả các phần tử cần được lọc (ví dụ: mức 'rgk'):

for rgk in getElementsAt(dic, 1): 
    if isValid(rgk): 
     for ek in getElementsAt(rgk, 2): 
      # Do something with ek 

Ít nhất điều đó sẽ làm cho nó dễ dàng hơn một chút để làm việc với một hệ thống phân cấp từ điển. Sử dụng nhiều tên mô tả hơn cũng sẽ hữu ích.

+0

cảm ơn đã cho tôi thấy việc sử dụng dic.itervalues ​​(), mà tôi đã không đánh giá cao cho đến nay. Vấn đề là nó không thực sự giải quyết vấn đề lồng sâu và không linh hoạt cho các chuỗi. – AlexandreS

1

Tôi sẽ chia sẻ một số suy nghĩ về điều này. Thay vì chức năng này:

for mk in dic.keys(): 
    for rgk in dic[mk].keys(): 
     for nk in dic[mk][rgk].keys(): 
      for ik in dic[mk][rgk][nk].keys(): 
       for ek in dic[mk][rgk][nk][ik].keys(): 
        #do something 

nào bạn muốn chỉ đơn giản là viết như:

for ek in deep_loop(dic): 
    do_something 

Có 2 cách. Một là chức năng, thứ hai là giống như máy phát điện. Câu hỏi thứ hai là:

def deep_loop(dic): 
    for mk in dic.keys(): 
     for rgk in dic[mk].keys(): 
      for nk in dic[mk][rgk].keys(): 
       for ik in dic[mk][rgk][nk].keys(): 
        for ek in dic[mk][rgk][nk][ik].keys(): 
         yield ek 

Điều này cho phép bạn nắm bắt được logic đi qua từ điển. Nó rất dễ dàng để sửa đổi chức năng này để hỗ trợ các cách khác nhau để đi qua cấu trúc. Nó phụ thuộc vào cách cấu trúc của bạn thay đổi, nếu nó chỉ là chiều sâu của vòng lặp hay cái gì đó khác. Bạn có thể đăng một số ví dụ nâng cao hơn về những yêu cầu về việc đi qua cây bạn có không? Giống như lọc, tìm kiếm, v.v ...? Độ sâu sẽ trông như thế này (chưa được kiểm tra) - nó sẽ mang lại một cặp (tuple phím), (giá trị):

def deep_loop(dic, depth): 
    if depth == 0: 
     yield(), dic 
    for subkey, subval in dic.items(): 
     for ktuple, value in deep_loop(subval, depth-1): 
      yield (subkey,)+ktuple, value 

Bây giờ nó được dễ dàng hơn:

for (k1,k2,k3,k4), value in deep_loop(dic, 4): 
    # do something 

Có nhiều cách khác để tùy chỉnh điều này, bạn có thể thêm một kiểu tuple được đặt tên làm tham số của deep_loop. Deep_loop có thể tự động phát hiện chiều sâu từ tuple có tên và trả về bộ tuple có tên.

+0

Tôi đăng thêm các ví dụ về những gì tôi quản lý để thực hiện như một phụ lục, hy vọng rằng sẽ giúp. Giải pháp đầu tiên của bạn gói quá trình tìm kiếm trong một hàm, nhưng không giải quyết được hầu hết các vấn đề của tôi, vì các tìm kiếm thường biến đổi và tôi sẽ phải xác định nhiều hàm để mô tả từng tìm kiếm. Tùy chọn thứ hai của bạn đề cập đến điểm này, nhưng sử dụng đệ quy, làm tôi sợ một chút sau những trải nghiệm xấu mà tôi đã có với nó (mặc dù tôi đã viết một số hàm đệ quy làm việc). Nhưng cảm ơn đã chỉ cho tôi cách bạn ghép đôi dic.items() với các biểu thức máy phát điện, điều đó rất hữu ích đối với tôi. – AlexandreS

Các vấn đề liên quan