#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0

    Request: Efficient procedure to manually loop between two lists in python


    Hello everyone,

    I feel really silly asking this but I have been struggling for a couple of days on how to improve my code and I cannot discern the obvious way to improve the actual design which is rather inefficient and ugly despite its supposed simplicity...

    I need to move across a couple of lists using key events.


    For simplicity's sake let's say that the first list is “Shops” and the second one is “Products”

    Shops = [ShopA,ShopB,ShopC,ShopcD,ShopE]

    Products = [Oranges, Milk, Water, Chocolate, Rice]


    Let's say that with the keyboards keys “1” and “2” I want to move forward and backward respectively along the “Shops” list. On the other hand, I want to use keys “3” and “4” to move forward and backward respectively on the “Products” list.

    Now, the method I have consists in a couple of counters: The “ShopCounter” and the “ProductsCounter”. Incidentally, each time I move along the “Shops” list I set the “ProductsCounter” to zero so it can start at the checking at the first item (in this example that would be the oranges

    My problem lies now in the location/design of the counters. I have four sections in this code: One for each key instruction I want as and input (Next/Previous Shop; Next/Previous Product).

    Since python starts the lists at zero the obvious reason would be to place the counters at the end of each code section. However, if I do this I must press twice each key a backward/forward key if the previous command was a forward/backward. Since it preserves the increased counter from the previous command

    If I put the counters at the beginning of each section (which makes each section more... easier to understand...) I have to set an “if” check to make sure the code does not overcome the first items on the list. Also following this design, I must check an additional if check to make sure it does not try to find an item beyond the list length...

    Any advice on the right way to handle these kind of operations?? It would be most welcome.
  2. #2
  3. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,905
    Rep Power
    481

    Befriend modular division


    Not knowing what you did, nor why you think some code should be at a particular end, it's hard to say. You can avoid "if" statements using modular division. It's the remainder following division by an integral factor of the divisor. Here I've designed a subclass of list that maintains its own index. It computes its index based on an offset to its current value mod the list length. Hook functions help with your incidentals.
    Code:
    class MyList(list):
    
        '''
            To use these doctests command
            ShellPrompt> python -m doctest thisfile.py
    
            A MyList object maintains its own index.  The pred, curr,
            succ, and offset functions alter the index by -1, 0, 1, and
            arbitrary amount respectively and return the item at that
            index.  Furthermore, these methods as well as .reset() invoke
            the reset_hook functions.
    
            >>> #make Products first so its reset method is available as a hook.
            >>> Products = MyList((),
            ...     'Oranges, Milk, Water, Chocolate, Rice'.split(', '))
            >>>
            >>> Shops = MyList((Products.reset,),
            ...     'ShopA,ShopB,ShopC,ShopcD,ShopE'.split(','))
            >>> print(Products.succ())
            Milk
            >>> print(Products.curr())
            Milk
            >>> print(Shops.pred())
            ShopE
            >>> print(Products.curr())
            Oranges
            >>> len(Products)
            5
            >>> Shops[-2]
            'ShopcD'
            >>> print(Products.offset(-702))
            Chocolate
            >>> print(Products.succ())
            Rice
        '''
    
        def __init__(self, reset_hooks = [], *args, **kwargs):
            list.__init__(self, *args, **kwargs)
            self.reset_hooks = reset_hooks
            self.reset()
    
        def reset(self, offset = None):
            for f in self.reset_hooks:
                f()
            if offset is None:
                self.i = 0
            else:
                L = len(self)
                self.i = (((self.i + offset) % L) + L) % L
    
        def offset(self, offset):
            self.reset(offset)
            return self[self.i]
    
        def pred(self):
            return self.offset(-1)
    
        def succ(self):
            return self.offset( 1)
    
        def curr(self):
            return self.offset( 0)
    In your program you'd connect the Shops.pred/Shops.succ/Products.pred/Products.succ methods in some order to your detection of the 1234 key depresssion.
    Last edited by b49P23TIvg; September 3rd, 2013 at 09:14 PM.
    [code]Code tags[/code] are essential for python code and Makefiles!
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0
    hello b49P23TIvg! Thank you very much for your reply. You always give me good advice.

    You are right I did not explain my problem deeply enough. I did not want to confuse the simplicity of my question with the work I want do: In my code I open scientific data files which I need to plot (these would be the shops in my example). I use the 1 and 2 keys to move forward/backward along the files list. On the other hand, I use the 3 and 4 keys to move forwards/backwards the different regions of these datafiles (These would be the products). Still the mechanics should be the same. I have written a version of this example using my innefficient code structure:

    Code:
    import matplotlib.pyplot as plt
    
    def CounterManager(Key, ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter):
        
        if Key == "1" or Key == "2":
        
            ProductsCounter = 0
        
            if ShopsCounter == 0 and ShopsStarterCheck == True:
                ShopsStarterCheck = False
                print "eo"
                
            elif Key == "1" :        
                ShopsCounter =  ShopsCounter + 1        
            elif Key == "2" :        
                ShopsCounter =  ShopsCounter - 1
            
            if  ShopsCounter <= 0:
                ShopsCounter = 0      
            if  ShopsCounter >= ShopNumber - 1:
                ShopsCounter = ShopNumber - 1
    
            ProductsStarterCheck = True
            print "--Shop: " + str(ShopsCounter)
                
        if Key == "3" or Key == "4":
            
            if ProductsCounter == 0 and ProductsStarterCheck == True:
                ProductsStarterCheck = False           
            elif Key == "3" :        
                ProductsCounter =  ProductsCounter + 1
            elif Key == "4" :        
                ProductsCounter =  ProductsCounter - 1          
            
            if  ProductsCounter <= 0:
                ProductsCounter = 0
            if  ProductsCounter >= ProductsNumber - 1:
                ProductsCounter = ProductsNumber - 1
            print "--Product: " + str(ProductsCounter)
                
        return ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter
    
    def Key_Manager(event):
        
        global ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter
    
        if event.key == '1' or event.key == '2':
            ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter = CounterManager(event.key, ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter)
            print Shops[ShopsCounter]
    
        
        if event.key == '3' or event.key == '4':
            ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter = CounterManager(event.key, ShopNumber, ProductsNumber, ShopsStarterCheck, ProductsStarterCheck, ShopsCounter, ProductsCounter)
            print Products[ProductsCounter]
            
    Shops = ["ShopA","ShopB","ShopC","ShopD","ShopE"]
    Products = ["Oranges", "Milk", "Water", "Chocolate", "Rice"]
    
    ShopNumber, ProductsNumber = len(Shops), len(Products)
    ShopsStarterCheck,ProductsStarterCheck = True,True
    ShopsCounter,ProductsCounter, = 0,0
    
    Fig1=plt.figure(figsize=(10,5))
    Axis1 = Fig1.add_subplot(111)
    Fig_Connection = {}
    Fig_Connection['cid'] = plt.gcf().canvas.mpl_connect('key_press_event',Key_Manager)     #Create figure an connect the event to it
    
    plt.show()
    These data files have to me analyzed from graphical form (that's why I use matplotlib although in this code it is useless).

    I love your idea of individual methods to set the forward and backwards actions (Although I still to work on your example... I just learned what are the args and kargs )

    However I still would like to ask your opinion on how to "remove" the boolean I implemented in my code... I need them to make sure the program starts at the first element in each iteration...

    Thanks again for your time

    P.S. You might be wondering why I implemented the Number of products/shops in the Counter manager instead of just importing lists: In muy code the number of "products" does not remain constant, it must be calculate from each shop definition.
  6. #4
  7. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,905
    Rep Power
    481
    Sorry, my eyes have glazed over this final post for several days now. You have a program that works? Awesome.
    If you're slinging 500 files each with 500 rows of 10 thousand items then a bit of possibly mildly inferior logic at the top level---as long as it's correct logic not causing extra data loading---won't matter a speck.

    Where you have a second if statement could be elif because if the key is '1' or '2' it cannot be '2' or '3'. You won't be able to find this in any timing you do. You can replace x=='this' or x=='that' with x in ('this', 'that') but in your case they're single characters, so you could merely go with
    x in '12'
    Shorter to type but I think you won't find a timing difference. Now, if there were a huge list the underlying code for sets will perform much faster than a linear search. That is:
    i in list(range(10000)) # terrible
    i in set(range(10000)) # terrific
    please pretend that this example is designed for expediency because obviously here you could use
    isinstance(i,int) and 0<=i<10000
    A dictionary would provide a better example. Your case has just 2 characters. Linear string search probably way faster than a set of 2 items.

    Again, sorry. I'm not going to stare at it while daydreaming any longer.
    [code]Code tags[/code] are essential for python code and Makefiles!
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0
    Thanks again for the reply. It works for my purpose (moving along a list of galactic spectra, an within these plots move along its features) Also I managed to hide this "ugly" coding in a single method.

    I just would like to know a way to do it better... Once I do I shall post it here

    Thanks a lot!
  10. #6
  11. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,905
    Rep Power
    481
    Yes, and on a smaller scale I used to play with jcpds powder x-ray diffraction patterns. I haven't graduated yet to stars and Doppler effects.
    [code]Code tags[/code] are essential for python code and Makefiles!
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    9
    Rep Power
    0
    That's very interesting! So far I have been working with IRAF (I have made a few video tutorials actually: https://www.youtube.com/playlist?list=PLza8S46F-17XLmPkVpQLJb5C3qUQAEUq7)

    I want to do the same with python/pyfits but I still need to learn a lot

IMN logo majestic logo threadwatch logo seochat tools logo