Skip to content

Dataframe subset - #76

Merged
philipstarkey merged 15 commits into
labscript-suite:masterfrom
zakv:dataframe-subset
Nov 4, 2020
Merged

Dataframe subset#76
philipstarkey merged 15 commits into
labscript-suite:masterfrom
zakv:dataframe-subset

Conversation

@zakv

@zakv zakv commented Oct 29, 2020

Copy link
Copy Markdown
Contributor

This PR implements some of the features requested in #3 by adding optional n_sequences and filter_kwargs arguments to lyse.data().

Changes:

  • Add an optional n_sequences argument to lyse.data() which allows the user to request only a subset of the lyse dataframe, which can speed execution.
    • If there are fewer than n_sequences sequences available, then the entire lyse dataframe is returned.
  • Add an optional filter_kwargs argument to lyse.data()
    • This is a dictionary of keyword arguments passed to Dataframe.filter() before the dataframe is transmitted.
  • Improve thread safety of Webserver.handler()
  • Add a docstring for lyse.data()

@zakv

zakv commented Oct 29, 2020

Copy link
Copy Markdown
Contributor Author

I actually have a few questions still:

  1. Previously WebServer.handler() didn't have anything special in it to make it thread safe. Is it necessary to add a lock or an inmain_decorator or something now that there is some processing of the dataframe's info in WebServer.handler()?
  2. I'd be happy to implement the other feature requested in Multi-shot routines should be able to ask for a sub-set of the dataframe #3 as well, namely an optional date argument. I just have two questions related to that:
    • What form should the argument take? A string with some particular formatting? An instance of some date/time class?
    • Should this be AND'ed with n_sequences? In other words if n_sequences = 100 and date='today' then only return sequences from today, up to 100 of them.

@philipstarkey

Copy link
Copy Markdown
Member

Yeah, I believe I've raised the thread safety issue somewhere before (doesn't seem to have its own issue though...). I'm pretty sure the existing implementation is thread unsafe because the data frame is sent to internal zprocess methods for serialisation (I think using pickle?), and it's passed by reference. So even a lock or in_main call won't help with that because the lock would always be released before zprocess serialised it.

The only way to safe with the current architecture is to do a deepcopy of the data frame and return that (assuming it's even possible to deepcopy a data frame). Performance would probably be terrible for large data frames? Maybe it's worth trying though? I also have no idea if subsets return deep copies or only shallow copies of data.

Regarding 2, what about having n_sequences and then a set of arguments to pass directly to the filter method of the data frame? Then we don't have to worry about what sort of filtering we're supporting. Maybe have a single argument for data() called filter which the user would specify as a dictionary where the key/values match the pandas method and then the lyse code can just **filter_kwargs inside the call to filter if that makes sense?

@chrisjbillington

chrisjbillington commented Oct 29, 2020

Copy link
Copy Markdown
Member

zprocess doesn't defer serialisation such that it might occur in a different thread - serialisation occurs in the calling thread. (zmq actually sending the data is deferred, but not serialisation via pickling). So a lock or inmain() should be sufficient I should think.

pickling holds the GIL, so the dataframe can't be modified mid-pickle. However, the dataframe that gets sent may be in the middle of being updated by update_row(), so it may have some analysis results updated and some not yet.

I think inmain_decotrator on the handler would remove that possibility, since update_row() is called in the main thread.

@philipstarkey

philipstarkey commented Oct 29, 2020

Copy link
Copy Markdown
Member

Adding inmain_decorator on the handler only prevents updates to the dataframe while that method runs though. All of the machinery in ZMQServer.mainloop and ZMQ itself (where the pickling occurs right?) runs after ZMQServer.handler completes, and so the main thread can continue and may (partially) modify the dataframe before it is sent down the socket (unless we make a copy). So I don't think that solves the thread safety problem.

Also, just because pickling holds the GIL, doesn't Python still context switch between threads every 15ms anyway (previously was 1000 lines of bytecode in Python 2)? I don't think relying on the GIL is sufficient.

Both issues can be solved with a dedicated DataFrame lock, but zprocess would need to be updated so it can also be passed the lock somehow (to be held while internal zprocess/zmq code interacts with the dataframe). To be honest, I'm surprised there isn't a PEP for automatically using an RLock before accessing an object if one exists in object.__lock__. That way code like zprocess wouldn't need to grow additional kwargs to pass around lock objects...

EDIT: alternatively, if handler returns a copy of the dataframe, made while run in the main thread, then that would be safe too (since the only reference to the copy held after handler runs would be in the internal zprocess code). I was hoping to avoid data duplication though.

@chrisjbillington

chrisjbillington commented Oct 29, 2020

Copy link
Copy Markdown
Member

Ah I'm sorry, you're right. Would have to have the zmqserver's mainloop hold a lock somehow, not currently supported or easy to do by subclassing (but see below for an alternate method).

No, no other Python code can run whilst pickling, since pickling is a single python bytecode instruction that calls into C, and doesn't release the GIL within the C code. The whole interpreter stops whilst pickling, it's not possible for an object to be modified mid-pickle. I think this is by design and not just an implementation detail, though I'm not 100% sure.

But that's not super relevant - pickling itself can't fail, but it can begin at any arbitrary moment whilst the dataframe is being modified, so you can still get a half-modified dataframe.

EDIT: alternatively, if handler returns a copy of the dataframe, made while run in the main thread, then that would be safe too (since the only reference to the copy held after handler runs would be in the internal zprocess code). I was hoping to avoid data duplication though.

To avoid data duplication, we could add inmain_decorator to the handler, and have it call the ZMQServer's send method directly to send the response then and there instead of returning the object to be sent. Then we instead return ZMQServer.NO_RESPONSE:

@inmain_decorator(wait_for_return=True)
def handler(self, data):
    ....
    self.send(dataframe)
    return self.NO_RESPONSE
    ...

That way the pickling happens in the send() method whilst the lock is still held (edit: sorry, no lock is held per se,rather we're relying on the dataframe only being modified in the main thread. If that's not a safe assumption, we either need to make it true, or add a new lock around modifying the dataframe).

I use this same mechanism in the IMQDdxCamera BLACS tab, there it was because the handler does some time-consuming work updating the displayed image, but I didn't want that to delay the response. So it calls send() to send a response immediately before displaying the new image.

@zakv

zakv commented Oct 30, 2020

Copy link
Copy Markdown
Contributor Author

I've implemented Chris's suggestion with @inmain_decorator and self.send().

I also implemented Phil's filter suggestion, though I called it filter_kwargs to avoid using the name of a builtin. Sending a dictionary between the processes was a little tricky so I had to bring in a few imports: argparse, shlex, and ast. They're all from the standard library though.

I noticed that the other classes were using ParentClassName.__init__() rather than super().__init__() so I did the same with WebServer.

There is one issue that I haven't figured out yet though. For some reason the dataframe's index is replaced with a RangeIndex if filter_kwargs is used:

In []: filter_kwargs={'like':'run'}

In []: lyse.data(filter_kwargs=filter_kwargs).index
Out[]: RangeIndex(start=0, stop=3, step=1)

In []: lyse.data().filter(**filter_kwargs).index
Out[]: 
MultiIndex([('2020-10-29 19:07:50-04:00', '2020-10-29 19:07:51-04:00'),
            ('2020-10-29 19:07:50-04:00', '2020-10-29 19:07:53-04:00'),
            ('2020-10-29 19:07:51-04:00', '2020-10-29 19:07:54-04:00')],
           names=['sequence', 'run time'])

At first I assumed it had to do with the way that filter_kwargs is sent over ZeroMQ to the WebServer, but I saw the same behavior even when I temporarily hardcoded a value for filter_kwargs in WebServer.handler(). I'll keep digging but any suggestions would be appreciated.

@zakv

zakv commented Oct 30, 2020

Copy link
Copy Markdown
Contributor Author

Ok I think I see the issue actually. The app.filebox.shots_model.dataframe uses a RangeIndex, then when the dataframe reaches lyse.data() its changed to a multiindex. Running filter() in WebServer.handler runs it on app.filebox.shots_model.dataframe and can end up filtering out the columns that would later be used to generate the multiindex. Then lyse.data() swallows the error generated when trying to make the multiindex and just returns the dataframe with the rangeindex.

I guess the solution is to move the multiindex-generating code from lyse.data() to WebServer.handler()? That way it could be run before filter().

@philipstarkey

Copy link
Copy Markdown
Member

Was the use of shlex etc. to maintain backwards/forwards compatibility when accessing the dataframe from a standalone script? (For example lyse v3.0 server talking to lyse v3.1 client on another PC?).

Normally I would have probably just sent something like ['get_dataframe', n_sequences, filter_kwargs] as the command passed to zmq_get (zmq_get can send python objects), but I guess that would mean new lyse clients would raise an error when talking to old lyse servers. Oh...if n_sequences is None and filter_kwargs is None, the new client could fall back to sending just 'get_dataframe' instead of a list? The other direction could be accommodated too (old lyse clients talking to new lyse servers) by using a more complex if statement on the server side (to capture both 'get_dataframe' and ['get_dataframe', ..]. So you get an (unhelpful) error message if you use new client with old server and take advantage of the new kwargs (although could we catch the error returned by the old ZMQ server and present a more useful error to the user?)

Thoughts?

Regarding the RangeIndex, there is a comment about falling back to RangeIndex just below the call to zmq_get in data(). I'm not familiar with the piece of code, but just noticed it when looking at the other stuff now. Might be worth seeing if the try/except is suppressing some useful debug information there.

@philipstarkey

Copy link
Copy Markdown
Member

Ok I think I see the issue actually. The app.filebox.shots_model.dataframe uses a RangeIndex, then when the dataframe reaches lyse.data() its changed to a multiindex. Running filter() in WebServer.handler runs it on app.filebox.shots_model.dataframe and can end up filtering out the columns that would later be used to generate the multiindex. Then lyse.data() swallows the error generated when trying to make the multiindex and just returns the dataframe with the rangeindex.

I guess the solution is to move the multiindex-generating code from lyse.data() to WebServer.handler()? That way it could be run before filter().

Missed this while writing my own reply. Yeah, that could be a good idea. It does seem like filter should be run after generating the multiindex. Might need to test how this interacts with v3.0 server taking to v3.1 client though (and 3.1 server talking to 3.0 client). The code to convert to multi-index may need to be in both client and server until 4.0?

@zakv

zakv commented Oct 30, 2020

Copy link
Copy Markdown
Contributor Author

Was the use of shlex etc. to maintain backwards/forwards compatibility when accessing the dataframe from a standalone script? (For example lyse v3.0 server talking to lyse v3.1 client on another PC?).

Nope, somehow the idea that I could send anything besides a string didn't occur to me. That's a much better idea so I'll switch it over to do that instead and get rid of the superfluous imports.

Normally I would have probably just sent something like ['get_dataframe', n_sequences, filter_kwargs] as the command passed to zmq_get (zmq_get can send python objects), but I guess that would mean new lyse clients would raise an error when talking to old lyse servers. Oh...if n_sequences is None and filter_kwargs is None, the new client could fall back to sending just 'get_dataframe' instead of a list? The other direction could be accommodated too (old lyse clients talking to new lyse servers) by using a more complex if statement on the server side (to capture both 'get_dataframe' and ['get_dataframe', ..]. So you get an (unhelpful) error message if you use new client with old server and take advantage of the new kwargs (although could we catch the error returned by the old ZMQ server and present a more useful error to the user?)

Yeah that is a tricky situation which I hadn't considered. I can't think of a better solution than what you've suggested here. Actually the old lyse server just ignores the request if command is a list, so zmq_get() returns None in that case. That makes it easy to notice and raise a helpful error client-side if the server is outdated and doesn't support the new arguments. It can also fall back to sending just 'get dataframe' if n_sequences and filter_kwargs are None.

It does seem like filter should be run after generating the multiindex. Might need to test how this interacts with v3.0 server taking to v3.1 client though (and 3.1 server talking to 3.0 client). The code to convert to multi-index may need to be in both client and server until 4.0?

Yep I think you're right.

Edit: Actually I was mistaken, the old version would return a string starting with 'error' if an unrecognized command was sent. Still easy to catch though.

Comment thread lyse/__init__.py Outdated
Comment thread lyse/__init__.py Outdated
Comment thread lyse/__main__.py Outdated
@zakv

zakv commented Oct 31, 2020

Copy link
Copy Markdown
Contributor Author

Looks like some PRs got merged into master. Should I do a rebase / force push for this branch? Or is the plan to just do a merge?

@philipstarkey

Copy link
Copy Markdown
Member

Looks like some PRs got merged into master. Should I do a rebase / force push for this branch? Or is the plan to just do a merge?

I don't think you need to rebase unless you want to test the docs build. The rebase will probably also stuff up the git commit references in your comments, so I'd probably lean towards just merging and then patching any issues with the docs separately.

@philipstarkey philipstarkey self-assigned this Oct 31, 2020
Comment thread lyse/__init__.py Outdated
@philipstarkey
philipstarkey merged commit 695c220 into labscript-suite:master Nov 4, 2020
dihm added a commit that referenced this pull request Dec 7, 2021
commit 8575ff2
Author: David Meyer <dihm@users.noreply.github.com>
Date:   Tue Dec 7 10:52:15 2021 -0500

    Update setup.cfg to show python 3.9 support

commit e4b57f8
Merge: 8bf02a2 915cc1c
Author: David Meyer <dihm@users.noreply.github.com>
Date:   Tue Dec 7 07:31:45 2021 -0500

    Merge pull request #95 from dihm/msg_replacement

    Msg replacement

commit 8bf02a2
Merge: 72f82b3 a98a766
Author: David Meyer <dihm@users.noreply.github.com>
Date:   Tue Dec 7 07:30:06 2021 -0500

    Merge pull request #88 from zakv/move-infer_objects

    Move infer_objects() out of main thread

commit 915cc1c
Author: David Meyer <dihm@users.noreply.github.com>
Date:   Fri Dec 3 09:01:04 2021 -0500

    Adding the extra bugfixes found in #91.

commit fa9eedb
Author: David Meyer <dihm@users.noreply.github.com>
Date:   Fri Dec 3 09:00:29 2021 -0500

    Replace deprecated msgpack with python's pickle for saving dataframes.

    This keeps machinery in place to read msgpack files if the installed
    pandas supports it, but it will always save new dataframes as pickles.

commit 72f82b3
Merge: cb60667 26ef795
Author: David Meyer <dihm@users.noreply.github.com>
Date:   Fri Jul 16 16:09:52 2021 -0400

    Merge pull request #93 from dihm/update_lyse_docs

    Update lyse docs to match autogenerated docs from other modules.

commit 26ef795
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Thu Jul 15 10:30:09 2021 -0400

    Add docstring coverage to build.

commit 98cac68
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Thu Jul 15 10:29:57 2021 -0400

    Update sphinx pin.

commit 4671ef6
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Tue Jul 13 17:21:29 2021 -0400

    Add docstrings to the API functions.

    Reasonably sure they are all accurate.

commit 40d207c
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Mon Jul 12 10:47:31 2021 -0400

    Tweaked docs formatting.

commit 47b60a8
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Mon Jul 12 10:40:42 2021 -0400

    Convert doc generation to recursive autosummary call.

commit 9add58b
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Mon Jul 12 10:38:50 2021 -0400

    Tweak docstrings.

commit 3873dcc
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Mon Jul 12 09:26:55 2021 -0400

    Add Qt5 inventory for intersphinx lookup.

commit e7cf19a
Author: David Meyer <dihm.meyer@gmail.com>
Date:   Mon Jul 12 09:26:31 2021 -0400

    Update sphinx pins to match rest of suite docs.

commit cb60667
Merge: eaca856 8596dea
Author: Chris Billington <chrisjbillington@gmail.com>
Date:   Fri Jan 29 14:00:36 2021 +1100

    Merge pull request #89 from chrisjbillington/master

    Do not use deprecated set-env command

commit 8596dea
Author: chrisjbillington <chrisjbillington@gmail.com>
Date:   Fri Jan 29 13:57:39 2021 +1100

    Do not use deprecated set-env command

commit a98a766
Author: Zak V <zakven@mit.edu>
Date:   Thu Nov 19 14:13:34 2020 -0500

    infer_objects() is now run outside of the main thread when data() is called to reduce main thread contention.

commit eaca856
Merge: fcfda48 bcede15
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Sun Nov 8 12:11:30 2020 +1100

    Merge pull request #87 from zakv/fix-86

    Fix 86

commit bcede15
Author: Zak V <zakven@mit.edu>
Date:   Sat Nov 7 17:33:51 2020 -0500

    Simplified flow control and append_units() funciton in Run.get_units().

commit e56d5fa
Author: Zak V <zakven@mit.edu>
Date:   Fri Nov 6 20:51:33 2020 -0500

    Added docstring for Run.get_units().

commit 6991aa9
Author: Zak V <zakven@mit.edu>
Date:   Fri Nov 6 20:50:38 2020 -0500

    Run.get_units() now uses its optional group argument

commit fcfda48
Merge: cdef626 1dac68f
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Thu Nov 5 17:02:14 2020 +1100

    Merge pull request #80 from zakv/fix-78

    Fix 78

commit 1dac68f
Author: Zak V <zakven@mit.edu>
Date:   Wed Nov 4 12:54:52 2020 -0500

    Run.__init__() now properly handles when a lyse script is called analysis_subprocess.py.

commit 7ba7062
Author: Zak V <zakven@mit.edu>
Date:   Wed Nov 4 03:51:30 2020 -0500

    Changed Run's private attributes from single underscore to double underscore.

commit 3d38b3b
Author: Zak V <zakven@mit.edu>
Date:   Wed Nov 4 03:45:40 2020 -0500

    Refactored Sequence.__init__() to use Run.__init__() per Phil's idea in PR #80.

commit cdef626
Merge: 695c220 86ec2a6
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Wed Nov 4 16:39:00 2020 +1100

    Merge pull request #79 from philipstarkey/philipstarkey/issue77

    Fixes #77

commit 695c220
Merge: 0910447 f80f182
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Wed Nov 4 16:35:54 2020 +1100

    Merge pull request #76 from zakv/dataframe-subset

    Dataframe subset

commit f80f182
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 20:21:38 2020 -0500

    Corrected formatting in data()'s docstring.

commit ab8e44f
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 20:07:41 2020 -0500

    Corrected indentation in Run.group's docstring.

commit 005163e
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 20:06:50 2020 -0500

    Fixed issues with Sequence.__init__().

    First, it was updated to work with recent changes to its parent class. Also it used to error out if the hdf5 file didn't already exist, as first pointed out in PR 73, but that is now resolved..

commit 6a2755b
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 16:44:09 2020 -0500

    Replaced double underscores with single for private Run attributes so that child classes, e.g. Sequence, can use them.

commit c6d8306
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 15:49:50 2020 -0500

    Corrected typos in lyse.save_result()'s docstring.

commit e7778b5
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 15:49:17 2020 -0500

    Updated docstring for Run.save_result_array().

commit 9a590e5
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 15:32:58 2020 -0500

    Removed some trailing whitespace in lyse.Run().

commit cad1435
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 15:32:31 2020 -0500

    Updated error messages in Run.save_result_array() and replaced Exception with PermissionError to be more specific.

commit 9af179c
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 15:22:28 2020 -0500

    Cleaned up error messages in Run.save_result() and changed Exception to PermissionError to be more specific.

commit 60f00a4
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 15:04:28 2020 -0500

    Replaced "run" with "shot" in Run's docstring.

commit 575fb4f
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 14:56:32 2020 -0500

    Fixed minor typo in comment in Run class.

commit 0bd4a02
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 14:54:46 2020 -0500

    Changed Run's h5_path, no_write, and group attributes into properties.

commit d6c6919
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 14:29:47 2020 -0500

    Small update to docstring for Run.save_result().

commit c4203f3
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 14:25:28 2020 -0500

    Removed print statement from Run.save_results().

commit 5281dea
Author: Zak V <zakven@mit.edu>
Date:   Tue Nov 3 14:24:43 2020 -0500

    Updated docstrings for Run.save_result() and Run.save_results().

commit 9132cf3
Author: Zak V <zakven@mit.edu>
Date:   Mon Nov 2 16:31:23 2020 -0500

    Run._create_group_if_not_exists() can now handle another thread/process creating the hdf5 group partway through its execution.

commit 997a6c7
Author: Zak V <zakven@mit.edu>
Date:   Mon Nov 2 15:50:52 2020 -0500

    Corrected some typos in strings in __init__.py.

commit 565c1b3
Author: Zak V <zakven@mit.edu>
Date:   Mon Nov 2 15:45:35 2020 -0500

    Added a docstring for the Run class.

commit 029fdda
Author: Zak V <zakven@mit.edu>
Date:   Mon Nov 2 15:08:02 2020 -0500

    Added docstring for Run.set_group().

commit afd9d8f
Author: Zak V <zakven@mit.edu>
Date:   Mon Nov 2 14:19:03 2020 -0500

    Simplified Run.group's behavior when initialized outside of a lyse script.

commit bff7d19
Author: Zak V <zakven@mit.edu>
Date:   Mon Nov 2 10:41:33 2020 -0500

    Fixed some formatting in data()'s docstring.

commit 86ec2a6
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Mon Nov 2 17:22:49 2020 +1100

    Fixed missing import

commit 9eff3cd
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Mon Nov 2 17:20:07 2020 +1100

    Fixes #77

    We now mock the missing methods in the site module.

commit d79bada
Author: Zak V <zakven@mit.edu>
Date:   Sat Oct 31 16:47:01 2020 -0400

    Reduced the amount of code run with @inmain_decorator() in WebServer.handler().

commit 8d2a69e
Author: Zak V <zakven@mit.edu>
Date:   Fri Oct 30 20:35:03 2020 -0400

    Added intersphinx link for Dataframe.filter() in data()'s docstring.

commit 4c944dd
Author: Zak V <zakven@mit.edu>
Date:   Fri Oct 30 12:43:37 2020 -0400

    Updated the error message raised in data() when the server is running an outdated version of lyse.

commit f26aba2
Author: Zak V <zakven@mit.edu>
Date:   Fri Oct 30 12:32:55 2020 -0400

    Moved df.sort_index() from data() to _rangeindex_to_multiindex().

commit 0910447
Merge: 72fa369 ef03a15
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Fri Oct 30 16:56:33 2020 +1100

    Merge pull request #74 from zakv/n_runs

    Add n_runs column to lyse dataframe

commit 72fa369
Merge: da9de67 8803447
Author: Phil Starkey <philipstarkey@users.noreply.github.com>
Date:   Fri Oct 30 16:53:22 2020 +1100

    Merge pull request #72 from zakv/set-group

    Make Run.set_group() create the group if necessary.

commit b344e9e
Author: Zak V <zakven@mit.edu>
Date:   Thu Oct 29 23:57:09 2020 -0400

    Simplified lyse.data() communication and made it backwards compatible.

commit 8c7a456
Author: Zak V <zakven@mit.edu>
Date:   Thu Oct 29 22:21:57 2020 -0400

    Fixed an issue where the conversion to multiindex in lyse.data() could fail when using its filter_kwargs argument.

commit b61ab25
Author: Zak V <zakven@mit.edu>
Date:   Thu Oct 29 19:30:50 2020 -0400

    Updated docstring for lyse.data().

commit f1be302
Author: Zak V <zakven@mit.edu>
Date:   Thu Oct 29 19:14:43 2020 -0400

    Added optional filter_kwargs argument to lyse.data().

commit d134b6e
Author: Zak V <zakven@mit.edu>
Date:   Thu Oct 29 12:03:23 2020 -0400

    Improved WebServer's thread safety.

commit d1d9897
Author: Zak V <zakven@mit.edu>
Date:   Wed Oct 28 21:42:03 2020 -0400

    Clarified lyse.data()'s docstring about behavior when n_sequences is greater than number of sequences available.

commit c25caef
Author: Zak V <zakven@mit.edu>
Date:   Wed Oct 28 20:36:02 2020 -0400

    Added a docstring for lyse.data().

commit 9d662ea
Author: Zak V <zakven@mit.edu>
Date:   Wed Oct 28 20:00:43 2020 -0400

    lyse.data() now correctly handles when n_sequences=0.

commit c75b945
Author: Zak V <zakven@mit.edu>
Date:   Wed Oct 28 19:01:36 2020 -0400

    Added optional n_sequences argument to lyse.data().

commit ef03a15
Author: Zak V <zakven@mit.edu>
Date:   Wed Oct 21 07:54:18 2020 -0400

    Added n_runs column to lyse dataframe.

commit 8803447
Author: Zak V <zakven@mit.edu>
Date:   Thu Jul 16 16:58:30 2020 -0400

    Run.set_group() now creates the group if necessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants