eshangj/stackoverflow_q_and_a_sample huggingface dataset:
Question: I've got a logging handler setup that prints to stream and file. I'm working with a few dictionaries and modifying dictionaries. Is there a way to format a dictionary for the log file so that it shows up as one block rather than a line? I've gone through a bunch of simpler formatting attempts and read through How to insert newline in python logging?. Before I try writing a custom formatter just for dictionaries, wanted to find out if there's a known way to do this. Thanks! The desired output would be something like this: 2024-02-13 13:27:03,685 [DEBUG] root: shelf_name = 'some shelf', url = 'http://a_url', creation_date = '02/12/2024' 2024-02-13 13:34:55,889 [DEBUG] root:  So is there any way to do this for a dictionary block? UPDATED: Removed the old extraneous iterations. I'm posting the closest acceptable result, but still would like to make the dictionary print as a single block class Downloader: def __init__(self, shelf_data) -> None: shelf = ShelfData(shelf_data) [logging.debug(f"shelf['{key}']: {val}") for key, val in shelf.__dict__.items()]  Log file: 2024-02-13 16:29:18,024 [DEBUG] root: shelf['shelf_name']: test_shelf 2024-02-13 16:29:18,024 [DEBUG] root: shelf['url']: https://site/group/show/1865-scifi-and-fantasy-book-club 2024-02-13 16:29:18,024 [DEBUG] root: shelf['base_path']: C:\MyProjects\downloads 2024-02-13 16:29:18,024 [DEBUG] root: shelf['creation_date']: 02/12/2024 2024-02-13 16:29:18,039 [DEBUG] root: shelf['sort_order']: descend 2024-02-13 16:29:18,039 [DEBUG] root: shelf['books_per_page']: 100 2024-02-13 16:29:18,039 [DEBUG] root: shelf['download_dir']: None 2024-02-13 16:29:18,039 [DEBUG] root: shelf['book_count']: 37095 2024-02-13 16:29:18,039 [DEBUG] root: shelf['page_count']: 371  Also, might as well add my logger:  logging_config = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, }, 'handlers': { 'default_handler': { 'class': 'logging.FileHandler', 'level': 'DEBUG', 'formatter': 'standard', 'filename': os.path.join('logs', f'{log_name}.log'), 'encoding': 'utf-8' }, 'stream_handler': { 'class': 'logging.StreamHandler', 'level': 'DEBUG', 'formatter': 'standard',} }, 'loggers': { '': { 'handlers': ['default_handler', 'stream_handler'], 'level': 'DEBUG', 'propagate': False } } } logging.config.dictConfig(logging_config) 

Answer: Not sure if it's exactly what you're looking for, but I leveraged the pprint module to pretty-print dictionaries storing configurations for various things in an app I'm working on. The helper function and usage is below: import pprint import logging # set up pretty printer pp = pprint.PrettyPrinter(indent=2, sort_dicts=False) def log_pretty(obj): pretty_out = f"{pp.pformat(obj)}" return f'{pretty_out}\n' # set up and log as usual logger = logging.getLogger("myapp") logger.info(f'You could print your logging_config dict like so:\n{log_pretty(logging_config)}')  You can also tweak the pprint options to your liking... see here.
Question: I already installed InstructorEmbedding, but it keeps giving me the error, in jupyter notebook environment using Python 3.12 (I also tried in 3.11). Kernel restarting didn't help. import torch from langchain.embeddings import HuggingFaceInstructEmbeddings DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu" embedding = HuggingFaceInstructEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs={"device": DEVICE})  error: --------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) File /opt/conda/lib/python3.11/site-packages/langchain_community/embeddings/huggingface.py:151, in HuggingFaceInstructEmbeddings.__init__(self, **kwargs) 150 try: --> 151 from InstructorEmbedding import INSTRUCTOR 153 self.client = INSTRUCTOR( 154 self.model_name, cache_folder=self.cache_folder, **self.model_kwargs 155 ) File /opt/conda/lib/python3.11/site-packages/InstructorEmbedding/__init__.py:1 ----> 1 from .instructor import * File /opt/conda/lib/python3.11/site-packages/InstructorEmbedding/instructor.py:9 8 from torch import Tensor, device ----> 9 from sentence_transformers import SentenceTransformer 10 from sentence_transformers.models import Transformer ModuleNotFoundError: No module named 'sentence_transformers' The above exception was the direct cause of the following exception: ImportError Traceback (most recent call last) Cell In[2], line 10 4 DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu" 6 #loader = PyPDFDirectoryLoader("aircraft_pdfs") 7 #docs = loader.load() 8 #print(len(docs)) # length of all pages together ---> 10 embedding = HuggingFaceInstructEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs={"device": DEVICE}) File /opt/conda/lib/python3.11/site-packages/langchain_community/embeddings/huggingface.py:157, in HuggingFaceInstructEmbeddings.__init__(self, **kwargs) 153 self.client = INSTRUCTOR( 154 self.model_name, cache_folder=self.cache_folder, **self.model_kwargs 155 ) 156 except ImportError as e: --> 157 raise ImportError("Dependencies for InstructorEmbedding not found.") from e ImportError: Dependencies for InstructorEmbedding not found.  here is the output of pip freeze transformers==4.37.2 torch==2.2.0 langchain==0.1.6 InstructorEmbedding==1.0.1 ... 

Answer: I think you would also need to install sentence-transformers. Try installing it via: pip install -U sentence-transformers==2.2.2  and then run your code. Please make sure you install the version 2.2.2 otherwise you'll end up with this error: TypeError: INSTRUCTOR._load_sbert_model() got an unexpected keyword argument 'token'  It seems the latest version of sentence-transformers has some compatibility issues.
Question: I want to create an axis title that has part of it in bold font. This is an example of my current code: # Features to plot features = ['Overall Rating', 'Shooting Rating', 'Creativity Rating', 'Passing Rating', 'Dribbling Rating', 'Defending Rating', 'Pressing Rating', 'Aerial Rating'] # Loop through each feature and create density plots for i, feature in enumerate(features): ax = axes[i] # Draw the density plot using the current feature column from the DataFrame sns.kdeplot(df[feature], shade=True, color='white', ax=ax) # Plot marker for df player_value = df[feature].values[0] ax.set_title(f'{feature}\n{int(player_value)}', color='#100097', loc='right', fontweight ="bold")  I want the text for feature to be a normal font weight, and for player_value to be a bold font weight. I've tried various methods to no avail: creating a separate title for each section of text, trying to set the fontweight separately within the set_title function, etc...

Answer: You can use LaTeX** to achieve this, quite easily using this kind of expression: $\\bf{bold text}$". In this case, the "bold text" will be displayed as bold. So in your case, add this: ax.set_title(f'{feature}\n$\\bf{{{int(player_value)}}}$', color='#100097', loc='right')  **: about LaTeX, it seems MathText is what is being called and not LaTeX, I will edit my question later
Question: Using FastAPI I have set up a POST endpoint that takes a command, I want this command to be case insensitive, while still having suggested values (i.e. within the SwaggerUI docs) For this, I have set up an endpoint with a Command class as a schema for the POST body parameters: @router.post("/command", status_code=HTTPStatus.ACCEPTED) # @router is a fully set up APIRouter() async def control_battery(command: Command): result = do_work(command.action) return result  For Command I currently have 2 possible versions, which both do not have the full functionality I desire. from fastapi import HTTPException from pydantic import BaseModel, field_validator from typing import Literal ## VERSION 1 class Command(BaseModel): action: Literal["jump", "walk", "sleep"] ## VERSION 2 class Command(BaseModel): action: str @field_validator('action') @classmethod def validate_command(cls, v: str) -> str: """ Checks if command is valid and converts it to lower. """ if v.lower() not in {'jump', 'walk', 'sleep'}: raise HTTPException(status_code=422, detail="Action must be either 'jump', 'walk', or 'sleep'") return v.lower()  Version 1 is obviously not case sensitive, but has the correct 'suggested value' behaviour, as below.  Whereas Version 2 has the correct case sensitivity and allows for greater control over the validation, but no longer shares suggested values with users of the schema. e.g., in the image above "jump" would be replaced with "string". How do I combine the functionality of both of these approaches?

Answer: Specify the type as either a literal or some string and keep the validation: class Command(BaseModel): action: Literal["jump", "walk", "sleep"] | str @field_validator('action') @classmethod def validate_command(cls, v: str) -> str: """ Checks if command is valid and converts it to lower. """ if v.lower() not in {'jump', 'walk', 'sleep'}: raise HTTPException(status_code=422, detail="Action must be either 'jump', 'walk', or 'sleep'") return v.lower()  A purist might say that Literal["jump", "walk", "sleep"] | str is the same as str, but in this case both a human and a computer can infer that "jump" and the other two values are special inputs. It works for providing example values. You may not want to repeat the literal values - a mypy-passing solution would be to make them an enum: from enum import Enum class Action(Enum): jump = "jump" walk = "walk" sleep = "sleep" class Command(BaseModel): action: Action | str @field_validator('action') @classmethod def validate_command(cls, v: str) -> str: """ Checks if command is valid and converts it to lower. """ if v.lower() not in Action.__members__: raise HTTPException(status_code=422, detail="Action must be either 'jump', 'walk', or 'sleep'") return v.lower()  Another solution would be to keep using the Literal instead of the enum and use get_args to extract the values of the literal type, as explained in this answer
Question: I have this code to parsing street and house number in Python import re def parse_street(address): pattern = r'^(.*?)(?i)\b no\b' match = re.match(pattern, address) if match: return match.group(1).strip() else: return None def parse_housenumber(address): pattern = r'(?i)\bno\.?\s*([\dA-Z]+)' match = re.search(pattern, address) print(match) if match: return match.group(1) else: return None df['street'] = df['address'].apply(lambda x: pd.Series(parse_street(x))) df['house_number'] = df['address'].apply(lambda x: pd.Series(parse_housenumber(x)))  Why I am getting error for running the query in postgresql with the same regex syntax query like: SELECT (regexp_matches('Jl. ABC No.01', '^(.*?)(?i)\b no\b'))[1]  The error message is: ERROR: invalid regular expression: quantifier operand invalid  Is regex in python different with regex in other system? How can I convert my python function to PostgreSQL syntax? I want to add "street" and "house_number" column from "address" column within the same table

Answer: Yes, most regex engines' syntax is different, to a lesser or greater degree. This is why the regex tag requires another tag specifying the engine, tool or a programming language to which the regex question pertains. From PostgreSQL docs (emphasis mine):  An ARE can begin with embedded options: a sequence (?xyz) (where xyz is one or more alphabetic characters) specifies options affecting the rest of the RE. These options override any previously determined options — in particular, they can override the case-sensitivity behavior implied by a regex operator, or the flags parameter to a regex function. The available option letters are shown in Table 9.24. Note that these same option letters are used in the flags parameters of regex functions.  Thus, this regex is not legal in PostgreSQL: ^(.*?)(?i)\b no\b  But, this one is: (?i)^(.*?)\b no\b  However, while legal, it still doesn't mean the same as the Python one, because the meaning of \b is different: for Python, it is a word boundary assertion; for PostgreSQL, it is a backspace character. The word boundary assertion is \y, and you can also be more precise with \m and \M (word start and end assertions, respectively). Thus, the equivalent PostgreSQL regular expression is: (?i)^(.*?)\y no\y
Question: Given an enum like this: class Result(IntEnum): A = 0 B = 1 C = 3  I'd like to be able to create new enum members dynamically, but only if the value is within a given range. For example, given: class Result(IntEnum, valid_range=(4, 10)): A = 0 B = 1 C = 3  I'd expect the following behaviour:  Result(10) would be ok, returning an instance of the enum where .value is set to 10. Result(0) would return Result.A Result(20) or Result("hello") would raise a ValueError   Why? This is for the BACnet standard - a building management/HVAC protocol. Grossly simplified it defines a standard set of message types (for example: READ, WRITE, LIST) which map to integer values. I've modelled these as an enum: class MessageType(IntEnum): READ = 0 WRITE = 1 LIST = 2  This is then used as follows: @dataclass class Message: message_type: MessageType data: bytes def decode(raw: bytes) -> Message: message_type = MessageType(unpack("!H", bytes[:2])) data = bytes[2:] return Message(message_type=message_type, data=data)  The protocol also allows for vendors to define their own message types but limits them to a specific range of values (i.e. between 10 and 20). Options considered:  Define each value as an additional enum attribute (i.e. MessageType.VENDOR_10) explicitly or dynamically but the number of valid values can be in the millions, most of which will not be used. Don't use an enum, just an int, an int TypeAlias, or just a custom class. Change the type annotation on Message to int | MessageType and when decoding, attempt to create a MessageType, catching the ValueError and assigning the int. 

Answer: It is possible, but requires Python 3.9 or better, and does use one currently undocumented data structure (_value2member_map_): from enum import IntEnum class RangedEnum(IntEnum): def __repr__(self): if self._name_: return f"<{self.__class__.__name__}.{self._name_}: {self._value_}>" else: return f"<{self.__class__.__name__}: {self._value_}>" def __init_subclass__(cls, valid_range): cls.valid_range = valid_range @classmethod def _missing_(cls, value): start, stop = cls.valid_range if start <= value <= stop: member = int.__new__(cls, value) member._name_ = None member._value_ = value cls._value2member_map_[value] = member return member class Result(RangedEnum, valid_range=(4, 10)): A = 0 B = 1 C = 3  and some light test code: >>> for v in (0, 1, 2, 3, 4, 5, 9, 10, 11): ... try: ... Result(v) ... except ValueError: ... print('nope on', v) ... <Result.A: 0> <Result.B: 1> nope on 2 <Result.C: 3> <Result: 4> <Result: 5> <Result: 9> <Result: 10> nope on 11   1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
Question: I'm new to Polars, and I ended up writing this code to compute some aggregating expression over segments of n rows: import polars as pl df = pl.DataFrame({"a": [1, 1, 3, 8, 62, 535, 4213]}) ( df.with_columns(index=pl.int_range(pl.len(), dtype=pl.Int32)) .group_by_dynamic(index_column="index", every="3i") .agg(pl.col("a").mean()) )  For the example I set n==3 for 7 rows, but think of a smallish n of about 100, for a multicolumn data frame of about 10**6 rows. I was wondering if this is the idiomatic way of doing this type of operation. Somehow group_by_dynamic over an Int32 range seems overkill to me: I was wondering if there is a more direct way of doing the same aggregation.

Answer: IMO your solution using group_by_dynamic already follows best practices when it comes to the aggregation. However, you can simplify the creation of the index column quite a bit using pl.DataFrame.with_row_index. As the result is unsigned (and group_by_dyanmic only allows for a signed integer index column), you'll need to pass an expression doing the casting, i.e. ( df .with_row_index() .group_by_dynamic(index_column=pl.col("index").cast(pl.Int32), every="3i") .agg(pl.col("a").mean()) )
Question: Consider I have a dataframe: import polars as pl import polars.selectors as cs df = pl.DataFrame( { 'p': [1, 2, 1, 3, 1, 2], 'x': list(range(6, 0, -1)), 'y': list(range(2, 8)), 'z': [3, 4, 5, 6, 7, None], "q" : list('abcdef') } )  df shape: (6, 5) p x y z q i64 i64 i64 i64 str 1 6 2 3 "a" 2 5 3 4 "b" 1 4 4 5 "c" 3 3 5 6 "d" 1 2 6 7 "e" 2 1 7 null "f"  I need to select all the integer columns except p and z. One way is to manually select each column but, it is not feasible if there are hundreds of columns. What could a better and efficient way?

Answer: You can use polars' column selectors to create a selector for all integers columns. Then, you can use pl.Expr.exclude to exclude column p and z from the selection. import polars.selectors as cs df.select(cs.integer().exclude("p", "z"))
Question: I have a dataframe import polars as pl import numpy as np df = pl.DataFrame( { "nrs": [1, 2, 3, None, 5], "names": ["foo", "ham", "spam", "egg", None], "random": np.random.rand(5), "groups": ["A", "A", "B", "C", "B"], } )  I want to select only the columns in list: mylist = ['nrs', 'random']  This seems to work: import polars.selectors as cs df.select(cs.by_name(mylist)))  Is this the idiomatic way to do it? Or are there better ways?

Answer: It's actually simpler than that: df.select(['nrs', 'random']) ┌──────┬──────────┐ │ nrs ┆ random │ │ --- ┆ --- │ │ i64 ┆ f64 │ ╞══════╪══════════╡ │ 1 ┆ 0.662732 │ │ 2 ┆ 0.437345 │ │ 3 ┆ 0.43857 │ │ null ┆ 0.701177 │ │ 5 ┆ 0.390494 │ └──────┴──────────┘  selectors are in general for more complicated selections - like all string columns, column starting with certain phrase and so on. As written in select() documentation you can either path list of columns or expression there. so any of these work - .select('nrs', 'random'), .select(pl.col('nrs', 'random')), .select(pl.col('nrs'), pl.col('random')).
Question: I have a polars dataframe with columns a_0, a_1, a_2, b_0, b_1, b_2. I want to convert it to a longer and thinner dataframe (3 x rows, but just 2 columns a and b), so that a contains a_0[0], a_1[0], a_2[0], a_0[1], a_1[1], a_2[1],... and the same for b. How can I do that?

Answer: You can use concat_list() to join the columns you want together and then use explode() to convert them into rows. Let's take simple data frame as an example: df = pl.DataFrame( data=[[x for x in range(6)]], schema=[f"a_{i}" for i in range(3)] + [f"b_{i}" for i in range(3)] ) ┌─────┬─────┬─────┬─────┬─────┬─────┐ │ a_0 ┆ a_1 ┆ a_2 ┆ b_0 ┆ b_1 ┆ b_2 │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │ ╞═════╪═════╪═════╪═════╪═════╪═════╡ │ 0 ┆ 1 ┆ 2 ┆ 3 ┆ 4 ┆ 5 │ └─────┴─────┴─────┴─────┴─────┴─────┘  Now, you can reshape it. First, concat the columns into lists and rename the columns for the final result: import polars.selectors as cs df.select( pl.concat_list(cs.starts_with(x)).alias(x) for x in ['a','b'] ) ┌───────────┬───────────┐ │ a ┆ b │ │ --- ┆ --- │ │ list[i64] ┆ list[i64] │ ╞═══════════╪═══════════╡ │ [0, 1, 2] ┆ [3, 4, 5] │ └───────────┴───────────┘  No, explode lists into rows: df.select( pl.concat_list(cs.starts_with(x)).alias(x) for x in ['a','b'] ).explode(pl.all()) ┌─────┬─────┐ │ a ┆ b │ │ --- ┆ --- │ │ i64 ┆ i64 │ ╞═════╪═════╡ │ 0 ┆ 3 │ │ 1 ┆ 4 │ │ 2 ┆ 5 │ └─────┴─────┘
Question: Let's have a polars df: df = pl.DataFrame( { 'date': ['2022-01-01', '2022-01-02', '2022-01-07', '2022-01-17', '2022-03-02', '2022-06-05', '2022-06-07', '2022-07-02'], 'col1': [4, 4, 2, 2, 2, 3, 2, 1], 'col2': [1, 2, 3, 4, 1, 3, 3, 4], 'col3': [2, 3, 4, 4, 3, 2, 2, 1] } )     date col1 col2 col3     2022-01-01 1 1 2   2022-01-02 1 2 3   2022-01-07 2 3 4   2022-01-17 2 4 1   2022-03-02 3 1 3   2022-06-05 3 2 2   2022-06-07 4 3 4   2022-07-02 4 4 1    The df is sorted by date. I would like to create a column, that would give me a count of all the earlier rows (lower date) with all 3 columns having a value that is greater or equal to the value in the current row. Or in other words: Count rows where row_index < current_row_index & col1[row_index] >= col1[current_row_index] & col2[row_index] >= col2[current_row_index] & col3[row_index] >= col3[current_row_index] )  The result should look like this:    date col1 col2 col3 ge     2022-01-01 4 1 2 0   2022-01-02 4 2 3 0   2022-01-07 2 3 4 0   2022-01-17 2 4 4 0   2022-03-02 2 1 3 3   2022-06-05 3 3 2 0   2022-06-07 2 3 2 3   2022-07-02 1 4 1 1    I have tried various combinations of shift, qe, over, when, cum_count etc. but I have not been able to figure it out. I couldn't find a question similar enough to adopt its answer successfully either. Is there a way to achieve this using Polars?

Answer: You can also use a struct to put all the conditional logic inside .cumulative_eval() cols = "col1", "col2", "col3" df.with_columns(ge = pl.struct(cols).cumulative_eval( pl.all_horizontal( pl.element().struct[col] >= pl.element().struct[col].last() for col in cols ) .sum() - 1 # subtract 1 as we compare each row against itself ) )  shape: (8, 5) ┌────────────┬──────┬──────┬──────┬─────┐ │ date ┆ col1 ┆ col2 ┆ col3 ┆ ge │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ date ┆ i64 ┆ i64 ┆ i64 ┆ u32 │ ╞════════════╪══════╪══════╪══════╪═════╡ │ 2022-01-01 ┆ 4 ┆ 1 ┆ 2 ┆ 0 │ │ 2022-01-02 ┆ 4 ┆ 2 ┆ 3 ┆ 0 │ │ 2022-01-07 ┆ 2 ┆ 3 ┆ 4 ┆ 0 │ │ 2022-01-17 ┆ 2 ┆ 4 ┆ 4 ┆ 0 │ │ 2022-03-02 ┆ 2 ┆ 1 ┆ 3 ┆ 3 │ │ 2022-06-05 ┆ 3 ┆ 3 ┆ 2 ┆ 0 │ │ 2022-06-07 ┆ 2 ┆ 3 ┆ 2 ┆ 3 │ │ 2022-07-02 ┆ 1 ┆ 4 ┆ 1 ┆ 1 │ └────────────┴──────┴──────┴──────┴─────┘
Question: I'm doing some work with a fairly large amount of (horse racing!) data for a project, calculating rolling sums of values for various different combinations of data - thus I need to streamline it as much as possible. Essentially I am:  calculating the rolling calculation of a points field over time calculating this for various grouped combinations of data [in this case the combination of horse and trainer] looking at the average of the value by group for the last 180 days of data through time  The rolling window calculation below works fine - but takes 8.2s [this is about 1/8 of the total dataset - hence would take 1m 5s]. I am looking for ideas of how to streamline this calculation as I'm looking to do it for a number of different combinations of data, and thus speed is of the essence. Thanks. import pandas as pd import time url = 'https://raw.githubusercontent.com/richsdixon/testdata/main/testdata.csv' df = pd.read_csv(url, parse_dates=True) df['RaceDate'] = pd.to_datetime(df['RaceDate'], format='mixed') df.sort_values(by='RaceDate', inplace=True) df['HorseRaceCount90d'] = (df.groupby(['Horse','Trainer'], group_keys=False) .apply(lambda x: x.rolling(window='180D', on='RaceDate', min_periods=1)['Points'].mean())) 

Answer: Here is a little faster way: df.merge(df.set_index('RaceDate') .groupby(['Horse', 'Trainer'])['Points'] .rolling('180D') .mean() .rename('HorseRaceCount90d_1'), right_index=True, left_on=['Horse', 'Trainer', 'RaceDate'])  Your way:  18.5 s ± 727 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)  This way:  1.18 s ± 36.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)  Quick Comparison:  RaceID RaceDate Horse Jockey Trainer Points HorseRaceCount90d HorseRaceCount90d_1 0 678365 2017-01-08 STRADIVARIUS Andrea Atzeni John Gosden 100.00 100.000000 100.000000 289 680610 2017-01-08 CLASSIC PALACE Brian Hughes Dianne Sayer 1.76 1.760000 1.760000 288 680610 2017-01-08 ROMAN NUMERAL Joe Colliver David Thompson 0.00 0.000000 0.000000 287 680610 2017-01-08 COOPER'S FRIEND Conor O'Farrell R Mike Smith 0.00 0.000000 0.000000 286 680610 2017-01-08 GLEANN NA NDOCHAIS Craig Nichol Alistair Whillans 0.00 0.000000 0.000000 ... ... ... ... ... ... ... ... ... 96817 702712 2018-12-06 URBAN ICON Tom Marquand Richard Hannon 12.50 12.500000 12.500000 96816 702712 2018-12-06 EVEN KEEL Rob Hornby Jonathan Portman 11.07 11.070000 11.070000 96815 702712 2018-12-06 MOLLY BLAKE Hector Crouch Clive Cox 9.73 9.730000 9.730000 96885 702719 2018-12-06 CELTIC ARTISAN Cam Hardie Rebecca Menzies 2.37 1.046667 1.046667 97076 704968 2018-12-06 REVENGE David Allan Tim Easterby 2.47 1.346667 1.346667 [100008 rows x 8 columns]