Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom lines at various locations #14

Merged
merged 6 commits into from
Jul 10, 2020

Conversation

csemken
Copy link
Contributor

@csemken csemken commented Dec 6, 2019

This request is based on and implements the suggestions I made in #13 (only one should be accepted).

It allows users to add custom lines to various parts of the table, which are defined in a constant:
CUSTOM_LINES_LOCATIONS = ['body bottom', 'footer top', 'footer bottom']

Thus, users can, for example, create the following common regression table:

(1) (2)
var1 ... ...
var2 ... ...
Controls No Yes
-------------- ------ ------------
Sample Full Restricted
Observations 1000 500
0.5 0.7
Mean Dep Var 10 20

where Controls is added with location='body bottom', Sample is added with location='footer top' (the default location) and Mean Dep Var is added with location='footer bottom'.

I renamed the custom_footer_text variable to custom_lines and the setter method add_custom_lines(), where 'lines' is the word used in the Stargazer R package. Since this functionality was not previously implement, the code remains backwards compatible.

@csemken csemken changed the title Custom text generic Custom lines at various locations Dec 6, 2019
@MaxGhenis
Copy link
Contributor

I'd love to use this functionality! Could a maintainer please weigh in on which solution between this and #13 (or something else) is preferred, so conflicts can be resolved and one of them can be merged?

@csemken
Copy link
Contributor Author

csemken commented Jun 27, 2020

+1. Let me know whether you prefer this or #13 and I can update the PR.

@toobaz
Copy link
Collaborator

toobaz commented Jun 27, 2020

@csemken @MaxGhenis I think this more flexible approach is nice to have. Few comments:

  • I'm not a fan of the specific CUSTOM_LINES_LOCATIONS values... but I can't think of any better solution.
  • Couldn't the method just be add_line? This looks to me more natural & similar to the R stargazer.
  • Any reason for not including 'body top' as a location? (I mean: below the headers, but before any data)
  • I have a slight preference for having the location argument optional, and defaulting to location='body bottom'. But do let me know if you disagree.
  • While you update the PR, please add a docstring. Yes, I know most methods still miss it, but I'd like at least new API additions to have one.

@@ -176,6 +178,14 @@ def reset_covariate_order(self):
if self.original_cov_names is not None:
self.cov_names = self.original_cov_names

def add_custom_lines(self, lines, location='footer top'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @toobaz's comment, I think add_line would be more natural.

Also lines seems like an odd term, isn't this just one line that's added at a time? Below when you loop through the elements, those are individual values right?

def add_custom_lines(self, lines, location='footer top'):
for line in lines:
assert len(line) == self.num_models + 1, \
'Please input iterables of length "number of models + 1"'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A separate label arg may be cleaner than requiring it to be self.num_models + 1. This would also be more future-proof, if stargazer eventually supports multiple left-most (non-model) columns.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can allow both approaches: check that line has self.num_models + 1 (where 1 would be possibly replaced in the future by the number of left headers) items, and only if it has self.num_models instead, use the (optional) label argument (which would default to ''). (Raise if it has neither number of items)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. I think only supporting line of length self.num_models with a label would be the cleanest option. That way we offer one clear interface. Offering both interfaces might be confusing, increases code complexity and still creates the multiple left-most columns problem that @MaxGhenis mentioned.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. I think only supporting line of length self.num_models with a label would be the cleanest option.

OK, no objection: then I would have label as first argument and line as second.

'Please input iterables of length "number of models + 1"'
assert location in self.CUSTOM_LINES_LOCATIONS, \
'location needs to be one of {}'.format(str(self.CUSTOM_LINES_LOCATIONS))
self.custom_lines[location] = lines
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overwrites the current value, so only one line can be added per location, right?

It'd be nice to be able to add multiple lines per location, such that this would be some sort of append statement.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to be able to add multiple lines per location, such that this would be some sort of append statement.

Good point: it would be great if each self.custom_lines[location] was a list, and add_line appended lines to it. Then (at least for now) we could just mention in the docstrings that one can remove all added lines with table.custom_lines[location] = [].

for custom_row in self.custom_lines[location]:
custom_text += '<tr><td style="text-align: left">' + str(custom_row[0]) + '</td>'
for custom_column in custom_row[1:]:
custom_text += '<td>' + str(custom_column) + '</td>'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This str should in principle be replaced by the appropriate formatters for float (_float_format) and some day, int (see #53); ideally, add_line should even have an optional formatter argument. But all of this can be done later in a separate PR to now slow down this one.

@csemken
Copy link
Contributor Author

csemken commented Jun 28, 2020

Excellent suggestions @toobaz and @MaxGhenis.

* I'm not a fan of the specific `CUSTOM_LINES_LOCATIONS` values... but I can't think of any better solution.

Would you prefer an enum?

* While you update the PR, please add a docstring. Yes, I know most methods still miss it, but I'd like at least new API additions to have one.

Do you have any docstring format preferences? Maybe Google?

@toobaz
Copy link
Collaborator

toobaz commented Jun 28, 2020

Would you prefer an enum?

Yes, good idea. So users can cleanly import them, and they can always use the numbers if they prefer.

Do you have any docstring format preferences? Maybe Google?

I guess we could go with Numpydoc, as it is used by pandas, statsmodels... most of the stack stargazer users are probably used to.

@toobaz
Copy link
Collaborator

toobaz commented Jun 28, 2020

I guess we could go with Numpydoc, as it is used by pandas, statsmodels... most of the stack stargazer users are probably used to.

https://pandas.pydata.org/pandas-docs/stable/development/contributing_docstring.html

@csemken
Copy link
Contributor Author

csemken commented Jun 28, 2020

All done.

@MaxGhenis
Copy link
Contributor

Could you add to README.md?

@MaxGhenis
Copy link
Contributor

Just used this for a blog post. Thank you, @csemken!
image

@csemken
Copy link
Contributor Author

csemken commented Jul 8, 2020

Great @MaxGhenis! I am glad it is useful.

Could you add to README.md?

Absolutely. I updated both README.md and examples.ipynb.

@toobaz Ready to merge?

Copy link
Collaborator

@toobaz toobaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it, just two minor issues in the docstring

stargazer/stargazer.py Outdated Show resolved Hide resolved
stargazer/stargazer.py Outdated Show resolved Hide resolved
@csemken csemken requested a review from toobaz July 10, 2020 16:27
Copy link
Collaborator

@toobaz toobaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ready

@toobaz toobaz merged commit c1e9683 into StatsReporting:master Jul 10, 2020
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